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>

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libofmt/common/ofmt.c
          +++ new/usr/src/lib/libofmt/common/ofmt.c
↓ open down ↓ 17 lines elided ↑ open up ↑
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24   24   * Use is subject to license terms.
  25   25   */
  26   26  
  27   27  /*
       28 + * Copyright (c) 2015 by Delphix. All rights reserved.
  28   29   * Copyright 2017 Joyent, Inc.
  29   30   */
  30   31  
  31   32  #include <errno.h>
  32   33  #include <sys/types.h>
  33   34  #include <stdlib.h>
  34   35  #include <string.h>
  35   36  #include <strings.h>
  36   37  #include <stdio.h>
  37   38  #include <ofmt.h>
↓ open down ↓ 8 lines elided ↑ open up ↑
  46   47   * of fields selected for output.
  47   48   */
  48   49  typedef struct {
  49   50          char    *s_buf;
  50   51          const char **s_fields;  /* array of pointers to the fields in s_buf */
  51   52          uint_t  s_nfields;      /* the number of fields in s_buf */
  52   53          uint_t  s_currfield;    /* the current field being processed */
  53   54  } split_t;
  54   55  
  55   56  static void splitfree(split_t *);
       57 +static split_t *split_str(const char *, uint_t);
       58 +static split_t *split_fields(const ofmt_field_t *, uint_t, uint_t);
  56   59  
  57   60  /*
  58   61   * The state of the output is tracked in a ofmt_state_t structure.
  59   62   * Each os_fields[i] entry points at an ofmt_field_t array for
  60   63   * the sub-command whose contents are provided by the caller, with
  61   64   * os_nfields set to the number of requested fields.
  62   65   */
  63   66  typedef struct ofmt_state_s {
  64      -        ofmt_field_t    *os_fields;
       67 +        ofmt_field_t    *os_fields;
  65   68          uint_t          os_nfields;
  66   69          boolean_t       os_lastfield;
  67   70          uint_t          os_overflow;
  68   71          struct winsize  os_winsize;
  69   72          int             os_nrow;
  70   73          uint_t          os_flags;
  71   74          int             os_nbad;
  72   75          char            **os_badfields;
  73   76          int             os_maxnamelen;  /* longest name (f. multiline) */
       77 +        char            os_fs;          /* field seperator */
  74   78  } ofmt_state_t;
  75   79  /*
  76   80   * A B_TRUE return value from the callback function will print out the contents
  77   81   * of the output buffer, except when the buffer is returned with the empty
  78   82   * string "", in which case the  OFMT_VAL_UNDEF will be printed.
  79   83   *
  80   84   * If the callback function returns B_FALSE, the "?" string will be emitted.
  81   85   */
  82   86  #define OFMT_VAL_UNDEF          "--"
  83   87  #define OFMT_VAL_UNKNOWN        "?"
  84   88  
       89 +#define OFMT_DEFAULT_FS         ':'
       90 +
  85   91  /*
  86   92   * The maximum number of rows supported by the OFMT_WRAP option.
  87   93   */
  88   94  #define OFMT_MAX_ROWS           128
  89   95  
  90      -static void ofmt_print_header(ofmt_state_t *);
  91   96  static void ofmt_print_field(ofmt_state_t *, ofmt_field_t *, const char *,
  92   97      boolean_t);
  93   98  
  94   99  /*
  95  100   * Split `str' into at most `maxfields' fields, Return a pointer to a
  96  101   * split_t containing the split fields, or NULL on failure.
  97  102   */
  98  103  static split_t *
  99  104  split_str(const char *str, uint_t maxfields)
 100  105  {
↓ open down ↓ 19 lines elided ↑ open up ↑
 120  125                  token = NULL;
 121  126                  sp->s_fields[sp->s_nfields++] = field;
 122  127          }
 123  128          return (sp);
 124  129  fail:
 125  130          splitfree(sp);
 126  131          return (NULL);
 127  132  }
 128  133  
 129  134  /*
 130      - * Split a template into its maximum number of fields (capped by the maxcols
 131      - * if it's non-zero).  Return a pointer to a split_t containing the split
 132      - * fields, or NULL on failure.  Invoked when all fields are implicitly
 133      - * selected at handle creation.
      135 + * Split `fields' into at most `maxfields' fields. Return a pointer to
      136 + * a split_t containing the split fields, or NULL on failure. Invoked
      137 + * when all fields are implicitly selected at handle creation by
      138 + * passing in a NULL fields_str
 134  139   */
 135  140  static split_t *
 136      -split_max(const ofmt_field_t *template, uint_t maxcols)
      141 +split_fields(const ofmt_field_t *template, uint_t maxfields, uint_t maxcols)
 137  142  {
 138      -        const ofmt_field_t *ofp;
 139  143          split_t *sp;
 140      -        int i, cols, nfields = 0;
      144 +        int i, cols;
 141  145  
 142  146          sp = calloc(sizeof (split_t), 1);
 143  147          if (sp == NULL)
 144  148                  return (NULL);
 145  149  
 146      -        for (ofp = template; ofp->of_name != NULL; ofp++)
 147      -                nfields++;
 148      -
 149      -        sp->s_fields = malloc(sizeof (char *) * nfields);
      150 +        sp->s_fields = malloc(sizeof (char *) * maxfields);
 150  151          if (sp->s_fields == NULL)
 151  152                  goto fail;
 152  153          cols = 0;
 153      -        for (i = 0; i < nfields; i++) {
      154 +        for (i = 0; i < maxfields; i++) {
 154  155                  cols += template[i].of_width;
 155  156                  /*
 156  157                   * If all fields are implied without explicitly passing
 157  158                   * in a fields_str, build a list of field names, stopping
 158  159                   * when we run out of columns.
 159  160                   */
 160  161                  if (maxcols > 0 && cols > maxcols)
 161  162                          break;
 162  163                  sp->s_fields[sp->s_nfields++] = template[i].of_name;
 163  164          }
↓ open down ↓ 17 lines elided ↑ open up ↑
 181  182  }
 182  183  
 183  184  /*
 184  185   * Open a handle to be used for printing formatted output.
 185  186   */
 186  187  ofmt_status_t
 187  188  ofmt_open(const char *str, const ofmt_field_t *template, uint_t flags,
 188  189      uint_t maxcols, ofmt_handle_t *ofmt)
 189  190  {
 190  191          split_t         *sp;
 191      -        uint_t          i, of_index;
      192 +        uint_t          i, j, of_index;
 192  193          const ofmt_field_t *ofp;
 193  194          ofmt_field_t    *of;
 194  195          ofmt_state_t    *os = NULL;
      196 +        uint_t          nfields = 0;
 195  197          ofmt_status_t   error = OFMT_SUCCESS;
 196  198          boolean_t       parsable = (flags & OFMT_PARSABLE);
 197  199          boolean_t       wrap = (flags & OFMT_WRAP);
 198  200          boolean_t       multiline = (flags & OFMT_MULTILINE);
 199  201  
 200  202          *ofmt = NULL;
 201  203          if (parsable) {
 202  204                  if (multiline)
 203  205                          return (OFMT_EPARSEMULTI);
 204  206                  /*
↓ open down ↓ 3 lines elided ↑ open up ↑
 208  210                   */
 209  211                  if (str == NULL || str[0] == '\0')
 210  212                          return (OFMT_EPARSENONE);
 211  213                  if (strcasecmp(str, "all") == 0)
 212  214                          return (OFMT_EPARSEALL);
 213  215                  if (wrap)
 214  216                          return (OFMT_EPARSEWRAP);
 215  217          }
 216  218          if (template == NULL)
 217  219                  return (OFMT_ENOTEMPLATE);
 218      -
      220 +        for (ofp = template; ofp->of_name != NULL; ofp++)
      221 +                nfields++;
 219  222          /*
 220  223           * split str into the columns selected, or construct the
 221  224           * full set of columns (equivalent to -o all).
 222  225           */
 223  226          if (str != NULL && strcasecmp(str, "all") != 0) {
 224      -                const char *c;
 225      -                int nfields = 1;
 226      -
 227      -                /*
 228      -                 * Get an upper bound on the number of fields by counting
 229      -                 * the commas.
 230      -                 */
 231      -                for (c = str; *c != '\0'; c++) {
 232      -                        if (*c == ',')
 233      -                                nfields++;
 234      -                }
 235      -
 236  227                  sp = split_str(str, nfields);
 237  228          } else {
 238  229                  if (parsable || (str != NULL && strcasecmp(str, "all") == 0))
 239  230                          maxcols = 0;
 240      -                sp = split_max(template, maxcols);
      231 +                sp = split_fields(template, nfields, maxcols);
 241  232          }
 242  233          if (sp == NULL)
 243  234                  goto nomem;
 244  235  
 245  236          os = calloc(sizeof (ofmt_state_t) +
 246  237              sp->s_nfields * sizeof (ofmt_field_t), 1);
 247  238          if (os == NULL)
 248  239                  goto nomem;
 249  240          *ofmt = os;
 250  241          os->os_fields = (ofmt_field_t *)&os[1];
 251  242          os->os_flags = flags;
      243 +        os->os_fs = OFMT_DEFAULT_FS;
 252  244  
 253  245          of = os->os_fields;
 254  246          of_index = 0;
 255  247          /*
 256  248           * sp->s_nfields is the number of fields requested in fields_str.
 257  249           * nfields is the number of fields in template.
 258  250           */
 259  251          for (i = 0; i < sp->s_nfields; i++) {
 260      -                for (ofp = template; ofp->of_name != NULL; ofp++) {
 261      -                        if (strcasecmp(sp->s_fields[i], ofp->of_name) == 0)
      252 +                for (j = 0; j < nfields; j++) {
      253 +                        if (strcasecmp(sp->s_fields[i],
      254 +                            template[j].of_name) == 0) {
 262  255                                  break;
      256 +                        }
 263  257                  }
 264      -
 265      -                if (ofp->of_name == NULL) {
      258 +                if (j == nfields) {
 266  259                          int nbad = os->os_nbad++;
 267  260  
 268  261                          error = OFMT_EBADFIELDS;
 269  262                          if (os->os_badfields == NULL) {
 270  263                                  os->os_badfields = malloc(sp->s_nfields *
 271  264                                      sizeof (char *));
 272  265                                  if (os->os_badfields == NULL)
 273  266                                          goto nomem;
 274  267                          }
 275  268                          os->os_badfields[nbad] = strdup(sp->s_fields[i]);
 276  269                          if (os->os_badfields[nbad] == NULL)
 277  270                                  goto nomem;
 278  271                          continue;
 279  272                  }
 280      -                of[of_index].of_name = strdup(ofp->of_name);
      273 +                of[of_index].of_name = strdup(template[j].of_name);
 281  274                  if (of[of_index].of_name == NULL)
 282  275                          goto nomem;
 283  276                  if (multiline) {
 284  277                          int n = strlen(of[of_index].of_name);
 285  278  
 286  279                          os->os_maxnamelen = MAX(n, os->os_maxnamelen);
 287  280                  }
 288      -                of[of_index].of_width = ofp->of_width;
 289      -                of[of_index].of_id = ofp->of_id;
 290      -                of[of_index].of_cb = ofp->of_cb;
      281 +                of[of_index].of_width = template[j].of_width;
      282 +                of[of_index].of_id = template[j].of_id;
      283 +                of[of_index].of_cb = template[j].of_cb;
 291  284                  of_index++;
 292  285          }
 293  286          splitfree(sp);
 294  287          if (of_index == 0) /* all values in str are bogus */
 295  288                  return (OFMT_ENOFIELDS);
 296  289          os->os_nfields = of_index; /* actual number of fields printed */
 297  290          ofmt_update_winsize(*ofmt);
 298  291          return (error);
 299  292  nomem:
 300  293          error = OFMT_ENOMEM;
 301  294          if (os != NULL)
 302  295                  ofmt_close(os);
 303  296          *ofmt = NULL;
 304  297          splitfree(sp);
 305  298          return (error);
 306  299  }
 307  300  
      301 +void
      302 +ofmt_set_fs(ofmt_handle_t ofmt, char fs)
      303 +{
      304 +        ((ofmt_state_t *)ofmt)->os_fs = fs;
      305 +}
      306 +
 308  307  /*
 309  308   * free resources associated with the ofmt_handle_t
 310  309   */
 311  310  void
 312  311  ofmt_close(ofmt_handle_t ofmt)
 313  312  {
 314  313          ofmt_state_t *os = ofmt;
 315  314          int i;
 316  315  
 317  316          if (os == NULL)
↓ open down ↓ 16 lines elided ↑ open up ↑
 334  333  {
 335  334          uint_t  width = ofp->of_width;
 336  335          uint_t  valwidth;
 337  336          uint_t  compress;
 338  337          boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
 339  338          boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
 340  339          boolean_t rightjust = (os->os_flags & OFMT_RIGHTJUST);
 341  340          char    c;
 342  341  
 343  342          /*
 344      -         * Parsable fields are separated by ':'. If such a field contains
 345      -         * a ':' or '\', this character is prefixed by a '\'.
      343 +         * Parsable fields are separated by os_fs. os_fs and '\' are escaped
      344 +         * (prefixed by a) '\'.
 346  345           */
 347  346          if (parsable) {
 348  347                  if (os->os_nfields == 1) {
 349  348                          (void) printf("%s", value);
 350  349                          return;
 351  350                  }
 352  351                  while ((c = *value++) != '\0') {
 353      -                        if (escsep && ((c == ':' || c == '\\')))
      352 +                        if (escsep && ((c == os->os_fs || c == '\\')))
 354  353                                  (void) putchar('\\');
 355  354                          (void) putchar(c);
 356  355                  }
 357  356                  if (!os->os_lastfield)
 358      -                        (void) putchar(':');
      357 +                        (void) putchar(os->os_fs);
 359  358          } else if (multiline) {
 360  359                  if (value[0] == '\0')
 361  360                          value = OFMT_VAL_UNDEF;
 362  361                  (void) printf("%*.*s: %s", os->os_maxnamelen,
 363  362                      os->os_maxnamelen, ofp->of_name, value);
 364  363                  if (!os->os_lastfield)
 365  364                          (void) putchar('\n');
 366  365          } else {
 367  366                  if (os->os_lastfield) {
 368  367                          if (rightjust)
↓ open down ↓ 79 lines elided ↑ open up ↑
 448  447          boolean_t wrap = (os->os_flags & OFMT_WRAP);
 449  448  
 450  449          if (wrap) {
 451  450                  sp = calloc(sizeof (split_t *), os->os_nfields);
 452  451                  if (sp == NULL)
 453  452                          return;
 454  453          }
 455  454  
 456  455          if ((os->os_nrow++ % os->os_winsize.ws_row) == 0 &&
 457  456              !parsable && !multiline) {
 458      -                ofmt_print_header(os);
      457 +                if (!(os->os_flags & OFMT_NOHEADER))
      458 +                        ofmt_print_header(os);
 459  459                  os->os_nrow++;
 460  460          }
 461  461  
 462  462          if (multiline && os->os_nrow > 1)
 463  463                  (void) putchar('\n');
 464  464  
 465  465          of = os->os_fields;
 466  466          escsep = (os->os_nfields > 1);
 467  467          more_rows = B_FALSE;
 468  468          for (i = 0; i < os->os_nfields; i++) {
↓ open down ↓ 45 lines elided ↑ open up ↑
 514  514          if (sp != NULL) {
 515  515                  for (i = 0; i < os->os_nfields; i++)
 516  516                          splitfree(sp[i]);
 517  517                  free(sp);
 518  518          }
 519  519  }
 520  520  
 521  521  /*
 522  522   * Print the field headers
 523  523   */
 524      -static void
 525      -ofmt_print_header(ofmt_state_t *os)
      524 +void
      525 +ofmt_print_header(ofmt_handle_t ofmt)
 526  526  {
      527 +        ofmt_state_t *os = ofmt;
 527  528          int i;
 528  529          ofmt_field_t *of = os->os_fields;
 529  530          boolean_t escsep = (os->os_nfields > 1);
 530  531  
 531  532          for (i = 0; i < os->os_nfields; i++) {
 532  533                  os->os_lastfield = (i + 1 == os->os_nfields);
 533  534                  ofmt_print_field(os, &of[i], of[i].of_name, escsep);
 534  535          }
 535  536          (void) putchar('\n');
 536  537  }
↓ open down ↓ 103 lines elided ↑ open up ↑
 640  641          assert(die != NULL);
 641  642          assert(warn != NULL);
 642  643  
 643  644          if (oferr == OFMT_SUCCESS)
 644  645                  return;
 645  646  
 646  647          (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
 647  648  
 648  649          /*
 649  650           * All errors are considered fatal in parsable mode.  OFMT_ENOMEM and
 650      -         * OFMT_ENOFIELDS errors are always fatal, regardless of mode.  For
      651 +         * OFMT_ENOFIELDS errors are always fatal, regardless of mode. For
 651  652           * other errors, we print diagnostics in human-readable mode and
 652  653           * processs what we can.
 653  654           */
 654  655          if (parsable || oferr == OFMT_ENOFIELDS || oferr == OFMT_ENOMEM) {
 655  656                  ofmt_close(ofmt);
 656  657                  die(buf);
 657  658          } else {
 658  659                  warn(buf);
 659  660          }
 660  661  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX