dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libofmt / common / ofmt.c
blobbab3da73117020894fbe990817cb5bef4e0a9907
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <stdio.h>
32 #include <ofmt.h>
33 #include <sys/termios.h>
34 #include <unistd.h>
35 #include <sys/sysmacros.h>
36 #include <libintl.h>
39 * functions and structures to internally process a comma-separated string
40 * of fields selected for output.
42 typedef struct {
43 char *s_buf;
44 const char **s_fields; /* array of pointers to the fields in s_buf */
45 uint_t s_nfields; /* the number of fields in s_buf */
46 uint_t s_currfield; /* the current field being processed */
47 } split_t;
48 static void splitfree(split_t *);
49 static split_t *split_str(const char *, uint_t);
50 static split_t *split_fields(const ofmt_field_t *, uint_t, uint_t);
53 * The state of the output is tracked in a ofmt_state_t structure.
54 * Each os_fields[i] entry points at an ofmt_field_t array for
55 * the sub-command whose contents are provided by the caller, with
56 * os_nfields set to the number of requested fields.
58 typedef struct ofmt_state_s {
59 ofmt_field_t *os_fields;
60 uint_t os_nfields;
61 boolean_t os_lastfield;
62 uint_t os_overflow;
63 struct winsize os_winsize;
64 int os_nrow;
65 uint_t os_flags;
66 int os_nbad;
67 char **os_badfields;
68 int os_maxnamelen; /* longest name (f. multiline) */
69 } ofmt_state_t;
71 * A B_TRUE return value from the callback function will print out the contents
72 * of the output buffer, except when the buffer is returned with the empty
73 * string "", in which case the OFMT_VAL_UNDEF will be printed.
75 * If the callback function returns B_FALSE, the "?" string will be emitted.
77 #define OFMT_VAL_UNDEF "--"
78 #define OFMT_VAL_UNKNOWN "?"
81 * The maximum number of rows supported by the OFMT_WRAP option.
83 #define OFMT_MAX_ROWS 128
85 static void ofmt_print_header(ofmt_state_t *);
86 static void ofmt_print_field(ofmt_state_t *, ofmt_field_t *, const char *,
87 boolean_t);
90 * Split `str' into at most `maxfields' fields, Return a pointer to a
91 * split_t containing the split fields, or NULL on failure.
93 static split_t *
94 split_str(const char *str, uint_t maxfields)
96 char *field, *token, *lasts = NULL;
97 split_t *sp;
99 if (*str == '\0' || maxfields == 0)
100 return (NULL);
102 sp = calloc(sizeof (split_t), 1);
103 if (sp == NULL)
104 return (NULL);
106 sp->s_buf = strdup(str);
107 sp->s_fields = malloc(sizeof (char *) * maxfields);
108 if (sp->s_buf == NULL || sp->s_fields == NULL)
109 goto fail;
111 token = sp->s_buf;
112 while ((field = strtok_r(token, ",", &lasts)) != NULL) {
113 if (sp->s_nfields == maxfields)
114 goto fail;
115 token = NULL;
116 sp->s_fields[sp->s_nfields++] = field;
118 return (sp);
119 fail:
120 splitfree(sp);
121 return (NULL);
125 * Split `fields' into at most `maxfields' fields. Return a pointer to
126 * a split_t containing the split fields, or NULL on failure. Invoked
127 * when all fields are implicitly selected at handle creation by
128 * passing in a NULL fields_str
130 static split_t *
131 split_fields(const ofmt_field_t *template, uint_t maxfields, uint_t maxcols)
133 split_t *sp;
134 int i, cols;
136 sp = calloc(sizeof (split_t), 1);
137 if (sp == NULL)
138 return (NULL);
140 sp->s_fields = malloc(sizeof (char *) * maxfields);
141 if (sp->s_fields == NULL)
142 goto fail;
143 cols = 0;
144 for (i = 0; i < maxfields; i++) {
145 cols += template[i].of_width;
147 * If all fields are implied without explicitly passing
148 * in a fields_str, build a list of field names, stopping
149 * when we run out of columns.
151 if (maxcols > 0 && cols > maxcols)
152 break;
153 sp->s_fields[sp->s_nfields++] = template[i].of_name;
155 return (sp);
156 fail:
157 splitfree(sp);
158 return (NULL);
162 * Free the split_t structure pointed to by `sp'.
164 static void
165 splitfree(split_t *sp)
167 if (sp == NULL)
168 return;
169 free(sp->s_buf);
170 free(sp->s_fields);
171 free(sp);
175 * Open a handle to be used for printing formatted output.
177 ofmt_status_t
178 ofmt_open(const char *str, const ofmt_field_t *template, uint_t flags,
179 uint_t maxcols, ofmt_handle_t *ofmt)
181 split_t *sp;
182 uint_t i, j, of_index;
183 const ofmt_field_t *ofp;
184 ofmt_field_t *of;
185 ofmt_state_t *os = NULL;
186 int nfields = 0;
187 ofmt_status_t error = OFMT_SUCCESS;
188 boolean_t parsable = (flags & OFMT_PARSABLE);
189 boolean_t wrap = (flags & OFMT_WRAP);
190 boolean_t multiline = (flags & OFMT_MULTILINE);
192 *ofmt = NULL;
193 if (parsable) {
194 if (multiline)
195 return (OFMT_EPARSEMULTI);
197 * For parsable output mode, the caller always needs
198 * to specify precisely which fields are to be selected,
199 * since the set of fields may change over time.
201 if (str == NULL || str[0] == '\0')
202 return (OFMT_EPARSENONE);
203 if (strcasecmp(str, "all") == 0)
204 return (OFMT_EPARSEALL);
205 if (wrap)
206 return (OFMT_EPARSEWRAP);
208 if (template == NULL)
209 return (OFMT_ENOTEMPLATE);
210 for (ofp = template; ofp->of_name != NULL; ofp++)
211 nfields++;
213 * split str into the columns selected, or construct the
214 * full set of columns (equivalent to -o all).
216 if (str != NULL && strcasecmp(str, "all") != 0) {
217 sp = split_str(str, nfields);
218 } else {
219 if (parsable || (str != NULL && strcasecmp(str, "all") == 0))
220 maxcols = 0;
221 sp = split_fields(template, nfields, maxcols);
223 if (sp == NULL)
224 goto nomem;
226 os = calloc(sizeof (ofmt_state_t) +
227 sp->s_nfields * sizeof (ofmt_field_t), 1);
228 if (os == NULL)
229 goto nomem;
230 *ofmt = os;
231 os->os_fields = (ofmt_field_t *)&os[1];
232 os->os_flags = flags;
234 of = os->os_fields;
235 of_index = 0;
237 * sp->s_nfields is the number of fields requested in fields_str.
238 * nfields is the number of fields in template.
240 for (i = 0; i < sp->s_nfields; i++) {
241 for (j = 0; j < nfields; j++) {
242 if (strcasecmp(sp->s_fields[i],
243 template[j].of_name) == 0) {
244 break;
247 if (j == nfields) {
248 int nbad = os->os_nbad++;
250 error = OFMT_EBADFIELDS;
251 if (os->os_badfields == NULL) {
252 os->os_badfields = malloc(sp->s_nfields *
253 sizeof (char *));
254 if (os->os_badfields == NULL)
255 goto nomem;
257 os->os_badfields[nbad] = strdup(sp->s_fields[i]);
258 if (os->os_badfields[nbad] == NULL)
259 goto nomem;
260 continue;
262 of[of_index].of_name = strdup(template[j].of_name);
263 if (of[of_index].of_name == NULL)
264 goto nomem;
265 if (multiline) {
266 int n = strlen(of[of_index].of_name);
268 os->os_maxnamelen = MAX(n, os->os_maxnamelen);
270 of[of_index].of_width = template[j].of_width;
271 of[of_index].of_id = template[j].of_id;
272 of[of_index].of_cb = template[j].of_cb;
273 of_index++;
275 splitfree(sp);
276 if (of_index == 0) /* all values in str are bogus */
277 return (OFMT_ENOFIELDS);
278 os->os_nfields = of_index; /* actual number of fields printed */
279 ofmt_update_winsize(*ofmt);
280 return (error);
281 nomem:
282 error = OFMT_ENOMEM;
283 if (os != NULL)
284 ofmt_close(os);
285 *ofmt = NULL;
286 splitfree(sp);
287 return (error);
291 * free resources associated with the ofmt_handle_t
293 void
294 ofmt_close(ofmt_handle_t ofmt)
296 ofmt_state_t *os = ofmt;
297 int i;
299 if (os == NULL)
300 return;
301 for (i = 0; i < os->os_nfields; i++)
302 free(os->os_fields[i].of_name);
303 for (i = 0; i < os->os_nbad; i++)
304 free(os->os_badfields[i]);
305 free(os->os_badfields);
306 free(os);
310 * Print the value for the selected field by calling the callback-function
311 * registered for the field.
313 static void
314 ofmt_print_field(ofmt_state_t *os, ofmt_field_t *ofp, const char *value,
315 boolean_t escsep)
317 uint_t width = ofp->of_width;
318 uint_t valwidth;
319 uint_t compress;
320 boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
321 boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
322 boolean_t rightjust = (os->os_flags & OFMT_RIGHTJUST);
323 char c;
326 * Parsable fields are separated by ':'. If such a field contains
327 * a ':' or '\', this character is prefixed by a '\'.
329 if (parsable) {
330 if (os->os_nfields == 1) {
331 (void) printf("%s", value);
332 return;
334 while ((c = *value++) != '\0') {
335 if (escsep && ((c == ':' || c == '\\')))
336 (void) putchar('\\');
337 (void) putchar(c);
339 if (!os->os_lastfield)
340 (void) putchar(':');
341 } else if (multiline) {
342 if (value[0] == '\0')
343 value = OFMT_VAL_UNDEF;
344 (void) printf("%*.*s: %s", os->os_maxnamelen,
345 os->os_maxnamelen, ofp->of_name, value);
346 if (!os->os_lastfield)
347 (void) putchar('\n');
348 } else {
349 if (os->os_lastfield) {
350 if (rightjust)
351 (void) printf("%*s", width, value);
352 else
353 (void) printf("%s", value);
354 os->os_overflow = 0;
355 return;
358 valwidth = strlen(value);
359 if (valwidth + os->os_overflow >= width) {
360 os->os_overflow += valwidth - width + 1;
361 if (rightjust)
362 (void) printf("%*s ", width, value);
363 else
364 (void) printf("%s ", value);
365 return;
368 if (os->os_overflow > 0) {
369 compress = MIN(os->os_overflow, width - valwidth);
370 os->os_overflow -= compress;
371 width -= compress;
373 if (rightjust)
374 (void) printf("%*s ", width, value);
375 else
376 (void) printf("%-*s", width, value);
381 * Print enough to fit the field width.
383 static void
384 ofmt_fit_width(split_t **spp, uint_t width, char *value, uint_t bufsize)
386 split_t *sp = *spp;
387 char *ptr = value, *lim = ptr + bufsize;
388 int i, nextlen;
390 if (sp == NULL) {
391 sp = split_str(value, OFMT_MAX_ROWS);
392 if (sp == NULL)
393 return;
395 *spp = sp;
397 for (i = sp->s_currfield; i < sp->s_nfields; i++) {
398 ptr += snprintf(ptr, lim - ptr, "%s,", sp->s_fields[i]);
399 if (i + 1 == sp->s_nfields) {
400 nextlen = 0;
401 if (ptr > value)
402 ptr[-1] = '\0';
403 } else {
404 nextlen = strlen(sp->s_fields[i + 1]);
407 if (strlen(value) + nextlen > width || ptr >= lim) {
408 i++;
409 break;
412 sp->s_currfield = i;
416 * Print one or more rows of output values for the selected columns.
418 void
419 ofmt_print(ofmt_handle_t ofmt, void *arg)
421 ofmt_state_t *os = ofmt;
422 int i;
423 char value[1024];
424 ofmt_field_t *of;
425 boolean_t escsep, more_rows;
426 ofmt_arg_t ofarg;
427 split_t **sp = NULL;
428 boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
429 boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
430 boolean_t wrap = (os->os_flags & OFMT_WRAP);
432 if (wrap) {
433 sp = calloc(sizeof (split_t *), os->os_nfields);
434 if (sp == NULL)
435 return;
438 if ((os->os_nrow++ % os->os_winsize.ws_row) == 0 &&
439 !parsable && !multiline) {
440 ofmt_print_header(os);
441 os->os_nrow++;
444 if (multiline && os->os_nrow > 1)
445 (void) putchar('\n');
447 of = os->os_fields;
448 escsep = (os->os_nfields > 1);
449 more_rows = B_FALSE;
450 for (i = 0; i < os->os_nfields; i++) {
451 os->os_lastfield = (i + 1 == os->os_nfields);
452 value[0] = '\0';
453 ofarg.ofmt_id = of[i].of_id;
454 ofarg.ofmt_cbarg = arg;
456 if ((*of[i].of_cb)(&ofarg, value, sizeof (value))) {
457 if (wrap) {
459 * 'value' will be split at comma boundaries
460 * and stored into sp[i].
462 ofmt_fit_width(&sp[i], of[i].of_width, value,
463 sizeof (value));
464 if (sp[i] != NULL &&
465 sp[i]->s_currfield < sp[i]->s_nfields)
466 more_rows = B_TRUE;
469 ofmt_print_field(os, &of[i],
470 (*value == '\0' && !parsable) ?
471 OFMT_VAL_UNDEF : value, escsep);
472 } else {
473 ofmt_print_field(os, &of[i], OFMT_VAL_UNKNOWN, escsep);
476 (void) putchar('\n');
478 while (more_rows) {
479 more_rows = B_FALSE;
480 for (i = 0; i < os->os_nfields; i++) {
481 os->os_lastfield = (i + 1 == os->os_nfields);
482 value[0] = '\0';
484 ofmt_fit_width(&sp[i], of[i].of_width,
485 value, sizeof (value));
486 if (sp[i] != NULL &&
487 sp[i]->s_currfield < sp[i]->s_nfields)
488 more_rows = B_TRUE;
490 ofmt_print_field(os, &of[i], value, escsep);
492 (void) putchar('\n');
494 (void) fflush(stdout);
496 if (sp != NULL) {
497 for (i = 0; i < os->os_nfields; i++)
498 splitfree(sp[i]);
499 free(sp);
504 * Print the field headers
506 static void
507 ofmt_print_header(ofmt_state_t *os)
509 int i;
510 ofmt_field_t *of = os->os_fields;
511 boolean_t escsep = (os->os_nfields > 1);
513 for (i = 0; i < os->os_nfields; i++) {
514 os->os_lastfield = (i + 1 == os->os_nfields);
515 ofmt_print_field(os, &of[i], of[i].of_name, escsep);
517 (void) putchar('\n');
521 * Update the current window size.
523 void
524 ofmt_update_winsize(ofmt_handle_t ofmt)
526 ofmt_state_t *os = ofmt;
527 struct winsize *winsize = &os->os_winsize;
529 if (ioctl(1, TIOCGWINSZ, winsize) == -1 ||
530 winsize->ws_col == 0 || winsize->ws_row == 0) {
531 winsize->ws_col = 80;
532 winsize->ws_row = 24;
537 * Return error diagnostics using the information in the ofmt_handle_t
539 char *
540 ofmt_strerror(ofmt_handle_t ofmt, ofmt_status_t error, char *buf,
541 uint_t bufsize)
543 ofmt_state_t *os = ofmt;
544 int i;
545 const char *s;
546 char ebuf[OFMT_BUFSIZE];
547 boolean_t parsable;
550 * ebuf is intended for optional error-specific data to be appended
551 * after the internationalized error string for an error code.
553 ebuf[0] = '\0';
555 switch (error) {
556 case OFMT_SUCCESS:
557 s = "success";
558 break;
559 case OFMT_EBADFIELDS:
561 * Enumerate the singular/plural version of the warning
562 * and error to simplify and improve localization.
564 parsable = (os->os_flags & OFMT_PARSABLE);
565 if (!parsable) {
566 if (os->os_nbad > 1)
567 s = "ignoring unknown output fields:";
568 else
569 s = "ignoring unknown output field:";
570 } else {
571 if (os->os_nbad > 1)
572 s = "unknown output fields:";
573 else
574 s = "unknown output field:";
576 /* set up the bad fields in ebuf */
577 for (i = 0; i < os->os_nbad; i++) {
578 (void) strlcat(ebuf, " `", sizeof (ebuf));
579 (void) strlcat(ebuf, os->os_badfields[i],
580 sizeof (ebuf));
581 (void) strlcat(ebuf, "'", sizeof (ebuf));
583 break;
584 case OFMT_ENOFIELDS:
585 s = "no valid output fields";
586 break;
587 case OFMT_EPARSEMULTI:
588 s = "multiline mode incompatible with parsable mode";
589 break;
590 case OFMT_EPARSEALL:
591 s = "output field `all' invalid in parsable mode";
592 break;
593 case OFMT_EPARSENONE:
594 s = "output fields must be specified in parsable mode";
595 break;
596 case OFMT_EPARSEWRAP:
597 s = "parsable mode is incompatible with wrap mode";
598 break;
599 case OFMT_ENOTEMPLATE:
600 s = "no template provided for fields";
601 break;
602 case OFMT_ENOMEM:
603 s = strerror(ENOMEM);
604 break;
605 default:
606 (void) snprintf(buf, bufsize,
607 dgettext(TEXT_DOMAIN, "unknown ofmt error (%d)"),
608 error);
609 return (buf);
611 (void) snprintf(buf, bufsize, dgettext(TEXT_DOMAIN, s));
612 (void) strlcat(buf, ebuf, bufsize);
613 return (buf);