Print this page
11545 Want configurable output field separator for libofmt
Portions contributed by: Cody Peter Mello <cody.mello@joyent.com>
Reviewed by: Jason King <jason.king@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>

@@ -23,10 +23,11 @@
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 /*
+ * Copyright (c) 2015 by Delphix. All rights reserved.
  * Copyright 2017 Joyent, Inc.
  */
 
 #include <errno.h>
 #include <sys/types.h>

@@ -51,10 +52,12 @@
         uint_t  s_nfields;      /* the number of fields in s_buf */
         uint_t  s_currfield;    /* the current field being processed */
 } split_t;
 
 static void splitfree(split_t *);
+static split_t *split_str(const char *, uint_t);
+static split_t *split_fields(const ofmt_field_t *, uint_t, uint_t);
 
 /*
  * The state of the output is tracked in a ofmt_state_t structure.
  * Each os_fields[i] entry points at an ofmt_field_t array for
  * the sub-command whose contents are provided by the caller, with

@@ -69,10 +72,11 @@
         int             os_nrow;
         uint_t          os_flags;
         int             os_nbad;
         char            **os_badfields;
         int             os_maxnamelen;  /* longest name (f. multiline) */
+        char            os_fs;          /* field seperator */
 } ofmt_state_t;
 /*
  * A B_TRUE return value from the callback function will print out the contents
  * of the output buffer, except when the buffer is returned with the empty
  * string "", in which case the  OFMT_VAL_UNDEF will be printed.

@@ -80,16 +84,17 @@
  * If the callback function returns B_FALSE, the "?" string will be emitted.
  */
 #define OFMT_VAL_UNDEF          "--"
 #define OFMT_VAL_UNKNOWN        "?"
 
+#define OFMT_DEFAULT_FS         ':'
+
 /*
  * The maximum number of rows supported by the OFMT_WRAP option.
  */
 #define OFMT_MAX_ROWS           128
 
-static void ofmt_print_header(ofmt_state_t *);
 static void ofmt_print_field(ofmt_state_t *, ofmt_field_t *, const char *,
     boolean_t);
 
 /*
  * Split `str' into at most `maxfields' fields, Return a pointer to a

@@ -125,34 +130,30 @@
         splitfree(sp);
         return (NULL);
 }
 
 /*
- * Split a template into its maximum number of fields (capped by the maxcols
- * if it's non-zero).  Return a pointer to a split_t containing the split
- * fields, or NULL on failure.  Invoked when all fields are implicitly
- * selected at handle creation.
+ * Split `fields' into at most `maxfields' fields. Return a pointer to
+ * a split_t containing the split fields, or NULL on failure. Invoked
+ * when all fields are implicitly selected at handle creation by
+ * passing in a NULL fields_str
  */
 static split_t *
