From 949ea05f1af647834db24b9172ac006488d2e4f4 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 26 Sep 2016 11:20:07 +0200 Subject: libsmartcols: support custom wrap and remove SCOLS_FL_WRAPNL This new API provides full control on multi-line cells, you can wrap text by new lines (build-in support) or by another way (after words, commas, etc.) Changes: * new scols_column_set_wrapfunc() sets pointers to two callback functions 1/ chunksize() - returns largest data chunk size; used when we calculate columns width 2/ nextchunk() - terminate the current chunk and returns pointer to the next; used when we print data * remove SCOLS_FL_WRAPNL and add new functions scols_wrapnl_chunksize() and scols_wrapnl_nextchunk() to provide build-in functionality to wrap cells on \n * remove scols_column_is_wrapnl() add scols_column_is_customwrap() (returns true if custom wrap functions are defined) * add scols_column_set_safechars() and scols_column_get_safechars() to allow to control output encoding, safe chars are not encoded by \xFOO * modify "fromfile" test code to use build-in scols_wrapnl_* callbacks for "wrapnl" tests * add new function scols_column_get_table() Signed-off-by: Karel Zak --- libsmartcols/docs/libsmartcols-sections.txt | 8 +- libsmartcols/samples/fromfile.c | 9 +- libsmartcols/src/column.c | 146 +++++++++++++++++++++++++++- libsmartcols/src/libsmartcols.h.in | 19 +++- libsmartcols/src/libsmartcols.sym | 14 ++- libsmartcols/src/smartcolsP.h | 8 ++ libsmartcols/src/table_print.c | 62 ++++-------- 7 files changed, 208 insertions(+), 58 deletions(-) (limited to 'libsmartcols') diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt index 12366bd34..f05d45479 100644 --- a/libsmartcols/docs/libsmartcols-sections.txt +++ b/libsmartcols/docs/libsmartcols-sections.txt @@ -21,7 +21,10 @@ libscols_column scols_column_get_color scols_column_get_flags scols_column_get_header +scols_column_get_safechars; +scols_column_get_table; scols_column_get_whint +scols_column_is_customwrap; scols_column_is_hidden scols_column_is_noextremes scols_column_is_right @@ -29,15 +32,18 @@ scols_column_is_strict_width scols_column_is_tree scols_column_is_trunc scols_column_is_wrap -scols_column_is_wrapnl scols_column_set_cmpfunc scols_column_set_color scols_column_set_flags +scols_column_set_safechars; scols_column_set_whint +scols_column_set_wrapfunc; scols_copy_column scols_new_column scols_ref_column scols_unref_column +scols_wrapnl_chunksize; +scols_wrapnl_nextchunk;
diff --git a/libsmartcols/samples/fromfile.c b/libsmartcols/samples/fromfile.c index 6d4ee4aaf..fcf01c749 100644 --- a/libsmartcols/samples/fromfile.c +++ b/libsmartcols/samples/fromfile.c @@ -34,7 +34,7 @@ static const struct column_flag flags[] = { { "noextremes", SCOLS_FL_NOEXTREMES }, { "hidden", SCOLS_FL_HIDDEN }, { "wrap", SCOLS_FL_WRAP }, - { "wrapnl", SCOLS_FL_WRAPNL }, + { "wrapnl", SCOLS_FL_WRAP }, { "none", 0 } }; @@ -101,6 +101,13 @@ static struct libscols_column *parse_column(FILE *f) int flags = parse_column_flags(line); if (scols_column_set_flags(cl, flags)) goto fail; + if (strcmp(line, "wrapnl") == 0) { + scols_column_set_wrapfunc(cl, + scols_wrapnl_chunksize, + scols_wrapnl_nextchunk, + NULL); + scols_column_set_safechars(cl, "\n"); + } break; } case 3: /* COLOR */ diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c index 6f4ccc3bd..1e84a6363 100644 --- a/libsmartcols/src/column.c +++ b/libsmartcols/src/column.c @@ -22,6 +22,8 @@ #include #include +#include "mbsalign.h" + #include "smartcolsP.h" /** @@ -168,6 +170,17 @@ int scols_column_set_flags(struct libscols_column *cl, int flags) return 0; } +/** + * scols_column_get_table: + * @cl: a pointer to a struct libscols_column instance + * + * Returns: pointer to the table where columns is used + */ +struct libscols_table *scols_column_get_table(struct libscols_column *cl) +{ + return cl->table; +} + /** * scols_column_get_flags: * @cl: a pointer to a struct libscols_column instance @@ -227,6 +240,69 @@ const char *scols_column_get_color(const struct libscols_column *cl) return cl->color; } +/** + * scols_wrapnl_nextchunk: + * @cl: a pointer to a struct libscols_column instance + * @data: string + * @userdata: any data or NULL + * + * This is build-in function for scols_column_set_wrapfunc(). This function + * terminates the current chunk by \0 and returns pointer to the begin of + * the next chunk. The chunks are based on \n. + * + * For example for data "AAA\nBBB\nCCC" the next chunk is "BBB". + * + * Returns: next chunk + */ +char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unused)), + char *data, + void *userdata __attribute__((unused))) +{ + char *p = data ? strchr(data, '\n') : NULL; + + if (p) { + *p = '\0'; + return p + 1; + } + return NULL; +} + +/** + * scols_wrapnl_chunksize: + * @cl: a pointer to a struct libscols_column instance + * @data: string + * + * Analyzes @data and returns size of the largest chunk. The chunks are based + * on \n. For example for data "AAA\nBBB\nCCCC" the largest chunk size is 4. + * + * Note that the size has to be based on number of terminal cells rather than + * bytes to support multu-byte output. + * + * Returns: size of the largest chunk. + */ +size_t scols_wrapnl_chunksize(const struct libscols_column *cl __attribute__((unused)), + const char *data, + void *userdata __attribute__((unused))) +{ + size_t sum = 0; + + while (data && *data) { + const char *p = data; + size_t sz; + + p = strchr(data, '\n'); + if (p) { + sz = mbs_safe_nwidth(data, p - data, NULL); + p++; + } else + sz = mbs_safe_width(data); + + sum = max(sum, sz); + data = p;; + } + + return sum; +} /** * scols_column_set_cmpfunc: @@ -250,6 +326,66 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, return 0; } +/** + * 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_nextchunk: function to return next zero terminated data + * + * Extends SCOLS_FL_WRAP and allows to set custom wrap function. The default + * is to wrap by column size, but you can create functions to wrap for example + * after \n or after words, etc. + * + * Returns: 0, a negative value in case of an error. + */ +int scols_column_set_wrapfunc(struct libscols_column *cl, + size_t (*wrap_chunksize)(const struct libscols_column *, + const char *, + void *), + char * (*wrap_nextchunk)(const struct libscols_column *, + char *, + void *), + void *data) +{ + if (!cl) + return -EINVAL; + + cl->wrap_nextchunk = wrap_nextchunk; + cl->wrap_chunksize = wrap_chunksize; + cl->wrapfunc_data = data; + return 0; +} + +/** + * scols_column_set_safechars: + * @cl: a pointer to a struct libscols_column instance + * @safe: safe characters (e.g. "\n\t") + * + * Use for bytes you don't want to encode on output. This is for example + * necessary if you want to use custom wrap function based on \n, in this case + * you have to set "\n" as a safe char. + * + * Returns: 0, a negative value in case of an error. + */ +int scols_column_set_safechars(struct libscols_column *cl, const char *safe) +{ + if (!cl) + return -EINVAL; + cl->safechars = safe; + return 0; +} + +/** + * scols_column_get_safechars: + * @cl: a pointer to a struct libscols_column instance + * + * Returns: safe chars + */ +const char *scols_column_get_safechars(const struct libscols_column *cl) +{ + return cl->safechars; +} + /** * scols_column_is_hidden: * @cl: a pointer to a struct libscols_column instance @@ -340,16 +476,16 @@ int scols_column_is_wrap(const struct libscols_column *cl) return cl->flags & SCOLS_FL_WRAP ? 1 : 0; } /** - * scols_column_is_wrapnl: + * scols_column_is_customwrap: * @cl: a pointer to a struct libscols_column instance * - * Gets the value of @cl's flag wrap. - * * Returns: 0 or 1 * * Since: 2.29 */ -int scols_column_is_wrapnl(const struct libscols_column *cl) +int scols_column_is_customwrap(const struct libscols_column *cl) { - return cl->flags & SCOLS_FL_WRAPNL ? 1 : 0; + return (cl->flags & SCOLS_FL_WRAP) + && cl->wrap_chunksize + && cl->wrap_nextchunk ? 1 : 0; } diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in index 22cab64a1..d6ae91f60 100644 --- a/libsmartcols/src/libsmartcols.h.in +++ b/libsmartcols/src/libsmartcols.h.in @@ -84,8 +84,7 @@ enum { SCOLS_FL_STRICTWIDTH = (1 << 3), /* don't reduce width if column is empty */ SCOLS_FL_NOEXTREMES = (1 << 4), /* ignore extreme fields when count column width*/ SCOLS_FL_HIDDEN = (1 << 5), /* maintain data, but don't print */ - SCOLS_FL_WRAP = (1 << 6), /* wrap long lines to multi-line cells */ - SCOLS_FL_WRAPNL = (1 << 7) /* wrap based on '\n' char */ + SCOLS_FL_WRAP = (1 << 6) /* wrap long lines to multi-line cells */ }; /* @@ -146,7 +145,10 @@ extern int scols_column_is_strict_width(const struct libscols_column *cl); extern int scols_column_is_hidden(const struct libscols_column *cl); extern int scols_column_is_noextremes(const struct libscols_column *cl); extern int scols_column_is_wrap(const struct libscols_column *cl); -extern int scols_column_is_wrapnl(const struct libscols_column *cl); +extern int scols_column_is_customwrap(const struct libscols_column *cl); + +extern int scols_column_set_safechars(struct libscols_column *cl, const char *safe); +extern const char *scols_column_get_safechars(const struct libscols_column *cl); extern int scols_column_set_flags(struct libscols_column *cl, int flags); extern int scols_column_get_flags(const struct libscols_column *cl); @@ -159,12 +161,23 @@ extern double scols_column_get_whint(const struct libscols_column *cl); extern struct libscols_cell *scols_column_get_header(struct libscols_column *cl); extern int scols_column_set_color(struct libscols_column *cl, const char *color); extern const char *scols_column_get_color(const struct libscols_column *cl); +extern struct libscols_table *scols_column_get_table(struct libscols_column *cl); extern int scols_column_set_cmpfunc(struct libscols_column *cl, int (*cmp)(struct libscols_cell *a, struct libscols_cell *b, void *), void *data); +extern int scols_column_set_wrapfunc(struct libscols_column *cl, + size_t (*wrap_chunksize)(const struct libscols_column *, + const char *, void *), + char * (*wrap_nextchunk)(const struct libscols_column *, + char *, void *), + void *data); + +extern char *scols_wrapnl_nextchunk(const struct libscols_column *cl, char *data, void *userdata); +extern size_t scols_wrapnl_chunksize(const struct libscols_column *cl, const char *data, void *userdata); + /* line.c */ extern struct libscols_line *scols_new_line(void); extern void scols_ref_line(struct libscols_line *ln); diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym index aca648b63..d5c3674b8 100644 --- a/libsmartcols/src/libsmartcols.sym +++ b/libsmartcols/src/libsmartcols.sym @@ -139,15 +139,21 @@ global: SMARTCOLS_2.29 { global: - scols_column_is_wrapnl; + scols_column_get_safechars; + scols_column_get_table; + scols_column_is_customwrap; + scols_column_set_safechars; + scols_column_set_wrapfunc; scols_symbols_set_cell_padding; + scols_table_get_name; scols_table_get_symbols; scols_table_get_termforce; scols_table_get_termwidth; + scols_table_is_nolinesep; + scols_table_is_nowrap; scols_table_set_default_symbols; scols_table_set_termforce; scols_table_set_termwidth; - scols_table_get_name; - scols_table_is_nowrap; - scols_table_is_nolinesep; + scols_wrapnl_chunksize; + scols_wrapnl_nextchunk; } SMARTCOLS_2.28; diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 8e8bd768e..2f29c9f63 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -95,6 +95,14 @@ struct libscols_column { 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 *); + void *wrapfunc_data; + + const char *safechars; /* do not encode this bytes */ + struct libscols_cell header; struct list_head cl_columns; diff --git a/libsmartcols/src/table_print.c b/libsmartcols/src/table_print.c index 5771111b3..c73154ec6 100644 --- a/libsmartcols/src/table_print.c +++ b/libsmartcols/src/table_print.c @@ -370,7 +370,7 @@ static int print_pending_data( size_t width = cl->width, bytes; size_t len = width, i; char *data; - char *wrapnl = NULL; + char *nextchunk = NULL; if (!cl->pending_data) return 0; @@ -381,10 +381,9 @@ static int print_pending_data( if (!data) goto err; - if (scols_column_is_wrapnl(cl) && (wrapnl = strchr(data, '\n'))) { - *wrapnl = '\0'; - wrapnl++; - bytes = wrapnl - data; + if (scols_column_is_customwrap(cl) + && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) { + bytes = nextchunk - data; len = mbs_safe_nwidth(data, bytes, NULL); } else @@ -423,7 +422,7 @@ static int print_data(struct libscols_table *tb, { size_t len = 0, i, width, bytes; const char *color = NULL; - char *data, *wrapnl; + char *data, *nextchunk; int is_last; assert(tb); @@ -471,21 +470,18 @@ static int print_data(struct libscols_table *tb, color = get_cell_color(tb, cl, ln, ce); /* Encode. Note that 'len' and 'width' are number of cells, not bytes. - * For the columns with WRAPNL we mark \n as a safe char. */ - data = buffer_get_safe_data(buf, &len, - scols_column_is_wrapnl(cl) ? "\n" : NULL); + data = buffer_get_safe_data(buf, &len, scols_column_get_safechars(cl)); if (!data) data = ""; bytes = strlen(data); width = cl->width; - /* multi-line cell based on '\n' */ - if (*data && scols_column_is_wrapnl(cl) && (wrapnl = strchr(data, '\n'))) { - *wrapnl = '\0'; - wrapnl++; - set_pending_data(cl, wrapnl, bytes - (wrapnl - data)); - bytes = wrapnl - data; + /* 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 = mbs_safe_nwidth(data, bytes, NULL); } @@ -501,8 +497,9 @@ static int print_data(struct libscols_table *tb, bytes = mbs_truncate(data, &len); /* updates 'len' */ } - /* multi-line cell */ - if (len > width && scols_column_is_wrap(cl)) { + /* 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; @@ -989,30 +986,6 @@ static void dbg_columns(struct libscols_table *tb) dbg_column(tb, cl); } -/* count the maximal size of \n terminated chunk in the @data - * for example for "AAA\nBBBB\nXX" the wrap size is 4 ('BBBB'). - */ -static size_t count_wrapnl_size(const char *data) -{ - size_t sum = 0; - - while (data && *data) { - const char *p = data; - size_t sz; - - p = strchr(data, '\n'); - if (p) { - sz = mbs_safe_nwidth(data, p - data, NULL); - p++; - } else - sz = mbs_safe_width(data); - - sum = max(sum, sz); - data = p;; - } - - return sum; -} /* * This function counts column width. @@ -1061,8 +1034,8 @@ static int count_column_width(struct libscols_table *tb, if (!data) len = 0; - else if (scols_column_is_wrapnl(cl)) - len = count_wrapnl_size(data); + else if (scols_column_is_customwrap(cl)) + len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data); else len = mbs_safe_width(data); @@ -1265,7 +1238,8 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf continue; /* never truncate columns with absolute sizes */ if (scols_column_is_tree(cl) && width <= cl->width_treeart) continue; /* never truncate the tree */ - if (trunc_only && !(scols_column_is_trunc(cl) || scols_column_is_wrap(cl))) + if (trunc_only && !(scols_column_is_trunc(cl) || + (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl)))) continue; if (cl->width == cl->width_min) continue; -- cgit v1.2.3-55-g7522