1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   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;
 379                         if (rightjust)
 380                                 (void) printf("%*s ", width, value);
 381                         else
 382                                 (void) printf("%s ", value);
 383                         return;
 384                 }
 385 
 386                 if (os->os_overflow > 0) {
 387                         compress = MIN(os->os_overflow, width - valwidth);
 388                         os->os_overflow -= compress;
 389                         width -= compress;
 390                 }
 391                 if (rightjust)
 392                         (void) printf("%*s ", width, value);
 393                 else
 394                         (void) printf("%-*s", width, value);
 395         }
 396 }
 397 
 398 /*
 399  * Print enough to fit the field width.
 400  */
 401 static void
 402 ofmt_fit_width(split_t **spp, uint_t width, char *value, uint_t bufsize)
 403 {
 404         split_t         *sp = *spp;
 405         char            *ptr = value, *lim = ptr + bufsize;
 406         int             i, nextlen;
 407 
 408         if (sp == NULL) {
 409                 sp = split_str(value, OFMT_MAX_ROWS);
 410                 if (sp == NULL)
 411                         return;
 412 
 413                 *spp = sp;
 414         }
 415         for (i = sp->s_currfield; i < sp->s_nfields; i++) {
 416                 ptr += snprintf(ptr, lim - ptr, "%s,", sp->s_fields[i]);
 417                 if (i + 1 == sp->s_nfields) {
 418                         nextlen = 0;
 419                         if (ptr > value)
 420                                 ptr[-1] = '\0';
 421                 } else {
 422                         nextlen = strlen(sp->s_fields[i + 1]);
 423                 }
 424 
 425                 if (strlen(value) + nextlen > width || ptr >= lim) {
 426                         i++;
 427                         break;
 428                 }
 429         }
 430         sp->s_currfield = i;
 431 }
 432 
 433 /*
 434  * Print one or more rows of output values for the selected columns.
 435  */
 436 void
 437 ofmt_print(ofmt_handle_t ofmt, void *arg)
 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
 478                                  * and stored into sp[i].
 479                                  */
 480                                 ofmt_fit_width(&sp[i], of[i].of_width, value,
 481                                     sizeof (value));
 482                                 if (sp[i] != NULL &&
 483                                     sp[i]->s_currfield < sp[i]->s_nfields)
 484                                         more_rows = B_TRUE;
 485                         }
 486 
 487                         ofmt_print_field(os, &of[i],
 488                             (*value == '\0' && !parsable) ?
 489                             OFMT_VAL_UNDEF : value, escsep);
 490                 } else {
 491                         ofmt_print_field(os, &of[i], OFMT_VAL_UNKNOWN, escsep);
 492                 }
 493         }
 494         (void) putchar('\n');
 495 
 496         while (more_rows) {
 497                 more_rows = B_FALSE;
 498                 for (i = 0; i < os->os_nfields; i++) {
 499                         os->os_lastfield = (i + 1 == os->os_nfields);
 500                         value[0] = '\0';
 501 
 502                         ofmt_fit_width(&sp[i], of[i].of_width,
 503                             value, sizeof (value));
 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 
 547         if (ioctl(1, TIOCGWINSZ, winsize) == -1 ||
 548             winsize->ws_col == 0 || winsize->ws_row == 0) {
 549                 winsize->ws_col = 80;
 550                 winsize->ws_row = 24;
 551         }
 552 }
 553 
 554 /*
 555  * Return error diagnostics using the information in the ofmt_handle_t
 556  */
 557 char *
 558 ofmt_strerror(ofmt_handle_t ofmt, ofmt_status_t error, char *buf,
 559     uint_t bufsize)
 560 {
 561         ofmt_state_t *os = ofmt;
 562         int i;
 563         const char *s;
 564         char ebuf[OFMT_BUFSIZE];
 565         boolean_t parsable;
 566 
 567         /*
 568          * ebuf is intended for optional error-specific data to be appended
 569          * after the internationalized error string for an error code.
 570          */
 571         ebuf[0] = '\0';
 572 
 573         switch (error) {
 574         case OFMT_SUCCESS:
 575                 s = "success";
 576                 break;
 577         case OFMT_EBADFIELDS:
 578                 /*
 579                  * Enumerate the singular/plural version of the warning
 580                  * and error to simplify and improve localization.
 581                  */
 582                 parsable = (os->os_flags & OFMT_PARSABLE);
 583                 if (!parsable) {
 584                         if (os->os_nbad > 1)
 585                                 s = "ignoring unknown output fields:";
 586                         else
 587                                 s = "ignoring unknown output field:";
 588                 } else {
 589                         if (os->os_nbad > 1)
 590                                 s = "unknown output fields:";
 591                         else
 592                                 s = "unknown output field:";
 593                 }
 594                 /* set up the bad fields in ebuf */
 595                 for (i = 0; i < os->os_nbad; i++) {
 596                         (void) strlcat(ebuf, " `", sizeof (ebuf));
 597                         (void) strlcat(ebuf, os->os_badfields[i],
 598                             sizeof (ebuf));
 599                         (void) strlcat(ebuf, "'", sizeof (ebuf));
 600                 }
 601                 break;
 602         case OFMT_ENOFIELDS:
 603                 s = "no valid output fields";
 604                 break;
 605         case OFMT_EPARSEMULTI:
 606                 s = "multiline mode incompatible with parsable mode";
 607                 break;
 608         case OFMT_EPARSEALL:
 609                 s = "output field `all' invalid in parsable mode";
 610                 break;
 611         case OFMT_EPARSENONE:
 612                 s = "output fields must be specified in parsable mode";
 613                 break;
 614         case OFMT_EPARSEWRAP:
 615                 s = "parsable mode is incompatible with wrap mode";
 616                 break;
 617         case OFMT_ENOTEMPLATE:
 618                 s = "no template provided for fields";
 619                 break;
 620         case OFMT_ENOMEM:
 621                 s = strerror(ENOMEM);
 622                 break;
 623         default:
 624                 (void) snprintf(buf, bufsize,
 625                     dgettext(TEXT_DOMAIN, "unknown ofmt error (%d)"),
 626                     error);
 627                 return (buf);
 628         }
 629         (void) snprintf(buf, bufsize, dgettext(TEXT_DOMAIN, s));
 630         (void) strlcat(buf, ebuf, bufsize);
 631         return (buf);
 632 }
 633 
 634 void
 635 ofmt_check(ofmt_status_t oferr, boolean_t parsable, ofmt_handle_t ofmt,
 636     void (*die)(const char *, ...), void (*warn)(const char *, ...))
 637 {
 638         char buf[OFMT_BUFSIZE];
 639 
 640         assert(die != NULL);
 641         assert(warn != NULL);
 642 
 643         if (oferr == OFMT_SUCCESS)
 644                 return;
 645 
 646         (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
 647 
 648         /*
 649          * All errors are considered fatal in parsable mode.  OFMT_ENOMEM and
 650          * OFMT_ENOFIELDS errors are always fatal, regardless of mode.  For
 651          * other errors, we print diagnostics in human-readable mode and
 652          * processs what we can.
 653          */
 654         if (parsable || oferr == OFMT_ENOFIELDS || oferr == OFMT_ENOMEM) {
 655                 ofmt_close(ofmt);
 656                 die(buf);
 657         } else {
 658                 warn(buf);
 659         }
 660 }