-split_max(const ofmt_field_t *template, uint_t maxcols)
+split_fields(const ofmt_field_t *template, uint_t maxfields, uint_t maxcols)
 {
-        const ofmt_field_t *ofp;
         split_t *sp;
-        int i, cols, nfields = 0;
+        int i, cols;
 
         sp = calloc(sizeof (split_t), 1);
         if (sp == NULL)
                 return (NULL);
 
-        for (ofp = template; ofp->of_name != NULL; ofp++)
-                nfields++;
-
-        sp->s_fields = malloc(sizeof (char *) * nfields);
+        sp->s_fields = malloc(sizeof (char *) * maxfields);
         if (sp->s_fields == NULL)
                 goto fail;
         cols = 0;
-        for (i = 0; i < nfields; i++) {
+        for (i = 0; i < maxfields; i++) {
                 cols += template[i].of_width;
                 /*
                  * If all fields are implied without explicitly passing
                  * in a fields_str, build a list of field names, stopping
                  * when we run out of columns.

@@ -186,14 +187,15 @@
 ofmt_status_t
 ofmt_open(const char *str, const ofmt_field_t *template, uint_t flags,
     uint_t maxcols, ofmt_handle_t *ofmt)
 {
         split_t         *sp;
-        uint_t          i, of_index;
+        uint_t          i, j, of_index;
         const ofmt_field_t *ofp;
         ofmt_field_t    *of;
         ofmt_state_t    *os = NULL;
+        uint_t          nfields = 0;
         ofmt_status_t   error = OFMT_SUCCESS;
         boolean_t       parsable = (flags & OFMT_PARSABLE);
         boolean_t       wrap = (flags & OFMT_WRAP);
         boolean_t       multiline = (flags & OFMT_MULTILINE);
 

@@ -213,33 +215,22 @@
                 if (wrap)
                         return (OFMT_EPARSEWRAP);
         }
         if (template == NULL)
                 return (OFMT_ENOTEMPLATE);
-
+        for (ofp = template; ofp->of_name != NULL; ofp++)
+                nfields++;
         /*
          * split str into the columns selected, or construct the
          * full set of columns (equivalent to -o all).
          */
         if (str != NULL && strcasecmp(str, "all") != 0) {
-                const char *c;
-                int nfields = 1;
-
-                /*
-                 * Get an upper bound on the number of fields by counting
-                 * the commas.
-                 */
-                for (c = str; *c != '\0'; c++) {
-                        if (*c == ',')
-                                nfields++;
-                }
-
                 sp = split_str(str, nfields);
         } else {
                 if (parsable || (str != NULL && strcasecmp(str, "all") == 0))
                         maxcols = 0;
-                sp = split_max(template, maxcols);
+                sp = split_fields(template, nfields, maxcols);
         }
         if (sp == NULL)
                 goto nomem;
 
         os = calloc(sizeof (ofmt_state_t) +

@@ -247,24 +238,26 @@
         if (os == NULL)
                 goto nomem;
         *ofmt = os;
         os->os_fields = (ofmt_field_t *)&os[1];
         os->os_flags = flags;
+        os->os_fs = OFMT_DEFAULT_FS;
 
         of = os->os_fields;
         of_index = 0;
         /*
          * sp->s_nfields is the number of fields requested in fields_str.
          * nfields is the number of fields in template.
          */
         for (i = 0; i < sp->s_nfields; i++) {
-                for (ofp = template; ofp->of_name != NULL; ofp++) {
-                        if (strcasecmp(sp->s_fields[i], ofp->of_name) == 0)
+                for (j = 0; j < nfields; j++) {
+                        if (strcasecmp(sp->s_fields[i],
+                            template[j].of_name) == 0) {
                                 break;
                 }
-
-                if (ofp->of_name == NULL) {
+                }
+                if (j == nfields) {
                         int nbad = os->os_nbad++;
 
                         error = OFMT_EBADFIELDS;
                         if (os->os_badfields == NULL) {
                                 os->os_badfields = malloc(sp->s_nfields *

@@ -275,21 +268,21 @@
                         os->os_badfields[nbad] = strdup(sp->s_fields[i]);
                         if (os->os_badfields[nbad] == NULL)
                                 goto nomem;
                         continue;
                 }
-                of[of_index].of_name = strdup(ofp->of_name);
+                of[of_index].of_name = strdup(template[j].of_name);
                 if (of[of_index].of_name == NULL)
                         goto nomem;
                 if (multiline) {
                         int n = strlen(of[of_index].of_name);
 
                         os->os_maxnamelen = MAX(n, os->os_maxnamelen);
                 }
-                of[of_index].of_width = ofp->of_width;
-                of[of_index].of_id = ofp->of_id;
-                of[of_index].of_cb = ofp->of_cb;
+                of[of_index].of_width = template[j].of_width;
+                of[of_index].of_id = template[j].of_id;
+                of[of_index].of_cb = template[j].of_cb;
                 of_index++;
         }
         splitfree(sp);
         if (of_index == 0) /* all values in str are bogus */
                 return (OFMT_ENOFIELDS);

@@ -303,10 +296,16 @@
         *ofmt = NULL;
         splitfree(sp);
         return (error);
 }
 
+void
+ofmt_set_fs(ofmt_handle_t ofmt, char fs)
+{
+        ((ofmt_state_t *)ofmt)->os_fs = fs;
+}
+
 /*
  * free resources associated with the ofmt_handle_t
  */
 void
 ofmt_close(ofmt_handle_t ofmt)

@@ -339,25 +338,25 @@
         boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
         boolean_t rightjust = (os->os_flags & OFMT_RIGHTJUST);
         char    c;
 
         /*
-         * Parsable fields are separated by ':'. If such a field contains
-         * a ':' or '\', this character is prefixed by a '\'.
+         * Parsable fields are separated by os_fs. os_fs and '\' are escaped
+         * (prefixed by a) '\'.
          */
         if (parsable) {
                 if (os->os_nfields == 1) {
                         (void) printf("%s", value);
                         return;
                 }
                 while ((c = *value++) != '\0') {
-                        if (escsep && ((c == ':' || c == '\\')))
+                        if (escsep && ((c == os->os_fs || c == '\\')))
                                 (void) putchar('\\');
                         (void) putchar(c);
                 }
                 if (!os->os_lastfield)
-                        (void) putchar(':');
+                        (void) putchar(os->os_fs);
         } else if (multiline) {
                 if (value[0] == '\0')
                         value = OFMT_VAL_UNDEF;
                 (void) printf("%*.*s: %s", os->os_maxnamelen,
                     os->os_maxnamelen, ofp->of_name, value);

@@ -453,10 +452,11 @@
                         return;
         }
 
         if ((os->os_nrow++ % os->os_winsize.ws_row) == 0 &&
             !parsable && !multiline) {
+                if (!(os->os_flags & OFMT_NOHEADER))
                 ofmt_print_header(os);
                 os->os_nrow++;
         }
 
         if (multiline && os->os_nrow > 1)

@@ -519,13 +519,14 @@
 }
 
 /*
  * Print the field headers
  */
-static void
-ofmt_print_header(ofmt_state_t *os)
+void
+ofmt_print_header(ofmt_handle_t ofmt)
 {
+        ofmt_state_t *os = ofmt;
         int i;
         ofmt_field_t *of = os->os_fields;
         boolean_t escsep = (os->os_nfields > 1);
 
         for (i = 0; i < os->os_nfields; i++) {