From: Karel Zak Date: Fri, 11 Feb 2022 11:06:41 +0000 (+0100) Subject: libsmartcols: change "export" behavior, add "shellvar" flag X-Git-Tag: v2.38-rc2~38 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3b5db50f7321676db431566c418bf2fb556e7601;p=thirdparty%2Futil-linux.git libsmartcols: change "export" behavior, add "shellvar" flag In version 2.37 the "export" output format automatically modifies column names to be compatible with shell's requirements for variable names. This change was backwardly incompatible for people who use for example "lsblk -P" in non-shell environment. It's painful to modify this behavior again in v2.38, but it seems better to add a way how to control this behavior independently on output format as it seems attractive feature for more use-cases. This commit introduces scols_table_enable_shellvar() function to enable/disable this feature. It also introduces scols_column_set_name scols_column_get_name scols_column_get_name_as_shellvar scols_table_is_shellvar to make it easy to work with column names. Fixes: https://github.com/util-linux/util-linux/issues/1594 Signed-off-by: Karel Zak --- diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt index ec0c9bd65e..aa7bb15475 100644 --- a/libsmartcols/docs/libsmartcols-sections.txt +++ b/libsmartcols/docs/libsmartcols-sections.txt @@ -23,6 +23,8 @@ scols_column_get_color scols_column_get_flags scols_column_get_header scols_column_get_json_type +scols_column_get_name +scols_column_get_name_as_shellvar scols_column_get_safechars scols_column_get_table scols_column_get_whint @@ -39,6 +41,7 @@ scols_column_set_cmpfunc scols_column_set_color scols_column_set_flags scols_column_set_json_type +scols_column_set_name scols_column_set_safechars scols_column_set_whint scols_column_set_wrapfunc @@ -128,16 +131,17 @@ scols_table_add_line scols_table_colors_wanted scols_table_enable_ascii scols_table_enable_colors -scols_table_enable_noencoding scols_table_enable_export scols_table_enable_header_repeat scols_table_enable_json scols_table_enable_maxout scols_table_enable_minout +scols_table_enable_noencoding scols_table_enable_noheadings scols_table_enable_nolinesep scols_table_enable_nowrap scols_table_enable_raw +scols_table_enable_shellvar scols_table_get_column scols_table_get_column_separator scols_table_get_line @@ -158,17 +162,17 @@ scols_table_is_header_repeat scols_table_is_json scols_table_is_maxout scols_table_is_minout -scols_table_is_noheadings scols_table_is_noencoding +scols_table_is_noheadings scols_table_is_nolinesep scols_table_is_nowrap scols_table_is_raw +scols_table_is_shellvar scols_table_is_tree scols_table_move_column scols_table_new_column scols_table_new_line scols_table_next_column -scols_table_set_columns_iter scols_table_next_line scols_table_reduce_termwidth scols_table_remove_column @@ -176,6 +180,7 @@ scols_table_remove_columns scols_table_remove_line scols_table_remove_lines scols_table_set_column_separator +scols_table_set_columns_iter scols_table_set_default_symbols scols_table_set_line_separator scols_table_set_name diff --git a/libsmartcols/samples/fromfile.c b/libsmartcols/samples/fromfile.c index 3b09a09dbc..0fdc9295e8 100644 --- a/libsmartcols/samples/fromfile.c +++ b/libsmartcols/samples/fromfile.c @@ -78,17 +78,13 @@ static struct libscols_column *parse_column(FILE *f) switch (nlines) { case 0: /* NAME */ - { - struct libscols_cell *hr; - cl = scols_new_column(); if (!cl) goto fail; - hr = scols_column_get_header(cl); - if (!hr || scols_cell_set_data(hr, line)) + if (scols_column_set_name(cl, line) != 0) goto fail; break; - } + case 1: /* WIDTH-HINT */ { double whint = strtod_or_err(line, "failed to parse column whint"); diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c index c11df69f59..d5a00f0739 100644 --- a/libsmartcols/src/column.c +++ b/libsmartcols/src/column.c @@ -74,6 +74,7 @@ void scols_unref_column(struct libscols_column *cl) free(cl->color); free(cl->safechars); free(cl->pending_data_buf); + free(cl->shellvar); free(cl); } } @@ -245,6 +246,84 @@ struct libscols_cell *scols_column_get_header(struct libscols_column *cl) return &cl->header; } +/** + * scols_column_set_name: + * @cl: a pointer to a struct libscols_column instance + * @name: column name + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.38 + */ +int scols_column_set_name(struct libscols_column *cl, const char *name) +{ + struct libscols_cell *hr = scols_column_get_header(cl); + + if (!hr) + return -EINVAL; + + free(cl->shellvar); + cl->shellvar = NULL; + + return scols_cell_set_data(hr, name); +} + +/** + * scols_column_get_name: + * @cl: a pointer to a struct libscols_column instance + * + * Returns: A pointer to a column name, which is stored in column header + * + * Since: 2.38 + */ +const char *scols_column_get_name(struct libscols_column *cl) +{ + return scols_cell_get_data(&cl->header); +} + +/** + * scols_column_get_name_as_shellvar + * @cl: a pointer to a struct libscols_column instance + * + * Like scols_column_get_name(), but column name is modified to be compatible with shells + * requirements for variable names. + * + * Since: 2.38 + */ +const char *scols_column_get_name_as_shellvar(struct libscols_column *cl) +{ + if (!cl->shellvar) { + const char *s, *name = scols_column_get_name(cl); + char *p; + size_t sz; + + if (!name || !*name) + return NULL; + + /* "1FOO%" --> "_1FOO_PCT */ + sz = strlen(name) + 1 + 3; + p = cl->shellvar = calloc(1, sz + 1); + if (!cl->shellvar) + return NULL; + + /* convert "1FOO" to "_1FOO" */ + if (!isalpha(*name)) + *p++ = '_'; + + /* replace all "bad" chars with "_" */ + for (s = name; *s; s++) + *p++ = !isalnum(*s) ? '_' : *s; + + if (!*s && *(s - 1) == '%') { + *p++ = 'P'; + *p++ = 'C'; + *p++ = 'T'; + } + } + return cl->shellvar; +} + + /** * scols_column_set_color: * @cl: a pointer to a struct libscols_column instance diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in index bddcbad746..6b64bc81f8 100644 --- a/libsmartcols/src/libsmartcols.h.in +++ b/libsmartcols/src/libsmartcols.h.in @@ -190,6 +190,10 @@ 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(const struct libscols_column *cl); +extern int scols_column_set_name(struct libscols_column *cl, const char *name); +extern const char *scols_column_get_name(struct libscols_column *cl); +extern const char *scols_column_get_name_as_shellvar(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 *), @@ -246,6 +250,7 @@ extern int scols_table_is_noheadings(const struct libscols_table *tb); extern int scols_table_is_header_repeat(const struct libscols_table *tb); extern int scols_table_is_empty(const struct libscols_table *tb); extern int scols_table_is_export(const struct libscols_table *tb); +extern int scols_table_is_shellvar(const struct libscols_table *tb); extern int scols_table_is_maxout(const struct libscols_table *tb); extern int scols_table_is_minout(const struct libscols_table *tb); extern int scols_table_is_nowrap(const struct libscols_table *tb); @@ -260,6 +265,7 @@ extern int scols_table_enable_json(struct libscols_table *tb, int enable); extern int scols_table_enable_noheadings(struct libscols_table *tb, int enable); extern int scols_table_enable_header_repeat(struct libscols_table *tb, int enable); extern int scols_table_enable_export(struct libscols_table *tb, int enable); +extern int scols_table_enable_shellvar(struct libscols_table *tb, int enable); extern int scols_table_enable_maxout(struct libscols_table *tb, int enable); extern int scols_table_enable_minout(struct libscols_table *tb, int enable); extern int scols_table_enable_nowrap(struct libscols_table *tb, int enable); diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym index e678faf14a..309535f7b9 100644 --- a/libsmartcols/src/libsmartcols.sym +++ b/libsmartcols/src/libsmartcols.sym @@ -203,4 +203,9 @@ SMARTCOLS_2.35 { SMARTCOLS_2.38 { scols_line_get_column_data; + scols_column_set_name; + scols_column_get_name; + scols_column_get_name_as_shellvar; + scols_table_is_shellvar; + scols_table_enable_shellvar; } SMARTCOLS_2.35; diff --git a/libsmartcols/src/print.c b/libsmartcols/src/print.c index cf41e0ab97..906df7e2a9 100644 --- a/libsmartcols/src/print.c +++ b/libsmartcols/src/print.c @@ -606,8 +606,11 @@ static int print_data(struct libscols_table *tb, if (!data) data = ""; - if (tb->format != SCOLS_FMT_HUMAN) - name = scols_cell_get_data(&cl->header); + if (tb->format != SCOLS_FMT_HUMAN) { + name = scols_table_is_shellvar(tb) ? + scols_column_get_name_as_shellvar(cl) : + scols_column_get_name(cl); + } is_last = is_last_column(cl); @@ -624,9 +627,7 @@ static int print_data(struct libscols_table *tb, return 0; case SCOLS_FMT_EXPORT: - fputs_shell_ident(name, tb->out); - if (endswith(name, "%")) - fputs("PCT", tb->out); + fputs(name, tb->out); fputc('=', tb->out); fputs_quoted(data, tb->out); if (!is_last) @@ -973,7 +974,10 @@ int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf) } } if (!rc) - rc = ul_buffer_append_string(buf, scols_cell_get_data(&cl->header)); + rc = ul_buffer_append_string(buf, + scols_table_is_shellvar(tb) ? + scols_column_get_name_as_shellvar(cl) : + scols_column_get_name(cl)); if (!rc) rc = print_data(tb, cl, NULL, &cl->header, buf); } @@ -1190,7 +1194,9 @@ int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf while (scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) continue; - extra_bufsz += strlen(scols_cell_get_data(&cl->header)); /* data */ + + if (scols_column_get_name(cl)) + extra_bufsz += strlen(scols_column_get_name(cl)); /* data */ extra_bufsz += 2; /* separators */ } break; diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index d2b14e49cc..8b83a1420c 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -126,7 +126,9 @@ struct libscols_column { void *wrapfunc_data; - struct libscols_cell header; + struct libscols_cell header; /* column name with color etc. */ + char *shellvar; /* raw colum name in shell compatible format */ + struct list_head cl_columns; /* member of table->tb_columns */ struct libscols_table *table; @@ -245,6 +247,7 @@ struct libscols_table { is_term :1, /* isatty() */ padding_debug :1, /* output visible padding chars */ is_dummy_print :1, /* printing used for width calculation only */ + is_shellvar :1, /* shell compatible column names */ maxout :1, /* maximize output */ minout :1, /* minimize output (mutually exclusive to maxout) */ header_repeat :1, /* print header after libscols_table->termheight */ diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c index cbc5fd42a5..30e9194fc5 100644 --- a/libsmartcols/src/table.c +++ b/libsmartcols/src/table.c @@ -415,7 +415,6 @@ struct libscols_column *scols_table_new_column(struct libscols_table *tb, int flags) { struct libscols_column *cl; - struct libscols_cell *hr; if (!tb) return NULL; @@ -426,13 +425,8 @@ struct libscols_column *scols_table_new_column(struct libscols_table *tb, if (!cl) return NULL; - /* set column name */ - hr = scols_column_get_header(cl); - if (!hr) + if (scols_column_set_name(cl, name)) goto err; - if (scols_cell_set_data(hr, name)) - goto err; - scols_column_set_whint(cl, whint); scols_column_set_flags(cl, flags); @@ -1078,9 +1072,11 @@ int scols_table_enable_json(struct libscols_table *tb, int enable) * Enable/disable export output format (COLUMNAME="value" ...). * The parsable output formats (export and raw) are mutually exclusive. * - * Note that COLUMNAME maybe be modified on output to contains only chars - * allowed as shell variable identifiers, for example MIN-IO and FSUSE% will be - * MIN_IO and FSUSE_PCT. + * See also scols_table_enable_shellvar(). Note that in version 2.37 (and only + * in this version) scols_table_enable_shellvar() functionality has been + * automatically enabled for "export" format. This behavior has been reverted + * in version 2.38 due to backward compatibility issues. Now it's necessary to + * explicitly call scols_table_enable_shellvar(). * * Returns: 0 on success, negative number in case of an error. */ @@ -1097,6 +1093,29 @@ int scols_table_enable_export(struct libscols_table *tb, int enable) return 0; } +/** + * scols_table_enable_shellvar: + * @tb: table + * @enable: 1 or 0 + * + * Force library to print column names to be compatible with shell requirements + * to variable names. For example "1FOO%" will be printed as "_1FOO_PCT". + * + * Returns: 0 on success, negative number in case of an error. + * + * Since: 2.38 + */ +int scols_table_enable_shellvar(struct libscols_table *tb, int enable) +{ + if (!tb) + return -EINVAL; + + DBG(TAB, ul_debugobj(tb, "shellvar: %s", enable ? "ENABLE" : "DISABLE")); + tb->is_shellvar = enable ? 1 : 0; + return 0; +} + + /** * scols_table_enable_ascii: * @tb: table @@ -1343,6 +1362,20 @@ int scols_table_is_export(const struct libscols_table *tb) return tb->format == SCOLS_FMT_EXPORT; } +/** + * scols_table_is_shellvar: + * @tb: table + * + * Returns: 1 if column names has to be compatible with shell requirements + * to variable names + * + * Since: 2.38 + */ +int scols_table_is_shellvar(const struct libscols_table *tb) +{ + return tb->is_shellvar; +} + /** * scols_table_is_raw: * @tb: table