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 (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 uint_t 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 *
split_str(const char * str,uint_t maxfields)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 *
split_fields(const ofmt_field_t * template,uint_t maxfields,uint_t maxcols)141 split_fields(const ofmt_field_t *template, uint_t maxfields, uint_t maxcols)
142 {
143 split_t *sp;
144 uint_t 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
splitfree(split_t * sp)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
ofmt_open(const char * str,const ofmt_field_t * template,uint_t flags,uint_t maxcols,ofmt_handle_t * ofmt)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
ofmt_set_fs(ofmt_handle_t ofmt,char fs)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
ofmt_close(ofmt_handle_t ofmt)311 ofmt_close(ofmt_handle_t ofmt)
312 {
313 ofmt_state_t *os = ofmt;
314 uint_t 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
ofmt_print_field(ofmt_state_t * os,ofmt_field_t * ofp,const char * value,boolean_t escsep)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;
378 if (rightjust)
379 (void) printf("%*s ", width, value);
380 else
381 (void) printf("%s ", value);
382 return;
383 }
384
385 if (os->os_overflow > 0) {
386 compress = MIN(os->os_overflow, width - valwidth);
387 os->os_overflow -= compress;
388 width -= compress;
389 }
390 if (rightjust)
391 (void) printf("%*s ", width, value);
392 else
393 (void) printf("%-*s", width, value);
394 }
395 }
396
397 /*
398 * Print enough to fit the field width.
399 */
400 static void
ofmt_fit_width(split_t ** spp,uint_t width,char * value,uint_t bufsize)401 ofmt_fit_width(split_t **spp, uint_t width, char *value, uint_t bufsize)
402 {
403 split_t *sp = *spp;
404 char *ptr = value, *lim = ptr + bufsize;
405 uint_t i, nextlen;
406
407 if (sp == NULL) {
408 sp = split_str(value, OFMT_MAX_ROWS);
409 if (sp == NULL)
410 return;
411
412 *spp = sp;
413 }
414 for (i = sp->s_currfield; i < sp->s_nfields; i++) {
415 ptr += snprintf(ptr, lim - ptr, "%s,", sp->s_fields[i]);
416 if (i + 1 == sp->s_nfields) {
417 nextlen = 0;
418 if (ptr > value)
419 ptr[-1] = '\0';
420 } else {
421 nextlen = strlen(sp->s_fields[i + 1]);
422 }
423
424 if (strlen(value) + nextlen > width || ptr >= lim) {
425 i++;
426 break;
427 }
428 }
429 sp->s_currfield = i;
430 }
431
432 /*
433 * Print one or more rows of output values for the selected columns.
434 */
435 void
ofmt_print(ofmt_handle_t ofmt,void * arg)436 ofmt_print(ofmt_handle_t ofmt, void *arg)
437 {
438 ofmt_state_t *os = ofmt;
439 uint_t 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
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 void
ofmt_print_header(ofmt_handle_t ofmt)525 ofmt_print_header(ofmt_handle_t ofmt)
526 {
527 ofmt_state_t *os = ofmt;
528 uint_t 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
ofmt_update_winsize(ofmt_handle_t ofmt)543 ofmt_update_winsize(ofmt_handle_t ofmt)
544 {
545 ofmt_state_t *os = ofmt;
546 struct winsize *winsize = &os->os_winsize;
547
548 if (ioctl(1, TIOCGWINSZ, winsize) == -1 ||
549 winsize->ws_col == 0 || winsize->ws_row == 0) {
550 winsize->ws_col = 80;
551 winsize->ws_row = 24;
552 }
553 }
554
555 /*
556 * Return error diagnostics using the information in the ofmt_handle_t
557 */
558 char *
ofmt_strerror(ofmt_handle_t ofmt,ofmt_status_t error,char * buf,uint_t bufsize)559 ofmt_strerror(ofmt_handle_t ofmt, ofmt_status_t error, char *buf,
560 uint_t bufsize)
561 {
562 ofmt_state_t *os = ofmt;
563 uint_t i;
564 const char *s;
565 char ebuf[OFMT_BUFSIZE];
566 boolean_t parsable;
567
568 /*
569 * ebuf is intended for optional error-specific data to be appended
570 * after the internationalized error string for an error code.
571 */
572 ebuf[0] = '\0';
573
574 switch (error) {
575 case OFMT_SUCCESS:
576 s = "success";
577 break;
578 case OFMT_EBADFIELDS:
579 /*
580 * Enumerate the singular/plural version of the warning
581 * and error to simplify and improve localization.
582 */
583 parsable = (os->os_flags & OFMT_PARSABLE);
584 if (!parsable) {
585 if (os->os_nbad > 1)
586 s = "ignoring unknown output fields:";
587 else
588 s = "ignoring unknown output field:";
589 } else {
590 if (os->os_nbad > 1)
591 s = "unknown output fields:";
592 else
593 s = "unknown output field:";
594 }
595 /* set up the bad fields in ebuf */
596 for (i = 0; i < os->os_nbad; i++) {
597 (void) strlcat(ebuf, " `", sizeof (ebuf));
598 (void) strlcat(ebuf, os->os_badfields[i],
599 sizeof (ebuf));
600 (void) strlcat(ebuf, "'", sizeof (ebuf));
601 }
602 break;
603 case OFMT_ENOFIELDS:
604 s = "no valid output fields";
605 break;
606 case OFMT_EPARSEMULTI:
607 s = "multiline mode incompatible with parsable mode";
608 break;
609 case OFMT_EPARSEALL:
610 s = "output field `all' invalid in parsable mode";
611 break;
612 case OFMT_EPARSENONE:
613 s = "output fields must be specified in parsable mode";
614 break;
615 case OFMT_EPARSEWRAP:
616 s = "parsable mode is incompatible with wrap mode";
617 break;
618 case OFMT_ENOTEMPLATE:
619 s = "no template provided for fields";
620 break;
621 case OFMT_ENOMEM:
622 s = strerror(ENOMEM);
623 break;
624 default:
625 (void) snprintf(buf, bufsize,
626 dgettext(TEXT_DOMAIN, "unknown ofmt error (%d)"),
627 error);
628 return (buf);
629 }
630 (void) snprintf(buf, bufsize, dgettext(TEXT_DOMAIN, s));
631 (void) strlcat(buf, ebuf, bufsize);
632 return (buf);
633 }
634
635 void
ofmt_check(ofmt_status_t oferr,boolean_t parsable,ofmt_handle_t ofmt,void (* die)(const char *,...),void (* warn)(const char *,...))636 ofmt_check(ofmt_status_t oferr, boolean_t parsable, ofmt_handle_t ofmt,
637 void (*die)(const char *, ...), void (*warn)(const char *, ...))
638 {
639 char buf[OFMT_BUFSIZE];
640
641 assert(die != NULL);
642 assert(warn != NULL);
643
644 if (oferr == OFMT_SUCCESS)
645 return;
646
647 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
648
649 /*
650 * All errors are considered fatal in parsable mode. OFMT_ENOMEM and
651 * OFMT_ENOFIELDS errors are always fatal, regardless of mode. For
652 * other errors, we print diagnostics in human-readable mode and
653 * processs what we can.
654 */
655 if (parsable || oferr == OFMT_ENOFIELDS || oferr == OFMT_ENOMEM) {
656 ofmt_close(ofmt);
657 die(buf);
658 } else {
659 warn(buf);
660 }
661 }
662