]>
Commit | Line | Data |
---|---|---|
fa469183 KZ |
1 | /* |
2 | * Copyright (C) 2016 Karel Zak <kzak@redhat.com> | |
3 | * | |
4 | * This file may be redistributed under the terms of the | |
5 | * GNU Lesser General Public License. | |
6 | */ | |
7 | #include <stdlib.h> | |
8 | #include <unistd.h> | |
9 | #include <string.h> | |
10 | #include <errno.h> | |
11 | #include <sys/types.h> | |
12 | #include <sys/stat.h> | |
13 | #include <dirent.h> | |
14 | #include <getopt.h> | |
15 | ||
16 | #include "c.h" | |
17 | #include "nls.h" | |
18 | #include "strutils.h" | |
19 | #include "xalloc.h" | |
4a2b50f2 | 20 | #include "optutils.h" |
fa469183 KZ |
21 | |
22 | #include "libsmartcols.h" | |
23 | ||
24 | struct column_flag { | |
25 | const char *name; | |
26 | int mask; | |
27 | }; | |
28 | ||
29 | static const struct column_flag flags[] = { | |
30 | { "trunc", SCOLS_FL_TRUNC }, | |
31 | { "tree", SCOLS_FL_TREE }, | |
32 | { "right", SCOLS_FL_RIGHT }, | |
33 | { "strictwidth",SCOLS_FL_STRICTWIDTH }, | |
34 | { "noextremes", SCOLS_FL_NOEXTREMES }, | |
35 | { "hidden", SCOLS_FL_HIDDEN }, | |
36 | { "wrap", SCOLS_FL_WRAP }, | |
949ea05f | 37 | { "wrapnl", SCOLS_FL_WRAP }, |
fa469183 KZ |
38 | { "none", 0 } |
39 | }; | |
40 | ||
332123f2 | 41 | static long name_to_flag(const char *name, size_t namesz) |
fa469183 KZ |
42 | { |
43 | size_t i; | |
44 | ||
45 | for (i = 0; i < ARRAY_SIZE(flags); i++) { | |
46 | const char *cn = flags[i].name; | |
47 | ||
48 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
49 | return flags[i].mask; | |
50 | } | |
18ff5154 | 51 | warnx("unknown flag: %s", name); |
fa469183 KZ |
52 | return -1; |
53 | } | |
54 | ||
55 | static int parse_column_flags(char *str) | |
56 | { | |
4ff3d13b | 57 | unsigned long num_flags = 0; |
fa469183 | 58 | |
4ff3d13b | 59 | if (string_to_bitmask(str, &num_flags, name_to_flag)) |
fa469183 KZ |
60 | err(EXIT_FAILURE, "failed to parse column flags"); |
61 | ||
4ff3d13b | 62 | return num_flags; |
fa469183 KZ |
63 | } |
64 | ||
65 | static struct libscols_column *parse_column(FILE *f) | |
66 | { | |
67 | size_t len = 0; | |
68 | char *line = NULL; | |
69 | int nlines = 0; | |
70 | ||
71 | struct libscols_column *cl = NULL; | |
72 | ||
73 | while (getline(&line, &len, f) != -1) { | |
74 | ||
75 | char *p = strrchr(line, '\n'); | |
76 | if (p) | |
77 | *p = '\0'; | |
78 | ||
79 | switch (nlines) { | |
80 | case 0: /* NAME */ | |
81 | { | |
82 | struct libscols_cell *hr; | |
83 | ||
84 | cl = scols_new_column(); | |
85 | if (!cl) | |
86 | goto fail; | |
87 | hr = scols_column_get_header(cl); | |
88 | if (!hr || scols_cell_set_data(hr, line)) | |
89 | goto fail; | |
90 | break; | |
91 | } | |
92 | case 1: /* WIDTH-HINT */ | |
93 | { | |
94 | double whint = strtod_or_err(line, "failed to parse column whint"); | |
95 | if (scols_column_set_whint(cl, whint)) | |
96 | goto fail; | |
97 | break; | |
98 | } | |
99 | case 2: /* FLAGS */ | |
100 | { | |
4ff3d13b SK |
101 | int num_flags = parse_column_flags(line); |
102 | if (scols_column_set_flags(cl, num_flags)) | |
fa469183 | 103 | goto fail; |
949ea05f KZ |
104 | if (strcmp(line, "wrapnl") == 0) { |
105 | scols_column_set_wrapfunc(cl, | |
106 | scols_wrapnl_chunksize, | |
107 | scols_wrapnl_nextchunk, | |
108 | NULL); | |
109 | scols_column_set_safechars(cl, "\n"); | |
110 | } | |
fa469183 KZ |
111 | break; |
112 | } | |
113 | case 3: /* COLOR */ | |
114 | if (scols_column_set_color(cl, line)) | |
115 | goto fail; | |
116 | break; | |
117 | default: | |
118 | break; | |
119 | } | |
120 | ||
121 | nlines++; | |
122 | } | |
123 | ||
d1bf0ce5 | 124 | free(line); |
fa469183 KZ |
125 | return cl; |
126 | fail: | |
d1bf0ce5 | 127 | free(line); |
fa469183 KZ |
128 | scols_unref_column(cl); |
129 | return NULL; | |
130 | } | |
131 | ||
18ff5154 | 132 | static int parse_column_data(FILE *f, struct libscols_table *tb, int col) |
fa469183 KZ |
133 | { |
134 | size_t len = 0, nlines = 0; | |
135 | int i; | |
136 | char *str = NULL; | |
137 | ||
138 | while ((i = getline(&str, &len, f)) != -1) { | |
139 | ||
140 | struct libscols_line *ln; | |
141 | char *p = strrchr(str, '\n'); | |
142 | if (p) | |
143 | *p = '\0'; | |
144 | ||
145 | while ((p = strrchr(str, '\\')) && *(p + 1) == 'n') { | |
146 | *p = '\n'; | |
147 | memmove(p + 1, p + 2, i - (p + 2 - str)); | |
148 | } | |
149 | ||
150 | ln = scols_table_get_line(tb, nlines++); | |
151 | if (!ln) | |
152 | break; | |
18ff5154 KZ |
153 | |
154 | scols_line_set_data(ln, col, str); | |
fa469183 KZ |
155 | } |
156 | ||
d1bf0ce5 | 157 | free(str); |
fa469183 KZ |
158 | return 0; |
159 | ||
160 | } | |
161 | ||
18ff5154 KZ |
162 | static struct libscols_line *get_line_with_id(struct libscols_table *tb, |
163 | int col_id, const char *id) | |
164 | { | |
165 | struct libscols_line *ln; | |
166 | struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); | |
167 | ||
168 | while (scols_table_next_line(tb, itr, &ln) == 0) { | |
169 | struct libscols_cell *ce = scols_line_get_cell(ln, col_id); | |
170 | const char *data = ce ? scols_cell_get_data(ce) : NULL; | |
171 | ||
172 | if (data && strcmp(data, id) == 0) | |
173 | break; | |
174 | } | |
175 | ||
176 | scols_free_iter(itr); | |
177 | return ln; | |
178 | } | |
179 | ||
180 | static void compose_tree(struct libscols_table *tb, int parent_col, int id_col) | |
181 | { | |
182 | struct libscols_line *ln; | |
183 | struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); | |
184 | ||
185 | while (scols_table_next_line(tb, itr, &ln) == 0) { | |
186 | struct libscols_line *parent = NULL; | |
187 | struct libscols_cell *ce = scols_line_get_cell(ln, parent_col); | |
188 | const char *data = ce ? scols_cell_get_data(ce) : NULL; | |
189 | ||
190 | if (data) | |
191 | parent = get_line_with_id(tb, id_col, data); | |
192 | if (parent) | |
193 | scols_line_add_child(parent, ln); | |
194 | } | |
195 | ||
196 | scols_free_iter(itr); | |
197 | } | |
198 | ||
199 | ||
6e1eda6f | 200 | static void __attribute__((__noreturn__)) usage(void) |
18ff5154 | 201 | { |
6e1eda6f | 202 | FILE *out = stdout; |
18ff5154 KZ |
203 | fprintf(out, |
204 | "\n %s [options] <column-data-file> ...\n\n", program_invocation_short_name); | |
205 | ||
206 | fputs(" -m, --maxout fill all terminal width\n", out); | |
207 | fputs(" -c, --column <file> column definition\n", out); | |
208 | fputs(" -n, --nlines <num> number of lines\n", out); | |
6a82714d | 209 | fputs(" -J, --json JSON output format\n", out); |
4a2b50f2 | 210 | fputs(" -r, --raw RAW output format\n", out); |
695fd479 KZ |
211 | fputs(" -E, --export use key=\"value\" output format\n", out); |
212 | fputs(" -C, --colsep <str> set columns separator\n", out); | |
18ff5154 KZ |
213 | fputs(" -w, --width <num> hardcode terminal width\n", out); |
214 | fputs(" -p, --tree-parent-column <n> parent column\n", out); | |
215 | fputs(" -i, --tree-id-column <n> id column\n", out); | |
216 | fputs(" -h, --help this help\n", out); | |
217 | fputs("\n", out); | |
218 | ||
6e1eda6f | 219 | exit(EXIT_SUCCESS); |
18ff5154 KZ |
220 | } |
221 | ||
fa469183 KZ |
222 | int main(int argc, char *argv[]) |
223 | { | |
224 | struct libscols_table *tb; | |
225 | int c, n, nlines = 0; | |
18ff5154 | 226 | int parent_col = -1, id_col = -1; |
fa469183 KZ |
227 | |
228 | static const struct option longopts[] = { | |
71f08e97 SK |
229 | { "maxout", 0, NULL, 'm' }, |
230 | { "column", 1, NULL, 'c' }, | |
231 | { "nlines", 1, NULL, 'n' }, | |
232 | { "width", 1, NULL, 'w' }, | |
233 | { "tree-parent-column", 1, NULL, 'p' }, | |
234 | { "tree-id-column", 1, NULL, 'i' }, | |
235 | { "json", 0, NULL, 'J' }, | |
236 | { "raw", 0, NULL, 'r' }, | |
237 | { "export", 0, NULL, 'E' }, | |
238 | { "colsep", 1, NULL, 'C' }, | |
239 | { "help", 0, NULL, 'h' }, | |
240 | { NULL, 0, NULL, 0 }, | |
fa469183 KZ |
241 | }; |
242 | ||
a7349ee3 | 243 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ |
4a2b50f2 KZ |
244 | { 'E', 'J', 'r' }, |
245 | { 0 } | |
246 | }; | |
247 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
fa469183 | 248 | |
4a2b50f2 | 249 | setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */ |
fa469183 KZ |
250 | scols_init_debug(0); |
251 | ||
252 | tb = scols_new_table(); | |
253 | if (!tb) | |
254 | err(EXIT_FAILURE, "failed to create output table"); | |
255 | ||
695fd479 | 256 | while((c = getopt_long(argc, argv, "hCc:Ei:Jmn:p:rw:", longopts, NULL)) != -1) { |
4a2b50f2 KZ |
257 | |
258 | err_exclusive_options(c, longopts, excl, excl_st); | |
259 | ||
fa469183 KZ |
260 | switch(c) { |
261 | case 'c': /* add column from file */ | |
262 | { | |
263 | struct libscols_column *cl; | |
264 | FILE *f = fopen(optarg, "r"); | |
265 | ||
266 | if (!f) | |
267 | err(EXIT_FAILURE, "%s: open failed", optarg); | |
268 | cl = parse_column(f); | |
269 | if (cl && scols_table_add_column(tb, cl)) | |
270 | err(EXIT_FAILURE, "%s: failed to add column", optarg); | |
271 | scols_unref_column(cl); | |
272 | fclose(f); | |
273 | break; | |
274 | } | |
18ff5154 KZ |
275 | case 'p': |
276 | parent_col = strtou32_or_err(optarg, "failed to parse tree PARENT column"); | |
277 | break; | |
278 | case 'i': | |
279 | id_col = strtou32_or_err(optarg, "failed to parse tree ID column"); | |
280 | break; | |
6a82714d KZ |
281 | case 'J': |
282 | scols_table_enable_json(tb, 1); | |
283 | scols_table_set_name(tb, "testtable"); | |
284 | break; | |
fa469183 KZ |
285 | case 'm': |
286 | scols_table_enable_maxout(tb, TRUE); | |
287 | break; | |
4a2b50f2 KZ |
288 | case 'r': |
289 | scols_table_enable_raw(tb, TRUE); | |
290 | break; | |
291 | case 'E': | |
292 | scols_table_enable_export(tb, TRUE); | |
293 | break; | |
695fd479 KZ |
294 | case 'C': |
295 | scols_table_set_column_separator(tb, optarg); | |
296 | break; | |
fa469183 KZ |
297 | case 'n': |
298 | nlines = strtou32_or_err(optarg, "failed to parse number of lines"); | |
299 | break; | |
a414a170 KZ |
300 | case 'w': |
301 | scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS); | |
302 | scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width")); | |
303 | break; | |
18ff5154 | 304 | case 'h': |
6e1eda6f | 305 | usage(); |
18ff5154 | 306 | default: |
6e1eda6f | 307 | errtryhelp(EXIT_FAILURE); |
fa469183 KZ |
308 | } |
309 | } | |
310 | ||
311 | if (nlines <= 0) | |
312 | errx(EXIT_FAILURE, "--nlines not set"); | |
313 | ||
314 | for (n = 0; n < nlines; n++) { | |
315 | struct libscols_line *ln = scols_new_line(); | |
316 | ||
317 | if (!ln || scols_table_add_line(tb, ln)) | |
318 | err(EXIT_FAILURE, "failed to add a new line"); | |
d1bf0ce5 KZ |
319 | |
320 | scols_unref_line(ln); | |
fa469183 KZ |
321 | } |
322 | ||
323 | n = 0; | |
324 | ||
325 | while (optind < argc) { | |
326 | FILE *f = fopen(argv[optind], "r"); | |
327 | ||
328 | if (!f) | |
329 | err(EXIT_FAILURE, "%s: open failed", argv[optind]); | |
330 | ||
331 | parse_column_data(f, tb, n); | |
332 | optind++; | |
333 | n++; | |
334 | } | |
335 | ||
18ff5154 KZ |
336 | if (scols_table_is_tree(tb) && parent_col >= 0 && id_col >= 0) |
337 | compose_tree(tb, parent_col, id_col); | |
338 | ||
fa469183 KZ |
339 | scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); |
340 | ||
341 | scols_print_table(tb); | |
342 | scols_unref_table(tb); | |
343 | return EXIT_SUCCESS; | |
344 | } |