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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
33 #include <sys/termios.h>
35 #include <sys/sysmacros.h>
39 * functions and structures to internally process a comma-separated string
40 * of fields selected for output.
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 */
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
;
61 boolean_t os_lastfield
;
63 struct winsize os_winsize
;
68 int os_maxnamelen
; /* longest name (f. multiline) */
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 *,
90 * Split `str' into at most `maxfields' fields, Return a pointer to a
91 * split_t containing the split fields, or NULL on failure.
94 split_str(const char *str
, uint_t maxfields
)
96 char *field
, *token
, *lasts
= NULL
;
99 if (*str
== '\0' || maxfields
== 0)
102 sp
= calloc(sizeof (split_t
), 1);
106 sp
->s_buf
= strdup(str
);
107 sp
->s_fields
= malloc(sizeof (char *) * maxfields
);
108 if (sp
->s_buf
== NULL
|| sp
->s_fields
== NULL
)
112 while ((field
= strtok_r(token
, ",", &lasts
)) != NULL
) {
113 if (sp
->s_nfields
== maxfields
)
116 sp
->s_fields
[sp
->s_nfields
++] = field
;
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
131 split_fields(const ofmt_field_t
*template, uint_t maxfields
, uint_t maxcols
)
136 sp
= calloc(sizeof (split_t
), 1);
140 sp
->s_fields
= malloc(sizeof (char *) * maxfields
);
141 if (sp
->s_fields
== NULL
)
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
)
153 sp
->s_fields
[sp
->s_nfields
++] = template[i
].of_name
;
162 * Free the split_t structure pointed to by `sp'.
165 splitfree(split_t
*sp
)
175 * Open a handle to be used for printing formatted output.
178 ofmt_open(const char *str
, const ofmt_field_t
*template, uint_t flags
,
179 uint_t maxcols
, ofmt_handle_t
*ofmt
)
182 uint_t i
, j
, of_index
;
183 const ofmt_field_t
*ofp
;
185 ofmt_state_t
*os
= NULL
;
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
);
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
);
206 return (OFMT_EPARSEWRAP
);
208 if (template == NULL
)
209 return (OFMT_ENOTEMPLATE
);
210 for (ofp
= template; ofp
->of_name
!= NULL
; ofp
++)
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
);
219 if (parsable
|| (str
!= NULL
&& strcasecmp(str
, "all") == 0))
221 sp
= split_fields(template, nfields
, maxcols
);
226 os
= calloc(sizeof (ofmt_state_t
) +
227 sp
->s_nfields
* sizeof (ofmt_field_t
), 1);
231 os
->os_fields
= (ofmt_field_t
*)&os
[1];
232 os
->os_flags
= flags
;
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) {
248 int nbad
= os
->os_nbad
++;
250 error
= OFMT_EBADFIELDS
;
251 if (os
->os_badfields
== NULL
) {
252 os
->os_badfields
= malloc(sp
->s_nfields
*
254 if (os
->os_badfields
== NULL
)
257 os
->os_badfields
[nbad
] = strdup(sp
->s_fields
[i
]);
258 if (os
->os_badfields
[nbad
] == NULL
)
262 of
[of_index
].of_name
= strdup(template[j
].of_name
);
263 if (of
[of_index
].of_name
== NULL
)
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
;
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
);
291 * free resources associated with the ofmt_handle_t
294 ofmt_close(ofmt_handle_t ofmt
)
296 ofmt_state_t
*os
= ofmt
;
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
);
310 * Print the value for the selected field by calling the callback-function
311 * registered for the field.
314 ofmt_print_field(ofmt_state_t
*os
, ofmt_field_t
*ofp
, const char *value
,
317 uint_t width
= ofp
->of_width
;
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
);
326 * Parsable fields are separated by ':'. If such a field contains
327 * a ':' or '\', this character is prefixed by a '\'.
330 if (os
->os_nfields
== 1) {
331 (void) printf("%s", value
);
334 while ((c
= *value
++) != '\0') {
335 if (escsep
&& ((c
== ':' || c
== '\\')))
336 (void) putchar('\\');
339 if (!os
->os_lastfield
)
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');
349 if (os
->os_lastfield
) {
351 (void) printf("%*s", width
, value
);
353 (void) printf("%s", value
);
358 valwidth
= strlen(value
);
359 if (valwidth
+ os
->os_overflow
>= width
) {
360 os
->os_overflow
+= valwidth
- width
+ 1;
362 (void) printf("%*s ", width
, value
);
364 (void) printf("%s ", value
);
368 if (os
->os_overflow
> 0) {
369 compress
= MIN(os
->os_overflow
, width
- valwidth
);
370 os
->os_overflow
-= compress
;
374 (void) printf("%*s ", width
, value
);
376 (void) printf("%-*s", width
, value
);
381 * Print enough to fit the field width.
384 ofmt_fit_width(split_t
**spp
, uint_t width
, char *value
, uint_t bufsize
)
387 char *ptr
= value
, *lim
= ptr
+ bufsize
;
391 sp
= split_str(value
, OFMT_MAX_ROWS
);
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
) {
404 nextlen
= strlen(sp
->s_fields
[i
+ 1]);
407 if (strlen(value
) + nextlen
> width
|| ptr
>= lim
) {
416 * Print one or more rows of output values for the selected columns.
419 ofmt_print(ofmt_handle_t ofmt
, void *arg
)
421 ofmt_state_t
*os
= ofmt
;
425 boolean_t escsep
, more_rows
;
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
);
433 sp
= calloc(sizeof (split_t
*), os
->os_nfields
);
438 if ((os
->os_nrow
++ % os
->os_winsize
.ws_row
) == 0 &&
439 !parsable
&& !multiline
) {
440 ofmt_print_header(os
);
444 if (multiline
&& os
->os_nrow
> 1)
445 (void) putchar('\n');
448 escsep
= (os
->os_nfields
> 1);
450 for (i
= 0; i
< os
->os_nfields
; i
++) {
451 os
->os_lastfield
= (i
+ 1 == os
->os_nfields
);
453 ofarg
.ofmt_id
= of
[i
].of_id
;
454 ofarg
.ofmt_cbarg
= arg
;
456 if ((*of
[i
].of_cb
)(&ofarg
, value
, sizeof (value
))) {
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
,
465 sp
[i
]->s_currfield
< sp
[i
]->s_nfields
)
469 ofmt_print_field(os
, &of
[i
],
470 (*value
== '\0' && !parsable
) ?
471 OFMT_VAL_UNDEF
: value
, escsep
);
473 ofmt_print_field(os
, &of
[i
], OFMT_VAL_UNKNOWN
, escsep
);
476 (void) putchar('\n');
480 for (i
= 0; i
< os
->os_nfields
; i
++) {
481 os
->os_lastfield
= (i
+ 1 == os
->os_nfields
);
484 ofmt_fit_width(&sp
[i
], of
[i
].of_width
,
485 value
, sizeof (value
));
487 sp
[i
]->s_currfield
< sp
[i
]->s_nfields
)
490 ofmt_print_field(os
, &of
[i
], value
, escsep
);
492 (void) putchar('\n');
494 (void) fflush(stdout
);
497 for (i
= 0; i
< os
->os_nfields
; i
++)
504 * Print the field headers
507 ofmt_print_header(ofmt_state_t
*os
)
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.
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
540 ofmt_strerror(ofmt_handle_t ofmt
, ofmt_status_t error
, char *buf
,
543 ofmt_state_t
*os
= ofmt
;
546 char ebuf
[OFMT_BUFSIZE
];
550 * ebuf is intended for optional error-specific data to be appended
551 * after the internationalized error string for an error code.
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
);
567 s
= "ignoring unknown output fields:";
569 s
= "ignoring unknown output field:";
572 s
= "unknown output fields:";
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
],
581 (void) strlcat(ebuf
, "'", sizeof (ebuf
));
585 s
= "no valid output fields";
587 case OFMT_EPARSEMULTI
:
588 s
= "multiline mode incompatible with parsable mode";
591 s
= "output field `all' invalid in parsable mode";
593 case OFMT_EPARSENONE
:
594 s
= "output fields must be specified in parsable mode";
596 case OFMT_EPARSEWRAP
:
597 s
= "parsable mode is incompatible with wrap mode";
599 case OFMT_ENOTEMPLATE
:
600 s
= "no template provided for fields";
603 s
= strerror(ENOMEM
);
606 (void) snprintf(buf
, bufsize
,
607 dgettext(TEXT_DOMAIN
, "unknown ofmt error (%d)"),
611 (void) snprintf(buf
, bufsize
, dgettext(TEXT_DOMAIN
, s
));
612 (void) strlcat(buf
, ebuf
, bufsize
);