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>


   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*

  28  * Copyright 2017 Joyent, Inc.
  29  */
  30 
  31 #include <errno.h>
  32 #include <sys/types.h>
  33 #include <stdlib.h>
  34 #include <string.h>
  35 #include <strings.h>
  36 #include <stdio.h>
  37 #include <ofmt.h>
  38 #include <sys/termios.h>
  39 #include <unistd.h>
  40 #include <sys/sysmacros.h>
  41 #include <libintl.h>
  42 #include <assert.h>
  43 
  44 /*
  45  * functions and structures to internally process a comma-separated string
  46  * of fields selected for output.
  47  */
  48 typedef struct {
  49         char    *s_buf;
  50         const char **s_fields;  /* array of pointers to the fields in s_buf */
  51         uint_t  s_nfields;      /* the number of fields in s_buf */
  52         uint_t  s_currfield;    /* the current field being processed */
  53 } split_t;
  54 
  55 static void splitfree(split_t *);


  56 
  57 /*
  58  * The state of the output is tracked in a ofmt_state_t structure.
  59  * Each os_fields[i] entry points at an ofmt_field_t array for
  60  * the sub-command whose contents are provided by the caller, with
  61  * os_nfields set to the number of requested fields.
  62  */
  63 typedef struct ofmt_state_s {
  64         ofmt_field_t    *os_fields;
  65         uint_t          os_nfields;
  66         boolean_t       os_lastfield;
  67         uint_t          os_overflow;
  68         struct winsize  os_winsize;
  69         int             os_nrow;
  70         uint_t          os_flags;
  71         int             os_nbad;
  72         char            **os_badfields;
  73         int             os_maxnamelen;  /* longest name (f. multiline) */

  74 } ofmt_state_t;
  75 /*
  76  * A B_TRUE return value from the callback function will print out the contents
  77  * of the output buffer, except when the buffer is returned with the empty
  78  * string "", in which case the  OFMT_VAL_UNDEF will be printed.
  79  *
  80  * If the callback function returns B_FALSE, the "?" string will be emitted.
  81  */
  82 #define OFMT_VAL_UNDEF          "--"
  83 #define OFMT_VAL_UNKNOWN        "?"
  84 


  85 /*
  86  * The maximum number of rows supported by the OFMT_WRAP option.
  87  */
  88 #define OFMT_MAX_ROWS           128
  89 
  90 static void ofmt_print_header(ofmt_state_t *);
  91 static void ofmt_print_field(ofmt_state_t *, ofmt_field_t *, const char *,
  92     boolean_t);
  93 
  94 /*
  95  * Split `str' into at most `maxfields' fields, Return a pointer to a
  96  * split_t containing the split fields, or NULL on failure.
  97  */
  98 static split_t *
  99 split_str(const char *str, uint_t maxfields)
 100 {
 101         char    *field, *token, *lasts = NULL;
 102         split_t *sp;
 103 
 104         if (*str == '\0' || maxfields == 0)
 105                 return (NULL);
 106 
 107         sp = calloc(sizeof (split_t), 1);
 108         if (sp == NULL)
 109                 return (NULL);
 110 
 111         sp->s_buf = strdup(str);
 112         sp->s_fields = malloc(sizeof (char *) * maxfields);
 113         if (sp->s_buf == NULL || sp->s_fields == NULL)
 114                 goto fail;
 115 
 116         token = sp->s_buf;
 117         while ((field = strtok_r(token, ",", &lasts)) != NULL) {
 118                 if (sp->s_nfields == maxfields)
 119                         goto fail;
 120                 token = NULL;
 121                 sp->s_fields[sp->s_nfields++] = field;
 122         }
 123         return (sp);
 124 fail:
 125         splitfree(sp);
 126         return (NULL);
 127 }
 128 
 129 /*
 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.
 134  */
 135 static split_t *
 136 split_max(const ofmt_field_t *template, uint_t maxcols)
 137 {
 138         const ofmt_field_t *ofp;
 139         split_t *sp;
 140         int i, cols, nfields = 0;
 141 
 142         sp = calloc(sizeof (split_t), 1);
 143         if (sp == NULL)
 144                 return (NULL);
 145 
 146         for (ofp = template; ofp->of_name != NULL; ofp++)
 147                 nfields++;
 148 
 149         sp->s_fields = malloc(sizeof (char *) * nfields);
 150         if (sp->s_fields == NULL)
 151                 goto fail;
 152         cols = 0;
 153         for (i = 0; i < nfields; i++) {
 154                 cols += template[i].of_width;
 155                 /*
 156                  * If all fields are implied without explicitly passing
 157                  * in a fields_str, build a list of field names, stopping
 158                  * when we run out of columns.
 159                  */
 160                 if (maxcols > 0 && cols > maxcols)
 161                         break;
 162                 sp->s_fields[sp->s_nfields++] = template[i].of_name;
 163         }
 164         return (sp);
 165 fail:
 166         splitfree(sp);
 167         return (NULL);
 168 }
 169 
 170 /*
 171  * Free the split_t structure pointed to by `sp'.
 172  */
 173 static void
 174 splitfree(split_t *sp)
 175 {
 176         if (sp == NULL)
 177                 return;
 178         free(sp->s_buf);
 179         free(sp->s_fields);
 180         free(sp);
 181 }
 182 
 183 /*
 184  * Open a handle to be used for printing formatted output.
 185  */
 186 ofmt_status_t
 187 ofmt_open(const char *str, const ofmt_field_t *template, uint_t flags,
 188     uint_t maxcols, ofmt_handle_t *ofmt)
 189 {
 190         split_t         *sp;
 191         uint_t          i, of_index;
 192         const ofmt_field_t *ofp;
 193         ofmt_field_t    *of;
 194         ofmt_state_t    *os = NULL;

 195         ofmt_status_t   error = OFMT_SUCCESS;
 196         boolean_t       parsable = (flags & OFMT_PARSABLE);
 197         boolean_t       wrap = (flags & OFMT_WRAP);
 198         boolean_t       multiline = (flags & OFMT_MULTILINE);
 199 
 200         *ofmt = NULL;
 201         if (parsable) {
 202                 if (multiline)
 203                         return (OFMT_EPARSEMULTI);
 204                 /*
 205                  * For parsable output mode, the caller always needs
 206                  * to specify precisely which fields are to be selected,
 207                  * since the set of fields may change over time.
 208                  */
 209                 if (str == NULL || str[0] == '\0')
 210                         return (OFMT_EPARSENONE);
 211                 if (strcasecmp(str, "all") == 0)
 212                         return (OFMT_EPARSEALL);
 213                 if (wrap)
 214                         return (OFMT_EPARSEWRAP);
 215         }
 216         if (template == NULL)
 217                 return (OFMT_ENOTEMPLATE);
 218 

 219         /*
 220          * split str into the columns selected, or construct the
 221          * full set of columns (equivalent to -o all).
 222          */
 223         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                 sp = split_str(str, nfields);
 237         } else {
 238                 if (parsable || (str != NULL && strcasecmp(str, "all") == 0))
 239                         maxcols = 0;
 240                 sp = split_max(template, maxcols);
 241         }
 242         if (sp == NULL)
 243                 goto nomem;
 244 
 245         os = calloc(sizeof (ofmt_state_t) +
 246             sp->s_nfields * sizeof (ofmt_field_t), 1);
 247         if (os == NULL)
 248                 goto nomem;
 249         *ofmt = os;
 250         os->os_fields = (ofmt_field_t *)&os[1];
 251         os->os_flags = flags;

 252 
 253         of = os->os_fields;
 254         of_index = 0;
 255         /*
 256          * sp->s_nfields is the number of fields requested in fields_str.
 257          * nfields is the number of fields in template.
 258          */
 259         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)

 262                                 break;
 263                 }
 264 
 265                 if (ofp->of_name == NULL) {
 266                         int nbad = os->os_nbad++;
 267 
 268                         error = OFMT_EBADFIELDS;
 269                         if (os->os_badfields == NULL) {
 270                                 os->os_badfields = malloc(sp->s_nfields *
 271                                     sizeof (char *));
 272                                 if (os->os_badfields == NULL)
 273                                         goto nomem;
 274                         }
 275                         os->os_badfields[nbad] = strdup(sp->s_fields[i]);
 276                         if (os->os_badfields[nbad] == NULL)
 277                                 goto nomem;
 278                         continue;
 279                 }
 280                 of[of_index].of_name = strdup(ofp->of_name);
 281                 if (of[of_index].of_name == NULL)
 282                         goto nomem;
 283                 if (multiline) {
 284                         int n = strlen(of[of_index].of_name);
 285 
 286                         os->os_maxnamelen = MAX(n, os->os_maxnamelen);
 287                 }
 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;
 291                 of_index++;
 292         }
 293         splitfree(sp);
 294         if (of_index == 0) /* all values in str are bogus */
 295                 return (OFMT_ENOFIELDS);
 296         os->os_nfields = of_index; /* actual number of fields printed */
 297         ofmt_update_winsize(*ofmt);
 298         return (error);
 299 nomem:
 300         error = OFMT_ENOMEM;
 301         if (os != NULL)
 302                 ofmt_close(os);
 303         *ofmt = NULL;
 304         splitfree(sp);
 305         return (error);
 306 }
 307 






 308 /*
 309  * free resources associated with the ofmt_handle_t
 310  */
 311 void
 312 ofmt_close(ofmt_handle_t ofmt)
 313 {
 314         ofmt_state_t *os = ofmt;
 315         int i;
 316 
 317         if (os == NULL)
 318                 return;
 319         for (i = 0; i < os->os_nfields; i++)
 320                 free(os->os_fields[i].of_name);
 321         for (i = 0; i < os->os_nbad; i++)
 322                 free(os->os_badfields[i]);
 323         free(os->os_badfields);
 324         free(os);
 325 }
 326 
 327 /*
 328  * Print the value for the selected field by calling the callback-function
 329  * registered for the field.
 330  */
 331 static void
 332 ofmt_print_field(ofmt_state_t *os, ofmt_field_t *ofp, const char *value,
 333     boolean_t escsep)
 334 {
 335         uint_t  width = ofp->of_width;
 336         uint_t  valwidth;
 337         uint_t  compress;
 338         boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
 339         boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
 340         boolean_t rightjust = (os->os_flags & OFMT_RIGHTJUST);
 341         char    c;
 342 
 343         /*
 344          * Parsable fields are separated by ':'. If such a field contains
 345          * a ':' or '\', this character is prefixed by a '\'.
 346          */
 347         if (parsable) {
 348                 if (os->os_nfields == 1) {
 349                         (void) printf("%s", value);
 350                         return;
 351                 }
 352                 while ((c = *value++) != '\0') {
 353                         if (escsep && ((c == ':' || c == '\\')))
 354                                 (void) putchar('\\');
 355                         (void) putchar(c);
 356                 }
 357                 if (!os->os_lastfield)
 358                         (void) putchar(':');
 359         } else if (multiline) {
 360                 if (value[0] == '\0')
 361                         value = OFMT_VAL_UNDEF;
 362                 (void) printf("%*.*s: %s", os->os_maxnamelen,
 363                     os->os_maxnamelen, ofp->of_name, value);
 364                 if (!os->os_lastfield)
 365                         (void) putchar('\n');
 366         } else {
 367                 if (os->os_lastfield) {
 368                         if (rightjust)
 369                                 (void) printf("%*s", width, value);
 370                         else
 371                                 (void) printf("%s", value);
 372                         os->os_overflow = 0;
 373                         return;
 374                 }
 375 
 376                 valwidth = strlen(value);
 377                 if (valwidth + os->os_overflow >= width) {
 378                         os->os_overflow += valwidth - width + 1;


 438 {
 439         ofmt_state_t *os = ofmt;
 440         int i;
 441         char value[1024];
 442         ofmt_field_t *of;
 443         boolean_t escsep, more_rows;
 444         ofmt_arg_t ofarg;
 445         split_t **sp = NULL;
 446         boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
 447         boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
 448         boolean_t wrap = (os->os_flags & OFMT_WRAP);
 449 
 450         if (wrap) {
 451                 sp = calloc(sizeof (split_t *), os->os_nfields);
 452                 if (sp == NULL)
 453                         return;
 454         }
 455 
 456         if ((os->os_nrow++ % os->os_winsize.ws_row) == 0 &&
 457             !parsable && !multiline) {

 458                 ofmt_print_header(os);
 459                 os->os_nrow++;
 460         }
 461 
 462         if (multiline && os->os_nrow > 1)
 463                 (void) putchar('\n');
 464 
 465         of = os->os_fields;
 466         escsep = (os->os_nfields > 1);
 467         more_rows = B_FALSE;
 468         for (i = 0; i < os->os_nfields; i++) {
 469                 os->os_lastfield = (i + 1 == os->os_nfields);
 470                 value[0] = '\0';
 471                 ofarg.ofmt_id = of[i].of_id;
 472                 ofarg.ofmt_cbarg = arg;
 473 
 474                 if ((*of[i].of_cb)(&ofarg, value, sizeof (value))) {
 475                         if (wrap) {
 476                                 /*
 477                                  * 'value' will be split at comma boundaries


 504                         if (sp[i] != NULL &&
 505                             sp[i]->s_currfield < sp[i]->s_nfields)
 506                                 more_rows = B_TRUE;
 507 
 508                         ofmt_print_field(os, &of[i], value, escsep);
 509                 }
 510                 (void) putchar('\n');
 511         }
 512         (void) fflush(stdout);
 513 
 514         if (sp != NULL) {
 515                 for (i = 0; i < os->os_nfields; i++)
 516                         splitfree(sp[i]);
 517                 free(sp);
 518         }
 519 }
 520 
 521 /*
 522  * Print the field headers
 523  */
 524 static void
 525 ofmt_print_header(ofmt_state_t *os)
 526 {

 527         int i;
 528         ofmt_field_t *of = os->os_fields;
 529         boolean_t escsep = (os->os_nfields > 1);
 530 
 531         for (i = 0; i < os->os_nfields; i++) {
 532                 os->os_lastfield = (i + 1 == os->os_nfields);
 533                 ofmt_print_field(os, &of[i], of[i].of_name, escsep);
 534         }
 535         (void) putchar('\n');
 536 }
 537 
 538 /*
 539  * Update the current window size.
 540  */
 541 void
 542 ofmt_update_winsize(ofmt_handle_t ofmt)
 543 {
 544         ofmt_state_t *os = ofmt;
 545         struct winsize *winsize = &os->os_winsize;
 546 




   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright (c) 2015 by Delphix. All rights reserved.
  29  * Copyright 2017 Joyent, Inc.
  30  */
  31 
  32 #include <errno.h>
  33 #include <sys/types.h>
  34 #include <stdlib.h>
  35 #include <string.h>
  36 #include <strings.h>
  37 #include <stdio.h>
  38 #include <ofmt.h>
  39 #include <sys/termios.h>
  40 #include <unistd.h>
  41 #include <sys/sysmacros.h>
  42 #include <libintl.h>
  43 #include <assert.h>
  44 
  45 /*
  46  * functions and structures to internally process a comma-separated string
  47  * of fields selected for output.
  48  */
  49 typedef struct {
  50         char    *s_buf;
  51         const char **s_fields;  /* array of pointers to the fields in s_buf */
  52         uint_t  s_nfields;      /* the number of fields in s_buf */
  53         uint_t  s_currfield;    /* the current field being processed */
  54 } split_t;
  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);
  59 
  60 /*
  61  * The state of the output is tracked in a ofmt_state_t structure.
  62  * Each os_fields[i] entry points at an ofmt_field_t array for
  63  * the sub-command whose contents are provided by the caller, with
  64  * os_nfields set to the number of requested fields.
  65  */
  66 typedef struct ofmt_state_s {
  67         ofmt_field_t    *os_fields;
  68         uint_t          os_nfields;
  69         boolean_t       os_lastfield;
  70         uint_t          os_overflow;
  71         struct winsize  os_winsize;
  72         int             os_nrow;
  73         uint_t          os_flags;
  74         int             os_nbad;
  75         char            **os_badfields;
  76         int             os_maxnamelen;  /* longest name (f. multiline) */
  77         char            os_fs;          /* field seperator */
  78 } ofmt_state_t;
  79 /*
  80  * A B_TRUE return value from the callback function will print out the contents
  81  * of the output buffer, except when the buffer is returned with the empty
  82  * string "", in which case the  OFMT_VAL_UNDEF will be printed.
  83  *
  84  * If the callback function returns B_FALSE, the "?" string will be emitted.
  85  */
  86 #define OFMT_VAL_UNDEF          "--"
  87 #define OFMT_VAL_UNKNOWN        "?"
  88 
  89 #define OFMT_DEFAULT_FS         ':'
  90 
  91 /*
  92  * The maximum number of rows supported by the OFMT_WRAP option.
  93  */
  94 #define OFMT_MAX_ROWS           128
  95 

  96 static void ofmt_print_field(ofmt_state_t *, ofmt_field_t *, const char *,
  97     boolean_t);
  98 
  99 /*
 100  * Split `str' into at most `maxfields' fields, Return a pointer to a
 101  * split_t containing the split fields, or NULL on failure.
 102  */
 103 static split_t *
 104 split_str(const char *str, uint_t maxfields)
 105 {
 106         char    *field, *token, *lasts = NULL;
 107         split_t *sp;
 108 
 109         if (*str == '\0' || maxfields == 0)
 110                 return (NULL);
 111 
 112         sp = calloc(sizeof (split_t), 1);
 113         if (sp == NULL)
 114                 return (NULL);
 115 
 116         sp->s_buf = strdup(str);
 117         sp->s_fields = malloc(sizeof (char *) * maxfields);
 118         if (sp->s_buf == NULL || sp->s_fields == NULL)
 119                 goto fail;
 120 
 121         token = sp->s_buf;
 122         while ((field = strtok_r(token, ",", &lasts)) != NULL) {
 123                 if (sp->s_nfields == maxfields)
 124                         goto fail;
 125                 token = NULL;
 126                 sp->s_fields[sp->s_nfields++] = field;
 127         }
 128         return (sp);
 129 fail:
 130         splitfree(sp);
 131         return (NULL);
 132 }
 133 
 134 /*
 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
 139  */
 140 static split_t *
 141 split_fields(const ofmt_field_t *template, uint_t maxfields, uint_t maxcols)
 142 {

 143         split_t *sp;
 144         int i, cols;
 145 
 146         sp = calloc(sizeof (split_t), 1);
 147         if (sp == NULL)
 148                 return (NULL);
 149 
 150         sp->s_fields = malloc(sizeof (char *) * maxfields);



 151         if (sp->s_fields == NULL)
 152                 goto fail;
 153         cols = 0;
 154         for (i = 0; i < maxfields; i++) {
 155                 cols += template[i].of_width;
 156                 /*
 157                  * If all fields are implied without explicitly passing
 158                  * in a fields_str, build a list of field names, stopping
 159                  * when we run out of columns.
 160                  */
 161                 if (maxcols > 0 && cols > maxcols)
 162                         break;
 163                 sp->s_fields[sp->s_nfields++] = template[i].of_name;
 164         }
 165         return (sp);
 166 fail:
 167         splitfree(sp);
 168         return (NULL);
 169 }
 170 
 171 /*
 172  * Free the split_t structure pointed to by `sp'.
 173  */
 174 static void
 175 splitfree(split_t *sp)
 176 {
 177         if (sp == NULL)
 178                 return;
 179         free(sp->s_buf);
 180         free(sp->s_fields);
 181         free(sp);
 182 }
 183 
 184 /*
 185  * Open a handle to be used for printing formatted output.
 186  */
 187 ofmt_status_t
 188 ofmt_open(const char *str, const ofmt_field_t *template, uint_t flags,
 189     uint_t maxcols, ofmt_handle_t *ofmt)
 190 {
 191         split_t         *sp;
 192         uint_t          i, j, of_index;
 193         const ofmt_field_t *ofp;
 194         ofmt_field_t    *of;
 195         ofmt_state_t    *os = NULL;
 196         uint_t          nfields = 0;
 197         ofmt_status_t   error = OFMT_SUCCESS;
 198         boolean_t       parsable = (flags & OFMT_PARSABLE);
 199         boolean_t       wrap = (flags & OFMT_WRAP);
 200         boolean_t       multiline = (flags & OFMT_MULTILINE);
 201 
 202         *ofmt = NULL;
 203         if (parsable) {
 204                 if (multiline)
 205                         return (OFMT_EPARSEMULTI);
 206                 /*
 207                  * For parsable output mode, the caller always needs
 208                  * to specify precisely which fields are to be selected,
 209                  * since the set of fields may change over time.
 210                  */
 211                 if (str == NULL || str[0] == '\0')
 212                         return (OFMT_EPARSENONE);
 213                 if (strcasecmp(str, "all") == 0)
 214                         return (OFMT_EPARSEALL);
 215                 if (wrap)
 216                         return (OFMT_EPARSEWRAP);
 217         }
 218         if (template == NULL)
 219                 return (OFMT_ENOTEMPLATE);
 220         for (ofp = template; ofp->of_name != NULL; ofp++)
 221                 nfields++;
 222         /*
 223          * split str into the columns selected, or construct the
 224          * full set of columns (equivalent to -o all).
 225          */
 226         if (str != NULL && strcasecmp(str, "all") != 0) {












 227                 sp = split_str(str, nfields);
 228         } else {
 229                 if (parsable || (str != NULL && strcasecmp(str, "all") == 0))
 230                         maxcols = 0;
 231                 sp = split_fields(template, nfields, maxcols);
 232         }
 233         if (sp == NULL)
 234                 goto nomem;
 235 
 236         os = calloc(sizeof (ofmt_state_t) +
 237             sp->s_nfields * sizeof (ofmt_field_t), 1);
 238         if (os == NULL)
 239                 goto nomem;
 240         *ofmt = os;
 241         os->os_fields = (ofmt_field_t *)&os[1];
 242         os->os_flags = flags;
 243         os->os_fs = OFMT_DEFAULT_FS;
 244 
 245         of = os->os_fields;
 246         of_index = 0;
 247         /*
 248          * sp->s_nfields is the number of fields requested in fields_str.
 249          * nfields is the number of fields in template.
 250          */
 251         for (i = 0; i < sp->s_nfields; i++) {
 252                 for (j = 0; j < nfields; j++) {
 253                         if (strcasecmp(sp->s_fields[i],
 254                             template[j].of_name) == 0) {
 255                                 break;
 256                         }
 257                 }
 258                 if (j == nfields) {
 259                         int nbad = os->os_nbad++;
 260 
 261                         error = OFMT_EBADFIELDS;
 262                         if (os->os_badfields == NULL) {
 263                                 os->os_badfields = malloc(sp->s_nfields *
 264                                     sizeof (char *));
 265                                 if (os->os_badfields == NULL)
 266                                         goto nomem;
 267                         }
 268                         os->os_badfields[nbad] = strdup(sp->s_fields[i]);
 269                         if (os->os_badfields[nbad] == NULL)
 270                                 goto nomem;
 271                         continue;
 272                 }
 273                 of[of_index].of_name = strdup(template[j].of_name);
 274                 if (of[of_index].of_name == NULL)
 275                         goto nomem;
 276                 if (multiline) {
 277                         int n = strlen(of[of_index].of_name);
 278 
 279                         os->os_maxnamelen = MAX(n, os->os_maxnamelen);
 280                 }
 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;
 284                 of_index++;
 285         }
 286         splitfree(sp);
 287         if (of_index == 0) /* all values in str are bogus */
 288                 return (OFMT_ENOFIELDS);
 289         os->os_nfields = of_index; /* actual number of fields printed */
 290         ofmt_update_winsize(*ofmt);
 291         return (error);
 292 nomem:
 293         error = OFMT_ENOMEM;
 294         if (os != NULL)
 295                 ofmt_close(os);
 296         *ofmt = NULL;
 297         splitfree(sp);
 298         return (error);
 299 }
 300 
 301 void
 302 ofmt_set_fs(ofmt_handle_t ofmt, char fs)
 303 {
 304         ((ofmt_state_t *)ofmt)->os_fs = fs;
 305 }
 306 
 307 /*
 308  * free resources associated with the ofmt_handle_t
 309  */
 310 void
 311 ofmt_close(ofmt_handle_t ofmt)
 312 {
 313         ofmt_state_t *os = ofmt;
 314         int i;
 315 
 316         if (os == NULL)
 317                 return;
 318         for (i = 0; i < os->os_nfields; i++)
 319                 free(os->os_fields[i].of_name);
 320         for (i = 0; i < os->os_nbad; i++)
 321                 free(os->os_badfields[i]);
 322         free(os->os_badfields);
 323         free(os);
 324 }
 325 
 326 /*
 327  * Print the value for the selected field by calling the callback-function
 328  * registered for the field.
 329  */
 330 static void
 331 ofmt_print_field(ofmt_state_t *os, ofmt_field_t *ofp, const char *value,
 332     boolean_t escsep)
 333 {
 334         uint_t  width = ofp->of_width;
 335         uint_t  valwidth;
 336         uint_t  compress;
 337         boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
 338         boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
 339         boolean_t rightjust = (os->os_flags & OFMT_RIGHTJUST);
 340         char    c;
 341 
 342         /*
 343          * Parsable fields are separated by os_fs. os_fs and '\' are escaped
 344          * (prefixed by a) '\'.
 345          */
 346         if (parsable) {
 347                 if (os->os_nfields == 1) {
 348                         (void) printf("%s", value);
 349                         return;
 350                 }
 351                 while ((c = *value++) != '\0') {
 352                         if (escsep && ((c == os->os_fs || c == '\\')))
 353                                 (void) putchar('\\');
 354                         (void) putchar(c);
 355                 }
 356                 if (!os->os_lastfield)
 357                         (void) putchar(os->os_fs);
 358         } else if (multiline) {
 359                 if (value[0] == '\0')
 360                         value = OFMT_VAL_UNDEF;
 361                 (void) printf("%*.*s: %s", os->os_maxnamelen,
 362                     os->os_maxnamelen, ofp->of_name, value);
 363                 if (!os->os_lastfield)
 364                         (void) putchar('\n');
 365         } else {
 366                 if (os->os_lastfield) {
 367                         if (rightjust)
 368                                 (void) printf("%*s", width, value);
 369                         else
 370                                 (void) printf("%s", value);
 371                         os->os_overflow = 0;
 372                         return;
 373                 }
 374 
 375                 valwidth = strlen(value);
 376                 if (valwidth + os->os_overflow >= width) {
 377                         os->os_overflow += valwidth - width + 1;


 437 {
 438         ofmt_state_t *os = ofmt;
 439         int i;
 440         char value[1024];
 441         ofmt_field_t *of;
 442         boolean_t escsep, more_rows;
 443         ofmt_arg_t ofarg;
 444         split_t **sp = NULL;
 445         boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
 446         boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
 447         boolean_t wrap = (os->os_flags & OFMT_WRAP);
 448 
 449         if (wrap) {
 450                 sp = calloc(sizeof (split_t *), os->os_nfields);
 451                 if (sp == NULL)
 452                         return;
 453         }
 454 
 455         if ((os->os_nrow++ % os->os_winsize.ws_row) == 0 &&
 456             !parsable && !multiline) {
 457                 if (!(os->os_flags & OFMT_NOHEADER))
 458                         ofmt_print_header(os);
 459                 os->os_nrow++;
 460         }
 461 
 462         if (multiline && os->os_nrow > 1)
 463                 (void) putchar('\n');
 464 
 465         of = os->os_fields;
 466         escsep = (os->os_nfields > 1);
 467         more_rows = B_FALSE;
 468         for (i = 0; i < os->os_nfields; i++) {
 469                 os->os_lastfield = (i + 1 == os->os_nfields);
 470                 value[0] = '\0';
 471                 ofarg.ofmt_id = of[i].of_id;
 472                 ofarg.ofmt_cbarg = arg;
 473 
 474                 if ((*of[i].of_cb)(&ofarg, value, sizeof (value))) {
 475                         if (wrap) {
 476                                 /*
 477                                  * 'value' will be split at comma boundaries


 504                         if (sp[i] != NULL &&
 505                             sp[i]->s_currfield < sp[i]->s_nfields)
 506                                 more_rows = B_TRUE;
 507 
 508                         ofmt_print_field(os, &of[i], value, escsep);
 509                 }
 510                 (void) putchar('\n');
 511         }
 512         (void) fflush(stdout);
 513 
 514         if (sp != NULL) {
 515                 for (i = 0; i < os->os_nfields; i++)
 516                         splitfree(sp[i]);
 517                 free(sp);
 518         }
 519 }
 520 
 521 /*
 522  * Print the field headers
 523  */
 524 void
 525 ofmt_print_header(ofmt_handle_t ofmt)
 526 {
 527         ofmt_state_t *os = ofmt;
 528         int i;
 529         ofmt_field_t *of = os->os_fields;
 530         boolean_t escsep = (os->os_nfields > 1);
 531 
 532         for (i = 0; i < os->os_nfields; i++) {
 533                 os->os_lastfield = (i + 1 == os->os_nfields);
 534                 ofmt_print_field(os, &of[i], of[i].of_name, escsep);
 535         }
 536         (void) putchar('\n');
 537 }
 538 
 539 /*
 540  * Update the current window size.
 541  */
 542 void
 543 ofmt_update_winsize(ofmt_handle_t ofmt)
 544 {
 545         ofmt_state_t *os = ofmt;
 546         struct winsize *winsize = &os->os_winsize;
 547