]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libsmartcols: multi-line cells refactoring
authorKarel Zak <kzak@redhat.com>
Thu, 12 Oct 2023 20:38:43 +0000 (22:38 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 23 Oct 2023 19:54:00 +0000 (21:54 +0200)
* move data wrapping code to column.c
* do data wrapping on one place when copy cell data to buffer
* use table cursor in affected functions
* calculate tree ASCII-art to wrapped data
* mark wrap_chunksize() callback as deprecated; library calculates
  the size itself from real data

Signed-off-by: Karel Zak <kzak@redhat.com>
libsmartcols/src/calculate.c
libsmartcols/src/column.c
libsmartcols/src/print.c
libsmartcols/src/smartcolsP.h

index cb7e0203868837f88d4b42e4f47a5a3770ea2e7b..ebb61344db24812fe31713ca20d1600a82e27648 100644 (file)
@@ -42,41 +42,38 @@ static int count_cell_width(struct libscols_table *tb,
                struct libscols_column *cl,
                struct ul_buffer *buf)
 {
-       size_t len;
-       char *data;
-       int rc;
+       size_t len = 0;
+       int rc = 0;
        struct libscols_cell *ce;
-       struct libscols_wstat *st;
+       char *data;
 
-       rc = __cell_to_buffer(tb, ln, cl, buf);
-       if (rc)
-               return rc;
+       ce = scols_line_get_cell(ln, cl->seqnum);
+       scols_table_set_cursor(tb, ln, cl, ce);
 
+       rc = __cursor_to_buffer(tb, buf, 1);
+       if (rc)
+               goto done;
        data = ul_buffer_get_data(buf, NULL, NULL);
        if (!data)
-               len = 0;
-       else if (scols_column_is_customwrap(cl))
-               len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data);
-       else if (scols_table_is_noencoding(tb))
-               len = mbs_width(data);
-       else
-               len = mbs_safe_width(data);
+               goto done;
+
+       len = scols_table_is_noencoding(tb) ?
+               mbs_width(data) :
+               mbs_safe_width(data);
 
        if (len == (size_t) -1)         /* ignore broken multibyte strings */
                len = 0;
 
-       ce = scols_line_get_cell(ln, cl->seqnum);
-       ce->width = len;
-
-       st = &cl->wstat;
-       st->width_max = max(len, st->width_max);
-
        if (scols_column_is_tree(cl)) {
                size_t treewidth = ul_buffer_get_safe_pointer_width(buf, SCOLS_BUFPTR_TREEEND);
                cl->width_treeart = max(cl->width_treeart, treewidth);
        }
 
-       return 0;
+       ce->width = len;
+       cl->wstat.width_max = max(len, cl->wstat.width_max);
+done:
+       scols_table_reset_cursor(tb);
+       return rc;
 }
 
 static int walk_count_cell_width(struct libscols_table *tb,
index db4c3572df1e430cb743f212e815cd24de3cd05d..f9c6a61f2cca8ed9021fd38204b06088ec39e6e0 100644 (file)
@@ -73,7 +73,7 @@ void scols_unref_column(struct libscols_column *cl)
                scols_reset_cell(&cl->header);
                free(cl->color);
                free(cl->safechars);
-               free(cl->pending_data_buf);
+               free(cl->wrap_data);
                free(cl->shellvar);
                free(cl);
        }
@@ -402,6 +402,8 @@ char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unu
  * Note that the size has to be based on number of terminal cells rather than
  * bytes to support multu-byte output.
  *
+ * Deprecated since 2.40.
+ *
  * Returns: size of the largest chunk.
  *
  * Since: 2.29
@@ -459,7 +461,7 @@ int scols_column_set_cmpfunc(struct libscols_column *cl,
 /**
  * scols_column_set_wrapfunc:
  * @cl: a pointer to a struct libscols_column instance
- * @wrap_chunksize: function to return size of the largest chink of data
+ * @wrap_chunksize: function to return size of the largest chink of data (deprecated)
  * @wrap_nextchunk: function to return next zero terminated data
  * @userdata: optional stuff for callbacks
  *
@@ -467,6 +469,9 @@ int scols_column_set_cmpfunc(struct libscols_column *cl,
  * is to wrap by column size, but you can create functions to wrap for example
  * after \n or after words, etc.
  *
+ * Note that since 2.40 the @wrap_chunksize is unnecessary. The library calculates
+ * the size itself.
+ *
  * Returns: 0, a negative value in case of an error.
  *
  * Since: 2.29
@@ -474,7 +479,7 @@ int scols_column_set_cmpfunc(struct libscols_column *cl,
 int scols_column_set_wrapfunc(struct libscols_column *cl,
                        size_t (*wrap_chunksize)(const struct libscols_column *,
                                                 const char *,
-                                                void *),
+                                                void *) __attribute__((__unused__)),
                        char * (*wrap_nextchunk)(const struct libscols_column *,
                                                 char *,
                                                 void *),
@@ -484,7 +489,6 @@ int scols_column_set_wrapfunc(struct libscols_column *cl,
                return -EINVAL;
 
        cl->wrap_nextchunk = wrap_nextchunk;
-       cl->wrap_chunksize = wrap_chunksize;
        cl->wrapfunc_data = userdata;
        return 0;
 }
@@ -639,7 +643,6 @@ int scols_column_is_wrap(const struct libscols_column *cl)
 int scols_column_is_customwrap(const struct libscols_column *cl)
 {
        return (cl->flags & SCOLS_FL_WRAP)
-               && cl->wrap_chunksize
                && cl->wrap_nextchunk ? 1 : 0;
 }
 
@@ -735,3 +738,122 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts)
        return rc;
 }
 
+static void scols_column_reset_wrap(struct libscols_column *cl)
+{
+       if (!cl)
+               return;
+
+       if (cl->wrap_data)
+               memset(cl->wrap_data, 0, cl->wrap_datamax);
+       cl->wrap_cell = NULL;
+       cl->wrap_datasz = 0;
+       cl->wrap_cur = NULL;
+       cl->wrap_next = NULL;
+}
+
+static int scols_column_init_wrap(
+                       struct libscols_column *cl,
+                       struct libscols_cell *ce)
+{
+       const char *data = scols_cell_get_data(ce);
+
+       if (!cl || !ce)
+               return -EINVAL;
+
+       assert(cl->table->cur_column == cl);
+       assert(cl->table->cur_cell == ce);
+
+       scols_column_reset_wrap(cl);
+
+       cl->wrap_cell = ce;
+       if (data) {
+               void *tmp;
+               cl->wrap_datasz = strlen(data) + 1;     /* TODO: use scols_cell_get_datasiz() */
+
+               if (cl->wrap_datasz > cl->wrap_datamax) {
+                       cl->wrap_datamax = cl->wrap_datasz;
+                       tmp = realloc(cl->wrap_data, cl->wrap_datamax);
+                       if (!tmp)
+                               return -ENOMEM;
+                       cl->wrap_data = tmp;
+               }
+               memcpy(cl->wrap_data, data, cl->wrap_datasz);
+               cl->wrap_cur = cl->wrap_data;
+               cl->wrap_next = NULL;
+       }
+
+       return 0;
+}
+
+/* Returns the next chunk of cell data in multi-line cells */
+int scols_column_next_wrap(
+                       struct libscols_column *cl,
+                       struct libscols_cell *ce,
+                       char **data)
+{
+       if (!cl || !data || (!cl->wrap_cell && !ce))
+               return -EINVAL;
+
+       *data = NULL;
+
+       if (ce && cl->wrap_cell != ce)
+               scols_column_init_wrap(cl, ce);         /* init */
+       else {
+               cl->wrap_cur = cl->wrap_next;   /* next step */
+               cl->wrap_next = NULL;
+       }
+
+       if (!cl->wrap_cur)
+               return 1;                               /* no more data */
+       if (scols_column_is_customwrap(cl))
+               cl->wrap_next = cl->wrap_nextchunk(cl, cl->wrap_cur, cl->wrapfunc_data);
+
+       *data = cl->wrap_cur;
+       return 0;
+}
+
+int scols_column_greatest_wrap(
+                       struct libscols_column *cl,
+                       struct libscols_cell *ce,
+                       char **data)
+{
+       size_t maxsz = 0;
+       char *res = NULL;;
+
+       if (!scols_column_is_customwrap(cl))
+               return scols_column_next_wrap(cl, ce, data);
+
+       while (scols_column_next_wrap(cl, ce, data) == 0) {
+               size_t sz = strlen(*data);
+
+               maxsz = max(maxsz, sz);
+               if (maxsz == sz)
+                       res = *data;
+       }
+
+       *data = res;
+       return 0;
+}
+
+/* Set the "next" chunk in multi-line cell to offset specified by @bytes.
+ * Don't use it for columns with custom wrapfunc().
+ */
+int scols_column_move_wrap(struct libscols_column *cl, size_t bytes)
+{
+       size_t x;       /* remaining bytes */
+
+       if (!cl->wrap_cur)
+               return -EINVAL;         /* scols_column_init_wrap() not called */
+
+       x = cl->wrap_datasz - (cl->wrap_cur - cl->wrap_data);
+       if (bytes >= x)
+               cl->wrap_next = NULL;   /* done */
+       else
+               cl->wrap_next = cl->wrap_cur + bytes;
+       return 0;
+}
+
+int scols_column_has_pending_wrap(struct libscols_column *cl)
+{
+       return cl && cl->wrap_next;
+}
index 6a7e6da04518adc00a8397273919d51f44b1a2e4..c0b5c5258a735105e10f70cf4a2b800842291fdc 100644 (file)
@@ -241,7 +241,7 @@ static int has_pending_data(struct libscols_table *tb)
        while (scols_table_next_column(tb, &itr, &cl) == 0) {
                if (scols_column_is_hidden(cl))
                        continue;
-               if (cl->pending_data)
+               if (scols_column_has_pending_wrap(cl))
                        return 1;
        }
        return 0;
