1e96f8419SYuri Pankov /*
2e96f8419SYuri Pankov * CDDL HEADER START
3e96f8419SYuri Pankov *
4e96f8419SYuri Pankov * The contents of this file are subject to the terms of the
5e96f8419SYuri Pankov * Common Development and Distribution License (the "License").
6e96f8419SYuri Pankov * You may not use this file except in compliance with the License.
7e96f8419SYuri Pankov *
8e96f8419SYuri Pankov * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9e96f8419SYuri Pankov * or http://www.opensolaris.org/os/licensing.
10e96f8419SYuri Pankov * See the License for the specific language governing permissions
11e96f8419SYuri Pankov * and limitations under the License.
12e96f8419SYuri Pankov *
13e96f8419SYuri Pankov * When distributing Covered Code, include this CDDL HEADER in each
14e96f8419SYuri Pankov * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15e96f8419SYuri Pankov * If applicable, add the following below this CDDL HEADER, with the
16e96f8419SYuri Pankov * fields enclosed by brackets "[]" replaced with your own identifying
17e96f8419SYuri Pankov * information: Portions Copyright [yyyy] [name of copyright owner]
18e96f8419SYuri Pankov *
19e96f8419SYuri Pankov * CDDL HEADER END
20e96f8419SYuri Pankov */
21e96f8419SYuri Pankov
22e96f8419SYuri Pankov /*
23e96f8419SYuri Pankov * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24e96f8419SYuri Pankov * Use is subject to license terms.
25e96f8419SYuri Pankov */
26b2f26520SBryan Cantrill
27b2f26520SBryan Cantrill /*
28519cca71SSebastien Roy * Copyright (c) 2015 by Delphix. All rights reserved.
29b2f26520SBryan Cantrill * Copyright 2017 Joyent, Inc.
30b2f26520SBryan Cantrill */
31b2f26520SBryan Cantrill
32e96f8419SYuri Pankov #include <errno.h>
33e96f8419SYuri Pankov #include <sys/types.h>
34e96f8419SYuri Pankov #include <stdlib.h>
35e96f8419SYuri Pankov #include <string.h>
36e96f8419SYuri Pankov #include <strings.h>
37e96f8419SYuri Pankov #include <stdio.h>
38e96f8419SYuri Pankov #include <ofmt.h>
39e96f8419SYuri Pankov #include <sys/termios.h>
40e96f8419SYuri Pankov #include <unistd.h>
41e96f8419SYuri Pankov #include <sys/sysmacros.h>
42e96f8419SYuri Pankov #include <libintl.h>
43b2f26520SBryan Cantrill #include <assert.h>
44e96f8419SYuri Pankov
45e96f8419SYuri Pankov /*
46e96f8419SYuri Pankov * functions and structures to internally process a comma-separated string
47e96f8419SYuri Pankov * of fields selected for output.
48e96f8419SYuri Pankov */
49e96f8419SYuri Pankov typedef struct {
50e96f8419SYuri Pankov char *s_buf;
51e96f8419SYuri Pankov const char **s_fields; /* array of pointers to the fields in s_buf */
52e96f8419SYuri Pankov uint_t s_nfields; /* the number of fields in s_buf */
53e96f8419SYuri Pankov uint_t s_currfield; /* the current field being processed */
54e96f8419SYuri Pankov } split_t;
55b2f26520SBryan Cantrill
56e96f8419SYuri Pankov static void splitfree(split_t *);
57519cca71SSebastien Roy static split_t *split_str(const char *, uint_t);
58519cca71SSebastien Roy static split_t *split_fields(const ofmt_field_t *, uint_t, uint_t);
59e96f8419SYuri Pankov
60e96f8419SYuri Pankov /*
61e96f8419SYuri Pankov * The state of the output is tracked in a ofmt_state_t structure.
62e96f8419SYuri Pankov * Each os_fields[i] entry points at an ofmt_field_t array for
63e96f8419SYuri Pankov * the sub-command whose contents are provided by the caller, with
64e96f8419SYuri Pankov * os_nfields set to the number of requested fields.
65e96f8419SYuri Pankov */
66e96f8419SYuri Pankov typedef struct ofmt_state_s {
67e96f8419SYuri Pankov ofmt_field_t *os_fields;
68e96f8419SYuri Pankov uint_t os_nfields;
69e96f8419SYuri Pankov boolean_t os_lastfield;
70e96f8419SYuri Pankov uint_t os_overflow;
71e96f8419SYuri Pankov struct winsize os_winsize;
72e96f8419SYuri Pankov int os_nrow;
73e96f8419SYuri Pankov uint_t os_flags;
74*6fc1cd8dSToomas Soome uint_t os_nbad;
75e96f8419SYuri Pankov char **os_badfields;
76e96f8419SYuri Pankov int os_maxnamelen; /* longest name (f. multiline) */
77519cca71SSebastien Roy char os_fs; /* field seperator */
78e96f8419SYuri Pankov } ofmt_state_t;
79e96f8419SYuri Pankov /*
80e96f8419SYuri Pankov * A B_TRUE return value from the callback function will print out the contents
81e96f8419SYuri Pankov * of the output buffer, except when the buffer is returned with the empty
82e96f8419SYuri Pankov * string "", in which case the OFMT_VAL_UNDEF will be printed.
83e96f8419SYuri Pankov *
84e96f8419SYuri Pankov * If the callback function returns B_FALSE, the "?" string will be emitted.
85e96f8419SYuri Pankov */
86e96f8419SYuri Pankov #define OFMT_VAL_UNDEF "--"
87e96f8419SYuri Pankov #define OFMT_VAL_UNKNOWN "?"
88e96f8419SYuri Pankov
89519cca71SSebastien Roy #define OFMT_DEFAULT_FS ':'
90519cca71SSebastien Roy
91e96f8419SYuri Pankov /*
92e96f8419SYuri Pankov * The maximum number of rows supported by the OFMT_WRAP option.
93e96f8419SYuri Pankov */
94e96f8419SYuri Pankov #define OFMT_MAX_ROWS 128
95e96f8419SYuri Pankov
96e96f8419SYuri Pankov static void ofmt_print_field(ofmt_state_t *, ofmt_field_t *, const char *,
97e96f8419SYuri Pankov boolean_t);
98e96f8419SYuri Pankov
99e96f8419SYuri Pankov /*
100e96f8419SYuri Pankov * Split `str' into at most `maxfields' fields, Return a pointer to a
101e96f8419SYuri Pankov * split_t containing the split fields, or NULL on failure.
102e96f8419SYuri Pankov */
103e96f8419SYuri Pankov static split_t *
split_str(const char * str,uint_t maxfields)104e96f8419SYuri Pankov split_str(const char *str, uint_t maxfields)
105e96f8419SYuri Pankov {
106e96f8419SYuri Pankov char *field, *token, *lasts = NULL;
107e96f8419SYuri Pankov split_t *sp;
108e96f8419SYuri Pankov
109e96f8419SYuri Pankov if (*str == '\0' || maxfields == 0)
110e96f8419SYuri Pankov return (NULL);
111e96f8419SYuri Pankov
112e96f8419SYuri Pankov sp = calloc(sizeof (split_t), 1);
113e96f8419SYuri Pankov if (sp == NULL)
114e96f8419SYuri Pankov return (NULL);
115e96f8419SYuri Pankov
116e96f8419SYuri Pankov sp->s_buf = strdup(str);
117e96f8419SYuri Pankov sp->s_fields = malloc(sizeof (char *) * maxfields);
118e96f8419SYuri Pankov if (sp->s_buf == NULL || sp->s_fields == NULL)
119e96f8419SYuri Pankov goto fail;
120e96f8419SYuri Pankov
121e96f8419SYuri Pankov token = sp->s_buf;
122e96f8419SYuri Pankov while ((field = strtok_r(token, ",", &lasts)) != NULL) {
123e96f8419SYuri Pankov if (sp->s_nfields == maxfields)
124e96f8419SYuri Pankov goto fail;
125e96f8419SYuri Pankov token = NULL;
126e96f8419SYuri Pankov sp->s_fields[sp->s_nfields++] = field;
127e96f8419SYuri Pankov }
128e96f8419SYuri Pankov return (sp);
129e96f8419SYuri Pankov fail:
130e96f8419SYuri Pankov splitfree(sp);
131e96f8419SYuri Pankov return (NULL);
132e96f8419SYuri Pankov }
133e96f8419SYuri Pankov
134e96f8419SYuri Pankov /*
135519cca71SSebastien Roy * Split `fields' into at most `maxfields' fields. Return a pointer to
136519cca71SSebastien Roy * a split_t containing the split fields, or NULL on failure. Invoked
137519cca71SSebastien Roy * when all fields are implicitly selected at handle creation by
138519cca71SSebastien Roy * passing in a NULL fields_str
139e96f8419SYuri Pankov */
140e96f8419SYuri Pankov static split_t *
split_fields(const ofmt_field_t * template,uint_t maxfields,uint_t maxcols)141519cca71SSebastien Roy split_fields(const ofmt_field_t *template, uint_t maxfields, uint_t maxcols)
142e96f8419SYuri Pankov {
143e96f8419SYuri Pankov split_t *sp;
144*6fc1cd8dSToomas Soome uint_t i, cols;
145e96f8419SYuri Pankov
146e96f8419SYuri Pankov sp = calloc(sizeof (split_t), 1);
147e96f8419SYuri Pankov if (sp == NULL)
148e96f8419SYuri Pankov return (NULL);
149e96f8419SYuri Pankov
150519cca71SSebastien Roy sp->s_fields = malloc(sizeof (char *) * maxfields);
151e96f8419SYuri Pankov if (sp->s_fields == NULL)
152e96f8419SYuri Pankov goto fail;
153e96f8419SYuri Pankov cols = 0;
154519cca71SSebastien Roy for (i = 0; i < maxfields; i++) {
155e96f8419SYuri Pankov cols += template[i].of_width;
156e96f8419SYuri Pankov /*
157e96f8419SYuri Pankov * If all fields are implied without explicitly passing
158e96f8419SYuri Pankov * in a fields_str, build a list of field names, stopping
159e96f8419SYuri Pankov * when we run out of columns.
160e96f8419SYuri Pankov */
161e96f8419SYuri Pankov if (maxcols > 0 && cols > maxcols)
162e96f8419SYuri Pankov break;
163e96f8419SYuri Pankov sp->s_fields[sp->s_nfields++] = template[i].of_name;
164e96f8419SYuri Pankov }
165e96f8419SYuri Pankov return (sp);
166e96f8419SYuri Pankov fail:
167e96f8419SYuri Pankov splitfree(sp);
168e96f8419SYuri Pankov return (NULL);
169e96f8419SYuri Pankov }
170e96f8419SYuri Pankov
171e96f8419SYuri Pankov /*
172e96f8419SYuri Pankov * Free the split_t structure pointed to by `sp'.
173e96f8419SYuri Pankov */
174e96f8419SYuri Pankov static void
splitfree(split_t * sp)175e96f8419SYuri Pankov splitfree(split_t *sp)
176e96f8419SYuri Pankov {
177e96f8419SYuri Pankov if (sp == NULL)
178e96f8419SYuri Pankov return;
179e96f8419SYuri Pankov free(sp->s_buf);
180e96f8419SYuri Pankov free(sp->s_fields);
181e96f8419SYuri Pankov free(sp);
182e96f8419SYuri Pankov }
183e96f8419SYuri Pankov
184e96f8419SYuri Pankov /*
185e96f8419SYuri Pankov * Open a handle to be used for printing formatted output.
186e96f8419SYuri Pankov */
187e96f8419SYuri Pankov ofmt_status_t
ofmt_open(const char * str,const ofmt_field_t * template,uint_t flags,uint_t maxcols,ofmt_handle_t * ofmt)188e96f8419SYuri Pankov ofmt_open(const char *str, const ofmt_field_t *template, uint_t flags,
189e96f8419SYuri Pankov uint_t maxcols, ofmt_handle_t *ofmt)
190e96f8419SYuri Pankov {
191e96f8419SYuri Pankov split_t *sp;
192519cca71SSebastien Roy uint_t i, j, of_index;
193e96f8419SYuri Pankov const ofmt_field_t *ofp;
194e96f8419SYuri Pankov ofmt_field_t *of;
195e96f8419SYuri Pankov ofmt_state_t *os = NULL;
196519cca71SSebastien Roy uint_t nfields = 0;
197e96f8419SYuri Pankov ofmt_status_t error = OFMT_SUCCESS;
198e96f8419SYuri Pankov boolean_t parsable = (flags & OFMT_PARSABLE);
199e96f8419SYuri Pankov boolean_t wrap = (flags & OFMT_WRAP);
200e96f8419SYuri Pankov boolean_t multiline = (flags & OFMT_MULTILINE);
201e96f8419SYuri Pankov
202e96f8419SYuri Pankov *ofmt = NULL;
203e96f8419SYuri Pankov if (parsable) {
204e96f8419SYuri Pankov if (multiline)
205e96f8419SYuri Pankov return (OFMT_EPARSEMULTI);
206e96f8419SYuri Pankov /*
207e96f8419SYuri Pankov * For parsable output mode, the caller always needs
208e96f8419SYuri Pankov * to specify precisely which fields are to be selected,
209e96f8419SYuri Pankov * since the set of fields may change over time.
210e96f8419SYuri Pankov */
211e96f8419SYuri Pankov if (str == NULL || str[0] == '\0')
212e96f8419SYuri Pankov return (OFMT_EPARSENONE);
213e96f8419SYuri Pankov if (strcasecmp(str, "all") == 0)
214e96f8419SYuri Pankov return (OFMT_EPARSEALL);
215e96f8419SYuri Pankov if (wrap)
216e96f8419SYuri Pankov return (OFMT_EPARSEWRAP);
217e96f8419SYuri Pankov }
218e96f8419SYuri Pankov if (template == NULL)
219e96f8419SYuri Pankov return (OFMT_ENOTEMPLATE);
220519cca71SSebastien Roy for (ofp = template; ofp->of_name != NULL; ofp++)
221519cca71SSebastien Roy nfields++;
222e96f8419SYuri Pankov /*
223e96f8419SYuri Pankov * split str into the columns selected, or construct the
224e96f8419SYuri Pankov * full set of columns (equivalent to -o all).
225e96f8419SYuri Pankov */
226e96f8419SYuri Pankov if (str != NULL && strcasecmp(str, "all") != 0) {
227e96f8419SYuri Pankov sp = split_str(str, nfields);
228e96f8419SYuri Pankov } else {
229e96f8419SYuri Pankov if (parsable || (str != NULL && strcasecmp(str, "all") == 0))
230e96f8419SYuri Pankov maxcols = 0;
231519cca71SSebastien Roy sp = split_fields(template, nfields, maxcols);
232e96f8419SYuri Pankov }
233e96f8419SYuri Pankov if (sp == NULL)
234e96f8419SYuri Pankov goto nomem;
235e96f8419SYuri Pankov
236e96f8419SYuri Pankov os = calloc(sizeof (ofmt_state_t) +
237e96f8419SYuri Pankov sp->s_nfields * sizeof (ofmt_field_t), 1);
238e96f8419SYuri Pankov if (os == NULL)
239e96f8419SYuri Pankov goto nomem;
240e96f8419SYuri Pankov *ofmt = os;
241e96f8419SYuri Pankov os->os_fields = (ofmt_field_t *)&os[1];
242e96f8419SYuri Pankov os->os_flags = flags;
243519cca71SSebastien Roy os->os_fs = OFMT_DEFAULT_FS;
244e96f8419SYuri Pankov
245e96f8419SYuri Pankov of = os->os_fields;
246e96f8419SYuri Pankov of_index = 0;
247e96f8419SYuri Pankov /*
248e96f8419SYuri Pankov * sp->s_nfields is the number of fields requested in fields_str.
249e96f8419SYuri Pankov * nfields is the number of fields in template.
250e96f8419SYuri Pankov */
251e96f8419SYuri Pankov for (i = 0; i < sp->s_nfields; i++) {
252519cca71SSebastien Roy for (j = 0; j < nfields; j++) {
253519cca71SSebastien Roy if (strcasecmp(sp->s_fields[i],
254519cca71SSebastien Roy template[j].of_name) == 0) {
255e96f8419SYuri Pankov break;
256e96f8419SYuri Pankov }
257519cca71SSebastien Roy }
258519cca71SSebastien Roy if (j == nfields) {
259e96f8419SYuri Pankov int nbad = os->os_nbad++;
260e96f8419SYuri Pankov
261e96f8419SYuri Pankov error = OFMT_EBADFIELDS;
262e96f8419SYuri Pankov if (os->os_badfields == NULL) {
263e96f8419SYuri Pankov os->os_badfields = malloc(sp->s_nfields *
264e96f8419SYuri Pankov sizeof (char *));
265e96f8419SYuri Pankov if (os->os_badfields == NULL)
266e96f8419SYuri Pankov goto nomem;
267e96f8419SYuri Pankov }
268e96f8419SYuri Pankov os->os_badfields[nbad] = strdup(sp->s_fields[i]);
269e96f8419SYuri Pankov if (os->os_badfields[nbad] == NULL)
270e96f8419SYuri Pankov goto nomem;
271e96f8419SYuri Pankov continue;
272e96f8419SYuri Pankov }
273519cca71SSebastien Roy of[of_index].of_name = strdup(template[j].of_name);
274e96f8419SYuri Pankov if (of[of_index].of_name == NULL)
275e96f8419SYuri Pankov goto nomem;
276e96f8419SYuri Pankov if (multiline) {
277e96f8419SYuri Pankov int n = strlen(of[of_index].of_name);
278e96f8419SYuri Pankov
279e96f8419SYuri Pankov os->os_maxnamelen = MAX(n, os->os_maxnamelen);
280e96f8419SYuri Pankov }
281519cca71SSebastien Roy of[of_index].of_width = template[j].of_width;
282519cca71SSebastien Roy of[of_index].of_id = template[j].of_id;
283519cca71SSebastien Roy of[of_index].of_cb = template[j].of_cb;
284e96f8419SYuri Pankov of_index++;
285e96f8419SYuri Pankov }
286e96f8419SYuri Pankov splitfree(sp);
287e96f8419SYuri Pankov if (of_index == 0) /* all values in str are bogus */
288e96f8419SYuri Pankov return (OFMT_ENOFIELDS);
289e96f8419SYuri Pankov os->os_nfields = of_index; /* actual number of fields printed */
290e96f8419SYuri Pankov ofmt_update_winsize(*ofmt);
291e96f8419SYuri Pankov return (error);
292e96f8419SYuri Pankov nomem:
293e96f8419SYuri Pankov error = OFMT_ENOMEM;
294e96f8419SYuri Pankov if (os != NULL)
295e96f8419SYuri Pankov ofmt_close(os);
296e96f8419SYuri Pankov *ofmt = NULL;
297e96f8419SYuri Pankov splitfree(sp);
298e96f8419SYuri Pankov return (error);
299e96f8419SYuri Pankov }
300e96f8419SYuri Pankov
301519cca71SSebastien Roy void
ofmt_set_fs(ofmt_handle_t ofmt,char fs)302519cca71SSebastien Roy ofmt_set_fs(ofmt_handle_t ofmt, char fs)
303519cca71SSebastien Roy {
304519cca71SSebastien Roy ((ofmt_state_t *)ofmt)->os_fs = fs;
305519cca71SSebastien Roy }
306519cca71SSebastien Roy
307e96f8419SYuri Pankov /*
308e96f8419SYuri Pankov * free resources associated with the ofmt_handle_t
309e96f8419SYuri Pankov */
310e96f8419SYuri Pankov void
ofmt_close(ofmt_handle_t ofmt)311e96f8419SYuri Pankov ofmt_close(ofmt_handle_t ofmt)
312e96f8419SYuri Pankov {
313e96f8419SYuri Pankov ofmt_state_t *os = ofmt;
314*6fc1cd8dSToomas Soome uint_t i;
315e96f8419SYuri Pankov
316e96f8419SYuri Pankov if (os == NULL)
317e96f8419SYuri Pankov return;
318e96f8419SYuri Pankov for (i = 0; i < os->os_nfields; i++)
319e96f8419SYuri Pankov free(os->os_fields[i].of_name);
320e96f8419SYuri Pankov for (i = 0; i < os->os_nbad; i++)
321e96f8419SYuri Pankov free(os->os_badfields[i]);
322e96f8419SYuri Pankov free(os->os_badfields);
323e96f8419SYuri Pankov free(os);
324e96f8419SYuri Pankov }
325e96f8419SYuri Pankov
326e96f8419SYuri Pankov /*
327e96f8419SYuri Pankov * Print the value for the selected field by calling the callback-function
328e96f8419SYuri Pankov * registered for the field.
329e96f8419SYuri Pankov */
330e96f8419SYuri Pankov static void
ofmt_print_field(ofmt_state_t * os,ofmt_field_t * ofp,const char * value,boolean_t escsep)331e96f8419SYuri Pankov ofmt_print_field(ofmt_state_t *os, ofmt_field_t *ofp, const char *value,
332e96f8419SYuri Pankov boolean_t escsep)
333e96f8419SYuri Pankov {
334e96f8419SYuri Pankov uint_t width = ofp->of_width;
335e96f8419SYuri Pankov uint_t valwidth;
336e96f8419SYuri Pankov uint_t compress;
337e96f8419SYuri Pankov boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
338e96f8419SYuri Pankov boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
339e96f8419SYuri Pankov boolean_t rightjust = (os->os_flags & OFMT_RIGHTJUST);
340e96f8419SYuri Pankov char c;
341e96f8419SYuri Pankov
342e96f8419SYuri Pankov /*
343519cca71SSebastien Roy * Parsable fields are separated by os_fs. os_fs and '\' are escaped
344519cca71SSebastien Roy * (prefixed by a) '\'.
345e96f8419SYuri Pankov */
346e96f8419SYuri Pankov if (parsable) {
347e96f8419SYuri Pankov if (os->os_nfields == 1) {
348e96f8419SYuri Pankov (void) printf("%s", value);
349e96f8419SYuri Pankov return;
350e96f8419SYuri Pankov }
351e96f8419SYuri Pankov while ((c = *value++) != '\0') {
352519cca71SSebastien Roy if (escsep && ((c == os->os_fs || c == '\\')))
353e96f8419SYuri Pankov (void) putchar('\\');
354e96f8419SYuri Pankov (void) putchar(c);
355e96f8419SYuri Pankov }
356e96f8419SYuri Pankov if (!os->os_lastfield)
357519cca71SSebastien Roy (void) putchar(os->os_fs);
358e96f8419SYuri Pankov } else if (multiline) {
359e96f8419SYuri Pankov if (value[0] == '\0')
360e96f8419SYuri Pankov value = OFMT_VAL_UNDEF;
361e96f8419SYuri Pankov (void) printf("%*.*s: %s", os->os_maxnamelen,
362e96f8419SYuri Pankov os->os_maxnamelen, ofp->of_name, value);
363e96f8419SYuri Pankov if (!os->os_lastfield)
364e96f8419SYuri Pankov (void) putchar('\n');
365e96f8419SYuri Pankov } else {
366e96f8419SYuri Pankov if (os->os_lastfield) {
367e96f8419SYuri Pankov if (rightjust)
368e96f8419SYuri Pankov (void) printf("%*s", width, value);
369e96f8419SYuri Pankov else
370e96f8419SYuri Pankov (void) printf("%s", value);
371e96f8419SYuri Pankov os->os_overflow = 0;
372e96f8419SYuri Pankov return;
373e96f8419SYuri Pankov }
374e96f8419SYuri Pankov
375e96f8419SYuri Pankov valwidth = strlen(value);
376e96f8419SYuri Pankov if (valwidth + os->os_overflow >= width) {
377e96f8419SYuri Pankov os->os_overflow += valwidth - width + 1;
378e96f8419SYuri Pankov if (rightjust)
379e96f8419SYuri Pankov (void) printf("%*s ", width, value);
380e96f8419SYuri Pankov else
381e96f8419SYuri Pankov (void) printf("%s ", value);
382e96f8419SYuri Pankov return;
383e96f8419SYuri Pankov }
384e96f8419SYuri Pankov
385e96f8419SYuri Pankov if (os->os_overflow > 0) {
386e96f8419SYuri Pankov compress = MIN(os->os_overflow, width - valwidth);
387e96f8419SYuri Pankov os->os_overflow -= compress;
388e96f8419SYuri Pankov width -= compress;
389e96f8419SYuri Pankov }
390e96f8419SYuri Pankov if (rightjust)
391e96f8419SYuri Pankov (void) printf("%*s ", width, value);
392e96f8419SYuri Pankov else
393e96f8419SYuri Pankov (void) printf("%-*s", width, value);
394e96f8419SYuri Pankov }
395e96f8419SYuri Pankov }
396e96f8419SYuri Pankov
397e96f8419SYuri Pankov /*
398e96f8419SYuri Pankov * Print enough to fit the field width.
399e96f8419SYuri Pankov */
400e96f8419SYuri Pankov static void
ofmt_fit_width(split_t ** spp,uint_t width,char * value,uint_t bufsize)401e96f8419SYuri Pankov ofmt_fit_width(split_t **spp, uint_t width, char *value, uint_t bufsize)
402e96f8419SYuri Pankov {
403e96f8419SYuri Pankov split_t *sp = *spp;
404e96f8419SYuri Pankov char *ptr = value, *lim = ptr + bufsize;
405*6fc1cd8dSToomas Soome uint_t i, nextlen;
406e96f8419SYuri Pankov
407e96f8419SYuri Pankov if (sp == NULL) {
408e96f8419SYuri Pankov sp = split_str(value, OFMT_MAX_ROWS);
409e96f8419SYuri Pankov if (sp == NULL)
410e96f8419SYuri Pankov return;
411e96f8419SYuri Pankov
412e96f8419SYuri Pankov *spp = sp;
413e96f8419SYuri Pankov }
414e96f8419SYuri Pankov for (i = sp->s_currfield; i < sp->s_nfields; i++) {
415e96f8419SYuri Pankov ptr += snprintf(ptr, lim - ptr, "%s,", sp->s_fields[i]);
416e96f8419SYuri Pankov if (i + 1 == sp->s_nfields) {
417e96f8419SYuri Pankov nextlen = 0;
418e96f8419SYuri Pankov if (ptr > value)
419e96f8419SYuri Pankov ptr[-1] = '\0';
420e96f8419SYuri Pankov } else {
421e96f8419SYuri Pankov nextlen = strlen(sp->s_fields[i + 1]);
422e96f8419SYuri Pankov }
423e96f8419SYuri Pankov
424e96f8419SYuri Pankov if (strlen(value) + nextlen > width || ptr >= lim) {
425e96f8419SYuri Pankov i++;
426e96f8419SYuri Pankov break;
427e96f8419SYuri Pankov }
428e96f8419SYuri Pankov }
429e96f8419SYuri Pankov sp->s_currfield = i;
430e96f8419SYuri Pankov }
431e96f8419SYuri Pankov
432e96f8419SYuri Pankov /*
433e96f8419SYuri Pankov * Print one or more rows of output values for the selected columns.
434e96f8419SYuri Pankov */
435e96f8419SYuri Pankov void
ofmt_print(ofmt_handle_t ofmt,void * arg)436e96f8419SYuri Pankov ofmt_print(ofmt_handle_t ofmt, void *arg)
437e96f8419SYuri Pankov {
438e96f8419SYuri Pankov ofmt_state_t *os = ofmt;
439*6fc1cd8dSToomas Soome uint_t i;
440e96f8419SYuri Pankov char value[1024];
441e96f8419SYuri Pankov ofmt_field_t *of;
442e96f8419SYuri Pankov boolean_t escsep, more_rows;
443e96f8419SYuri Pankov ofmt_arg_t ofarg;
444e96f8419SYuri Pankov split_t **sp = NULL;
445e96f8419SYuri Pankov boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
446e96f8419SYuri Pankov boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
447e96f8419SYuri Pankov boolean_t wrap = (os->os_flags & OFMT_WRAP);
448e96f8419SYuri Pankov
449e96f8419SYuri Pankov if (wrap) {
450e96f8419SYuri Pankov sp = calloc(sizeof (split_t *), os->os_nfields);
451e96f8419SYuri Pankov if (sp == NULL)
452e96f8419SYuri Pankov return;
453e96f8419SYuri Pankov }
454e96f8419SYuri Pankov
455e96f8419SYuri Pankov if ((os->os_nrow++ % os->os_winsize.ws_row) == 0 &&
456e96f8419SYuri Pankov !parsable && !multiline) {
457519cca71SSebastien Roy if (!(os->os_flags & OFMT_NOHEADER))
458e96f8419SYuri Pankov ofmt_print_header(os);
459e96f8419SYuri Pankov os->os_nrow++;
460e96f8419SYuri Pankov }
461e96f8419SYuri Pankov
462e96f8419SYuri Pankov if (multiline && os->os_nrow > 1)
463e96f8419SYuri Pankov (void) putchar('\n');
464e96f8419SYuri Pankov
465e96f8419SYuri Pankov of = os->os_fields;
466e96f8419SYuri Pankov escsep = (os->os_nfields > 1);
467e96f8419SYuri Pankov more_rows = B_FALSE;
468e96f8419SYuri Pankov for (i = 0; i < os->os_nfields; i++) {
469e96f8419SYuri Pankov os->os_lastfield = (i + 1 == os->os_nfields);
470e96f8419SYuri Pankov value[0] = '\0';
471e96f8419SYuri Pankov ofarg.ofmt_id = of[i].of_id;
472e96f8419SYuri Pankov ofarg.ofmt_cbarg = arg;
473e96f8419SYuri Pankov
474e96f8419SYuri Pankov if ((*of[i].of_cb)(&ofarg, value, sizeof (value))) {
475e96f8419SYuri Pankov if (wrap) {
476e96f8419SYuri Pankov /*
477e96f8419SYuri Pankov * 'value' will be split at comma boundaries
478e96f8419SYuri Pankov * and stored into sp[i].
479e96f8419SYuri Pankov */
480e96f8419SYuri Pankov ofmt_fit_width(&sp[i], of[i].of_width, value,
481e96f8419SYuri Pankov sizeof (value));
482e96f8419SYuri Pankov if (sp[i] != NULL &&
483e96f8419SYuri Pankov sp[i]->s_currfield < sp[i]->s_nfields)
484e96f8419SYuri Pankov more_rows = B_TRUE;
485e96f8419SYuri Pankov }
486e96f8419SYuri Pankov
487e96f8419SYuri Pankov ofmt_print_field(os, &of[i],
488e96f8419SYuri Pankov (*value == '\0' && !parsable) ?
489e96f8419SYuri Pankov OFMT_VAL_UNDEF : value, escsep);
490e96f8419SYuri Pankov } else {
491e96f8419SYuri Pankov ofmt_print_field(os, &of[i], OFMT_VAL_UNKNOWN, escsep);
492e96f8419SYuri Pankov }
493e96f8419SYuri Pankov }
494e96f8419SYuri Pankov (void) putchar('\n');
495e96f8419SYuri Pankov
496e96f8419SYuri Pankov while (more_rows) {
497e96f8419SYuri Pankov more_rows = B_FALSE;
498e96f8419SYuri Pankov for (i = 0; i < os->os_nfields; i++) {
499e96f8419SYuri Pankov os->os_lastfield = (i + 1 == os->os_nfields);
500e96f8419SYuri Pankov value[0] = '\0';
501e96f8419SYuri Pankov
502e96f8419SYuri Pankov ofmt_fit_width(&sp[i], of[i].of_width,
503e96f8419SYuri Pankov value, sizeof (value));
504e96f8419SYuri Pankov if (sp[i] != NULL &&
505e96f8419SYuri Pankov sp[i]->s_currfield < sp[i]->s_nfields)
506e96f8419SYuri Pankov more_rows = B_TRUE;
507e96f8419SYuri Pankov
508e96f8419SYuri Pankov ofmt_print_field(os, &of[i], value, escsep);
509e96f8419SYuri Pankov }
510e96f8419SYuri Pankov (void) putchar('\n');
511e96f8419SYuri Pankov }
512e96f8419SYuri Pankov (void) fflush(stdout);
513e96f8419SYuri Pankov
514e96f8419SYuri Pankov if (sp != NULL) {
515e96f8419SYuri Pankov for (i = 0; i < os->os_nfields; i++)
516e96f8419SYuri Pankov splitfree(sp[i]);
517e96f8419SYuri Pankov free(sp);
518e96f8419SYuri Pankov }
519e96f8419SYuri Pankov }
520e96f8419SYuri Pankov
521e96f8419SYuri Pankov /*
522e96f8419SYuri Pankov * Print the field headers
523e96f8419SYuri Pankov */
524519cca71SSebastien Roy void
ofmt_print_header(ofmt_handle_t ofmt)525519cca71SSebastien Roy ofmt_print_header(ofmt_handle_t ofmt)
526e96f8419SYuri Pankov {
527519cca71SSebastien Roy ofmt_state_t *os = ofmt;
528*6fc1cd8dSToomas Soome uint_t i;
529e96f8419SYuri Pankov ofmt_field_t *of = os->os_fields;
530e96f8419SYuri Pankov boolean_t escsep = (os->os_nfields > 1);
531e96f8419SYuri Pankov
532e96f8419SYuri Pankov for (i = 0; i < os->os_nfields; i++) {
533e96f8419SYuri Pankov os->os_lastfield = (i + 1 == os->os_nfields);
534e96f8419SYuri Pankov ofmt_print_field(os, &of[i], of[i].of_name, escsep);
535e96f8419SYuri Pankov }
536e96f8419SYuri Pankov (void) putchar('\n');
537e96f8419SYuri Pankov }
538e96f8419SYuri Pankov
539e96f8419SYuri Pankov /*
540e96f8419SYuri Pankov * Update the current window size.
541e96f8419SYuri Pankov */
542e96f8419SYuri Pankov void
ofmt_update_winsize(ofmt_handle_t ofmt)543e96f8419SYuri Pankov ofmt_update_winsize(ofmt_handle_t ofmt)
544e96f8419SYuri Pankov {
545e96f8419SYuri Pankov ofmt_state_t *os = ofmt;
546e96f8419SYuri Pankov struct winsize *winsize = &os->os_winsize;
547e96f8419SYuri Pankov
548e96f8419SYuri Pankov if (ioctl(1, TIOCGWINSZ, winsize) == -1 ||
549e96f8419SYuri Pankov winsize->ws_col == 0 || winsize->ws_row == 0) {
550e96f8419SYuri Pankov winsize->ws_col = 80;
551e96f8419SYuri Pankov winsize->ws_row = 24;
552e96f8419SYuri Pankov }
553e96f8419SYuri Pankov }
554e96f8419SYuri Pankov
555e96f8419SYuri Pankov /*
556e96f8419SYuri Pankov * Return error diagnostics using the information in the ofmt_handle_t
557e96f8419SYuri Pankov */
558e96f8419SYuri Pankov char *
ofmt_strerror(ofmt_handle_t ofmt,ofmt_status_t error,char * buf,uint_t bufsize)559e96f8419SYuri Pankov ofmt_strerror(ofmt_handle_t ofmt, ofmt_status_t error, char *buf,
560e96f8419SYuri Pankov uint_t bufsize)
561e96f8419SYuri Pankov {
562e96f8419SYuri Pankov ofmt_state_t *os = ofmt;
563*6fc1cd8dSToomas Soome uint_t i;
564e96f8419SYuri Pankov const char *s;
565e96f8419SYuri Pankov char ebuf[OFMT_BUFSIZE];
566e96f8419SYuri Pankov boolean_t parsable;
567e96f8419SYuri Pankov
568e96f8419SYuri Pankov /*
569e96f8419SYuri Pankov * ebuf is intended for optional error-specific data to be appended
570e96f8419SYuri Pankov * after the internationalized error string for an error code.
571e96f8419SYuri Pankov */
572e96f8419SYuri Pankov ebuf[0] = '\0';
573e96f8419SYuri Pankov
574e96f8419SYuri Pankov switch (error) {
575e96f8419SYuri Pankov case OFMT_SUCCESS:
576e96f8419SYuri Pankov s = "success";
577e96f8419SYuri Pankov break;
578e96f8419SYuri Pankov case OFMT_EBADFIELDS:
579e96f8419SYuri Pankov /*
580e96f8419SYuri Pankov * Enumerate the singular/plural version of the warning
581e96f8419SYuri Pankov * and error to simplify and improve localization.
582e96f8419SYuri Pankov */
583e96f8419SYuri Pankov parsable = (os->os_flags & OFMT_PARSABLE);
584e96f8419SYuri Pankov if (!parsable) {
585e96f8419SYuri Pankov if (os->os_nbad > 1)
586e96f8419SYuri Pankov s = "ignoring unknown output fields:";
587e96f8419SYuri Pankov else
588e96f8419SYuri Pankov s = "ignoring unknown output field:";
589e96f8419SYuri Pankov } else {
590e96f8419SYuri Pankov if (os->os_nbad > 1)
591e96f8419SYuri Pankov s = "unknown output fields:";
592e96f8419SYuri Pankov else
593e96f8419SYuri Pankov s = "unknown output field:";
594e96f8419SYuri Pankov }
595e96f8419SYuri Pankov /* set up the bad fields in ebuf */
596e96f8419SYuri Pankov for (i = 0; i < os->os_nbad; i++) {
597e96f8419SYuri Pankov (void) strlcat(ebuf, " `", sizeof (ebuf));
598e96f8419SYuri Pankov (void) strlcat(ebuf, os->os_badfields[i],
599e96f8419SYuri Pankov sizeof (ebuf));
600e96f8419SYuri Pankov (void) strlcat(ebuf, "'", sizeof (ebuf));
601e96f8419SYuri Pankov }
602e96f8419SYuri Pankov break;
603e96f8419SYuri Pankov case OFMT_ENOFIELDS:
604e96f8419SYuri Pankov s = "no valid output fields";
605e96f8419SYuri Pankov break;
606e96f8419SYuri Pankov case OFMT_EPARSEMULTI:
607e96f8419SYuri Pankov s = "multiline mode incompatible with parsable mode";
608e96f8419SYuri Pankov break;
609e96f8419SYuri Pankov case OFMT_EPARSEALL:
610e96f8419SYuri Pankov s = "output field `all' invalid in parsable mode";
611e96f8419SYuri Pankov break;
612e96f8419SYuri Pankov case OFMT_EPARSENONE:
613e96f8419SYuri Pankov s = "output fields must be specified in parsable mode";
614e96f8419SYuri Pankov break;
615e96f8419SYuri Pankov case OFMT_EPARSEWRAP:
616e96f8419SYuri Pankov s = "parsable mode is incompatible with wrap mode";
617e96f8419SYuri Pankov break;
618e96f8419SYuri Pankov case OFMT_ENOTEMPLATE:
619e96f8419SYuri Pankov s = "no template provided for fields";
620e96f8419SYuri Pankov break;
621e96f8419SYuri Pankov case OFMT_ENOMEM:
622e96f8419SYuri Pankov s = strerror(ENOMEM);
623e96f8419SYuri Pankov break;
624e96f8419SYuri Pankov default:
625e96f8419SYuri Pankov (void) snprintf(buf, bufsize,
626e96f8419SYuri Pankov dgettext(TEXT_DOMAIN, "unknown ofmt error (%d)"),
627e96f8419SYuri Pankov error);
628e96f8419SYuri Pankov return (buf);
629e96f8419SYuri Pankov }
630e96f8419SYuri Pankov (void) snprintf(buf, bufsize, dgettext(TEXT_DOMAIN, s));
631e96f8419SYuri Pankov (void) strlcat(buf, ebuf, bufsize);
632e96f8419SYuri Pankov return (buf);
633e96f8419SYuri Pankov }
634b2f26520SBryan Cantrill
635b2f26520SBryan Cantrill void
ofmt_check(ofmt_status_t oferr,boolean_t parsable,ofmt_handle_t ofmt,void (* die)(const char *,...),void (* warn)(const char *,...))636b2f26520SBryan Cantrill ofmt_check(ofmt_status_t oferr, boolean_t parsable, ofmt_handle_t ofmt,
637b2f26520SBryan Cantrill void (*die)(const char *, ...), void (*warn)(const char *, ...))
638b2f26520SBryan Cantrill {
639b2f26520SBryan Cantrill char buf[OFMT_BUFSIZE];
640b2f26520SBryan Cantrill
641b2f26520SBryan Cantrill assert(die != NULL);
642b2f26520SBryan Cantrill assert(warn != NULL);
643b2f26520SBryan Cantrill
644b2f26520SBryan Cantrill if (oferr == OFMT_SUCCESS)
645b2f26520SBryan Cantrill return;
646b2f26520SBryan Cantrill
647b2f26520SBryan Cantrill (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
648b2f26520SBryan Cantrill
649b2f26520SBryan Cantrill /*
650b2f26520SBryan Cantrill * All errors are considered fatal in parsable mode. OFMT_ENOMEM and
651b2f26520SBryan Cantrill * OFMT_ENOFIELDS errors are always fatal, regardless of mode. For
652b2f26520SBryan Cantrill * other errors, we print diagnostics in human-readable mode and
653b2f26520SBryan Cantrill * processs what we can.
654b2f26520SBryan Cantrill */
655b2f26520SBryan Cantrill if (parsable || oferr == OFMT_ENOFIELDS || oferr == OFMT_ENOMEM) {
656b2f26520SBryan Cantrill ofmt_close(ofmt);
657b2f26520SBryan Cantrill die(buf);
658b2f26520SBryan Cantrill } else {
659b2f26520SBryan Cantrill warn(buf);
660b2f26520SBryan Cantrill }
661b2f26520SBryan Cantrill }
662