]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libsmartcols: change "export" behavior, add "shellvar" flag
authorKarel Zak <kzak@redhat.com>
Fri, 11 Feb 2022 11:06:41 +0000 (12:06 +0100)
committerKarel Zak <kzak@redhat.com>
Fri, 11 Feb 2022 11:06:41 +0000 (12:06 +0100)
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 <kzak@redhat.com>
libsmartcols/docs/libsmartcols-sections.txt
libsmartcols/samples/fromfile.c
libsmartcols/src/column.c
libsmartcols/src/libsmartcols.h.in
libsmartcols/src/libsmartcols.sym
libsmartcols/src/print.c
libsmartcols/src/smartcolsP.h
libsmartcols/src/table.c

index ec0c9bd65e4c91b8c15155b55b4a2e13891ac3bc..aa7bb1547584f730dfc601a6099bffdbba49c354 100644 (file)
@@ -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
index 3b09a09dbcbf37443c122acb07e32d051513fa98..0fdc9295e8574fe12dd6119dc527045934b4b0c4 100644 (file)
@@ -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");
index c11df69f595985cda8c7852a24f643f87128be50..d5a00f073901238e4fb340cdd5b21a5c85d8dd80 100644 (file)
@@ -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
index bddcbad746c5519ee37f54933e471031b534fb0d..6b64bc81f8eea6d0030bfdfff260a63f4e8c4e3a 100644 (file)
@@ -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);
index e678faf14a366fe433f7e9d45b1f2b5c55f26f2b..309535f7b939c5c87b36f150397c021687727402 100644 (file)
@@ -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;
index cf41e0ab972bf8d6e1626c7e45ffbd10145f69ae..906df7e2a9f4d889f73f629b3f56e3367ea570fb 100644 (file)
@@ -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;
index d2b14e49cc43e3e72f74764cb73bb19f6760da24..8b83a1420cfa38b10819ac759ea0194b13895705 100644 (file)
@@ -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 */
index cbc5fd42a58f10f55ac90ca7e82a7cdbcb0baef6..30e9194fc556db73571fb69299e7bc1b538e4e88 100644 (file)
@@ -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