@@ -423,95 +423,43 @@ static void print_newline_padding(struct libscols_table *tb,
        fputs_color_line_close(tb);
 }
 
-/*
- * Pending data
- *
- * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
- * printed as usually and output is truncated to match column width.
- *
- * The rest of the long text is printed on next extra line(s). The extra lines
- * don't exist in the table (not represented by libscols_line). The data for
- * the extra lines are stored in libscols_column->pending_data_buf and the
- * function print_line() adds extra lines until the buffer is not empty in all
- * columns.
- */
-
-/* set data that will be printed by extra lines */
-static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz)
-{
-       char *p = NULL;
-
-       if (data && *data) {
-               DBG(COL, ul_debugobj(cl, "setting pending data"));
-               assert(sz);
-               p = strdup(data);
-               if (!p)
-                       return -ENOMEM;
-       }
-
-       free(cl->pending_data_buf);
-       cl->pending_data_buf = p;
-       cl->pending_data_sz = sz;
-       cl->pending_data = cl->pending_data_buf;
-       return 0;
-}
-
-/* the next extra line has been printed, move pending data cursor */
-static int step_pending_data(struct libscols_column *cl, size_t bytes)
-{
-       DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));
-
-       if (bytes >= cl->pending_data_sz)
-               return set_pending_data(cl, NULL, 0);
-
-       cl->pending_data += bytes;
-       cl->pending_data_sz -= bytes;
-       return 0;
-}
-
-/* print next pending data for the column @cl */
-static int print_pending_data(
-               struct libscols_table *tb,
-               struct libscols_column *cl,
-               struct libscols_line *ln,       /* optional */
-               struct libscols_cell *ce)
+static int print_pending_data(struct libscols_table *tb, struct ul_buffer *buf)
 {
-       size_t width = cl->width, bytes;
-       size_t len = width, i;
+       struct libscols_line *ln;
+       struct libscols_column *cl;
+       struct libscols_cell *ce;
        char *data;
-       char *nextchunk = NULL;
+       size_t i, width = 0, len = 0, bytes = 0;
 
-       if (!cl->pending_data)
-               return 0;
+       scols_table_get_cursor(tb, &ln, &cl, &ce);
+
+       width = cl->width;
        if (!width)
                return -EINVAL;
 
        DBG(COL, ul_debugobj(cl, "printing pending data"));
 
-       data = strdup(cl->pending_data);
+       if (scols_table_is_noencoding(tb))
+               data = ul_buffer_get_data(buf, &bytes, &len);
+       else
+               data = ul_buffer_get_safe_data(buf, &bytes, &len, scols_column_get_safechars(cl));
+
        if (!data)
-               goto err;
+               return 0;
 
-       if (scols_column_is_customwrap(cl)
-           && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
-               bytes = nextchunk - data;
+       /* standard multi-line cell */
+       if (len > width && scols_column_is_wrap(cl)
+           && !scols_column_is_customwrap(cl)) {
 
-               len = scols_table_is_noencoding(tb) ?
-                               mbs_nwidth(data, bytes) :
-                               mbs_safe_nwidth(data, bytes, NULL);
-       } else
+               len = width;
                bytes = mbs_truncate(data, &len);
 
-       if (bytes == (size_t) -1)
-               goto err;
-
-       if (bytes)
-               step_pending_data(cl, bytes);
+               if (bytes != (size_t) -1 && bytes > 0)
+                       scols_column_move_wrap(cl, mbs_safe_decode_size(data));
+       }
 
        fputs_color_cell_open(tb, cl, ln, ce);
-
        fputs(data, tb->out);
-       free(data);
 
        /* minout -- don't fill */
        if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) {
@@ -535,9 +483,6 @@ static int print_pending_data(
                fputs(colsep(tb), tb->out);
 
        return 0;
-err:
-       free(data);
-       return -errno;
 }
 
 static void print_json_data(struct libscols_table *tb,
@@ -574,32 +519,30 @@ static void print_json_data(struct libscols_table *tb,
                if (!scols_column_is_customwrap(cl))
                        ul_jsonwrt_value_s(&tb->json, NULL, data);
                else do {
-                               char *next = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data);
-
-                               if (cl->json_type == SCOLS_JSON_ARRAY_STRING)
-                                       ul_jsonwrt_value_s(&tb->json, NULL, data);
-                               else
-                                       ul_jsonwrt_value_raw(&tb->json, NULL, data);
-                               data = next;
-               } while (data);
+                       if (cl->json_type == SCOLS_JSON_ARRAY_STRING)
+                               ul_jsonwrt_value_s(&tb->json, NULL, data);
+                       else
+                               ul_jsonwrt_value_raw(&tb->json, NULL, data);
+               } while (scols_column_next_wrap(cl, NULL, &data) == 0);
 
                ul_jsonwrt_array_close(&tb->json);
                break;
        }
 }
 
-static int print_data(struct libscols_table *tb,
-                     struct libscols_column *cl,
-                     struct libscols_line *ln, /* optional */
-                     struct libscols_cell *ce, /* optional */
-                     struct ul_buffer *buf)
+static int print_data(struct libscols_table *tb, struct ul_buffer *buf)
 {
+       struct libscols_line *ln;       /* NULL for header line! */
+       struct libscols_column *cl;
+       struct libscols_cell *ce;
        size_t len = 0, i, width, bytes;
-       char *data, *nextchunk;
+       char *data;
        const char *name = NULL;
        int is_last;
 
        assert(tb);
+
+       scols_table_get_cursor(tb, &ln, &cl, &ce);
        assert(cl);
 
        data = ul_buffer_get_data(buf, NULL, NULL);
@@ -614,7 +557,7 @@ static int print_data(struct libscols_table *tb,
 
        is_last = is_last_column(cl);
 
-       if (is_last && scols_table_is_json(tb) &&
+       if (ln && is_last && scols_table_is_json(tb) &&
            scols_table_is_tree(tb) && has_children(ln))
                /* "children": [] is the real last value */
                is_last = 0;
@@ -642,7 +585,7 @@ static int print_data(struct libscols_table *tb,
                break;          /* continue below */
        }
 
-       /* Encode. Note that 'len' and 'width' are number of cells, not bytes.
+       /* Encode. Note that 'len' and 'width' are number of glyphs not bytes.
         */
        if (scols_table_is_noencoding(tb))
                data = ul_buffer_get_data(buf, &bytes, &len);
@@ -653,17 +596,6 @@ static int print_data(struct libscols_table *tb,
                data = "";
        width = cl->width;
 
-       /* custom multi-line cell based */
-       if (*data && scols_column_is_customwrap(cl)
-           && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
-               set_pending_data(cl, nextchunk, bytes - (nextchunk - data));
-               bytes = nextchunk - data;
-
-               len = scols_table_is_noencoding(tb) ?
-                               mbs_nwidth(data, bytes) :
-                               mbs_safe_nwidth(data, bytes, NULL);
-       }
-
        if (is_last
            && len < width
            && !scols_table_is_maxout(tb)
@@ -679,12 +611,12 @@ static int print_data(struct libscols_table *tb,
        /* standard multi-line cell */
        if (len > width && scols_column_is_wrap(cl)
            && !scols_column_is_customwrap(cl)) {
-               set_pending_data(cl, data, bytes);
 
                len = width;
                bytes = mbs_truncate(data, &len);
-               if (bytes  != (size_t) -1 && bytes > 0)
-                       step_pending_data(cl, bytes);
+
+               if (bytes != (size_t) -1 && bytes > 0)
+                       scols_column_move_wrap(cl, mbs_safe_decode_size(data));
        }
 
        if (bytes == (size_t) -1) {
@@ -701,7 +633,6 @@ static int print_data(struct libscols_table *tb,
                        len = width;
                }
                fputs(data, tb->out);
-
        }
 
        /* minout -- don't fill */
@@ -732,16 +663,23 @@ static int print_data(struct libscols_table *tb,
        return 0;
 }
 
-int __cell_to_buffer(struct libscols_table *tb,
-                         struct libscols_line *ln,
-                         struct libscols_column *cl,
-                         struct ul_buffer *buf)
+/*
+ * Copy curret cell data to buffer. The @cal means "calculation" phase.
+ */
+int __cursor_to_buffer(struct libscols_table *tb,
+                    struct ul_buffer *buf,
+                    int cal)
 {
-       const char *data;
+       const char *data = NULL;
        struct libscols_cell *ce;
+       struct libscols_line *ln;
+       struct libscols_column *cl;
        int rc = 0;
 
        assert(tb);
+
+       scols_table_get_cursor(tb, &ln, &cl, &ce);
+
        assert(ln);
        assert(cl);
        assert(buf);
@@ -749,11 +687,22 @@ int __cell_to_buffer(struct libscols_table *tb,
 
        ul_buffer_reset_data(buf);
 
-       ce = scols_line_get_cell(ln, cl->seqnum);
-       data = ce ? scols_cell_get_data(ce) : NULL;
+       if (ce) {
+               if (scols_column_is_wrap(cl)) {
+                       char *x = NULL;
+
+                       rc = cal ? scols_column_greatest_wrap(cl, ce, &x) :
+                                  scols_column_next_wrap(cl, ce, &x);
+                       if (rc < 0)
+                               return rc;
+                       data = x;
+                       rc = 0;
+               } else
+                       data = scols_cell_get_data(ce);
+       }
 
        if (!scols_column_is_tree(cl))
-               return data ? ul_buffer_append_string(buf, data) : 0;
+               goto notree;
 
        /*
         * Group stuff
@@ -775,7 +724,7 @@ int __cell_to_buffer(struct libscols_table *tb,
 
        if (!rc && (ln->parent || cl->is_groups) && !scols_table_is_json(tb))
                ul_buffer_save_pointer(buf, SCOLS_BUFPTR_TREEEND);
-
+notree:
        if (!rc && data)
                rc = ul_buffer_append_string(buf, data);
        return rc;
@@ -801,16 +750,18 @@ static int print_line(struct libscols_table *tb,
 
        /* regular line */
        scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
        while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
                if (scols_column_is_hidden(cl))
                        continue;
-               rc = __cell_to_buffer(tb, ln, cl, buf);
-               if (rc == 0)
-                       rc = print_data(tb, cl, ln,
-                                       scols_line_get_cell(ln, cl->seqnum),
-                                       buf);
-               if (rc == 0 && cl->pending_data)
+
+               scols_table_set_cursor(tb, ln, cl, scols_line_get_cell(ln, cl->seqnum));
+               rc = __cursor_to_buffer(tb, buf, 0);
+               if (!rc)
+                       rc = print_data(tb, buf);
+               if (!rc && scols_column_has_pending_wrap(cl))
                        pending = 1;
+               scols_table_reset_cursor(tb);
        }
        fputs_color_line_close(tb);
 
@@ -821,16 +772,23 @@ static int print_line(struct libscols_table *tb,
                fputs(linesep(tb), tb->out);
                fputs_color_line_open(tb, ln);
                tb->termlines_used++;
+
                scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
                while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
                        if (scols_column_is_hidden(cl))
                                continue;
-                       if (cl->pending_data) {
-                               rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum));
-                               if (rc == 0 && cl->pending_data)
+
+                       scols_table_set_cursor(tb, ln, cl, scols_line_get_cell(ln, cl->seqnum));
+                       if (scols_column_has_pending_wrap(cl)) {
+                               rc = __cursor_to_buffer(tb, buf, 0);
+                               if (!rc)
+                                       rc = print_pending_data(tb, buf);
+                               if (!rc && scols_column_has_pending_wrap(cl))
                                        pending = 1;
                        } else
                                print_empty_cell(tb, cl, ln, NULL, ul_buffer_get_bufsiz(buf));
+                       scols_table_reset_cursor(tb);
                }
                fputs_color_line_close(tb);
        }
@@ -963,6 +921,7 @@ int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf)
                        continue;
 
                ul_buffer_reset_data(buf);
+               scols_table_set_cursor(tb, NULL, cl, &cl->header);
 
                if (cl->is_groups
                    && scols_table_is_tree(tb) && scols_column_is_tree(cl)) {
@@ -979,7 +938,8 @@ int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf)
                                                scols_column_get_name_as_shellvar(cl) :
                                                scols_column_get_name(cl));
                if (!rc)
-                       rc = print_data(tb, cl, NULL, &cl->header, buf);
+                       rc = print_data(tb, buf);
+               scols_table_reset_cursor(tb);
        }
 
        if (rc == 0) {
index 89a7f6ec4a64e6fb6f97bc70532be28991d7549d..3abe8803a0c9b34f665c0a2352e64df3b274cdd7 100644 (file)
@@ -117,21 +117,21 @@ struct libscols_column {
        char    *color;         /* default column color */
        char    *safechars;     /* do not encode this bytes */
 
-       char    *pending_data;
-       size_t  pending_data_sz;
-       char    *pending_data_buf;
-
        int (*cmpfunc)(struct libscols_cell *,
                       struct libscols_cell *,
                       void *);                 /* cells comparison function */
        void *cmpfunc_data;
 
-       size_t (*wrap_chunksize)(const struct libscols_column *,
-                       const char *, void *);
-       char *(*wrap_nextchunk)(const struct libscols_column *,
-                       char *, void *);
+       /* multi-line cell data wrapping */
+       char *(*wrap_nextchunk)(const struct libscols_column *, char *, void *);
        void *wrapfunc_data;
 
+       size_t  wrap_datasz;
+       size_t  wrap_datamax;
+       char    *wrap_data;
+       char    *wrap_cur;
+       char    *wrap_next;
+       struct libscols_cell    *wrap_cell;
 
        struct libscols_cell    header;         /* column name with color etc. */
        char    *shellvar;                      /* raw colum name in shell compatible format */
@@ -303,6 +303,17 @@ int scols_line_next_group_child(struct libscols_line *ln,
                           struct libscols_iter *itr,
                           struct libscols_line **chld);
 
+/*
+ * column.c
+ */
+int scols_column_next_wrap(     struct libscols_column *cl,
+                                struct libscols_cell *ce,
+                                char **data);
+int scols_column_greatest_wrap( struct libscols_column *cl,
+                                struct libscols_cell *ce,
+                                char **data);
+int scols_column_has_pending_wrap(struct libscols_column *cl);
+int scols_column_move_wrap(struct libscols_column *cl, size_t bytes);
 
 /*
  * table.c
@@ -315,6 +326,8 @@ int scols_table_set_cursor(struct libscols_table *tb,
                            struct libscols_column *cl,
                            struct libscols_cell *ce);
 
+#define scols_table_reset_cursor(_t)   scols_table_set_cursor((_t), NULL, NULL, NULL)
+
 
 /*
  * grouping.c
@@ -348,10 +361,9 @@ extern int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf);
 /*
  * print.c
  */
-extern int __cell_to_buffer(struct libscols_table *tb,
-                          struct libscols_line *ln,
-                          struct libscols_column *cl,
-                          struct ul_buffer *buf);
+int __cursor_to_buffer(struct libscols_table *tb,
+                    struct ul_buffer *buf,
+                   int cal);
 
 void __scols_cleanup_printing(struct libscols_table *tb, struct ul_buffer *buf);
 int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf);