1 /*
2 * column.c - functions for table handling at the column level
3 *
4 * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
5 * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
6 *
7 * This file may be redistributed under the terms of the
8 * GNU Lesser General Public License.
9 */
10
11 /**
12 * SECTION: column
13 * @title: Column
14 * @short_description: defines output columns formats, headers, etc.
15 *
16 * An API to access and modify per-column data and information.
17 */
18
19
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <ctype.h>
24
25 #include "mbsalign.h"
26 #include "strutils.h"
27 #include "smartcolsP.h"
28
29 /**
30 * scols_new_column:
31 *
32 * Allocates space for a new column.
33 *
34 * Returns: a pointer to a new struct libscols_column instance, NULL in case of an ENOMEM error.
35 */
36 struct libscols_column *scols_new_column(void)
37 {
38 struct libscols_column *cl;
39
40 cl = calloc(1, sizeof(*cl));
41 if (!cl)
42 return NULL;
43 DBG(COL, ul_debugobj(cl, "alloc"));
44 cl->refcount = 1;
45 INIT_LIST_HEAD(&cl->cl_columns);
46 return cl;
47 }
48
49 /**
50 * scols_ref_column:
51 * @cl: a pointer to a struct libscols_column instance
52 *
53 * Increases the refcount of @cl.
54 */
55 void scols_ref_column(struct libscols_column *cl)
56 {
57 if (cl)
58 cl->refcount++;
59 }
60
61 /**
62 * scols_unref_column:
63 * @cl: a pointer to a struct libscols_column instance
64 *
65 * Decreases the refcount of @cl. When the count falls to zero, the instance
66 * is automatically deallocated.
67 */
68 void scols_unref_column(struct libscols_column *cl)
69 {
70 if (cl && --cl->refcount <= 0) {
71 DBG(COL, ul_debugobj(cl, "dealloc"));
72 list_del(&cl->cl_columns);
73 scols_reset_cell(&cl->header);
74 free(cl->color);
75 free(cl->safechars);
76 free(cl->pending_data_buf);
77 free(cl->shellvar);
78 free(cl);
79 }
80 }
81
82 /**
83 * scols_copy_column:
84 * @cl: a pointer to a struct libscols_column instance
85 *
86 * Creates a new column and copies @cl's data over to it.
87 *
88 * Returns: a pointer to a new struct libscols_column instance.
89 */
90 struct libscols_column *scols_copy_column(const struct libscols_column *cl)
91 {
92 struct libscols_column *ret;
93
94 if (!cl)
95 return NULL;
96 ret = scols_new_column();
97 if (!ret)
98 return NULL;
99
100 DBG(COL, ul_debugobj(cl, "copy"));
101
102 if (scols_column_set_color(ret, cl->color))
103 goto err;
104 if (scols_cell_copy_content(&ret->header, &cl->header))
105 goto err;
106
107 ret->width = cl->width;
108 ret->width_hint = cl->width_hint;
109 ret->flags = cl->flags;
110 ret->is_groups = cl->is_groups;
111
112 memcpy(&ret->wstat, &cl->wstat, sizeof(cl->wstat));
113
114 return ret;
115 err:
116 scols_unref_column(ret);
117 return NULL;
118 }
119
120 /**
121 * scols_column_set_whint:
122 * @cl: a pointer to a struct libscols_column instance
123 * @whint: a width hint
124 *
125 * Sets the width hint of column @cl to @whint. See scols_table_new_column().
126 *
127 * Returns: 0, a negative value in case of an error.
128 */
129 int scols_column_set_whint(struct libscols_column *cl, double whint)
130 {
131 if (!cl)
132 return -EINVAL;
133
134 cl->width_hint = whint;
135 return 0;
136 }
137
138 /**
139 * scols_column_get_whint:
140 * @cl: a pointer to a struct libscols_column instance
141 *
142 * Returns: The width hint of column @cl, a negative value in case of an error.
143 */
144 double scols_column_get_whint(const struct libscols_column *cl)
145 {
146 return cl->width_hint;
147 }
148
149 /**
150 * scols_column_set_flags:
151 * @cl: a pointer to a struct libscols_column instance
152 * @flags: a flag mask
153 *
154 * Sets the flags of @cl to @flags.
155 *
156 * Returns: 0, a negative value in case of an error.
157 */
158 int scols_column_set_flags(struct libscols_column *cl, int flags)
159 {
160 if (!cl)
161 return -EINVAL;
162
163 if (cl->table) {
164 if (!(cl->flags & SCOLS_FL_TREE) && (flags & SCOLS_FL_TREE))
165 cl->table->ntreecols++;
166 else if ((cl->flags & SCOLS_FL_TREE) && !(flags & SCOLS_FL_TREE))
167 cl->table->ntreecols--;
168 }
169
170 DBG(COL, ul_debugobj(cl, "setting flags from 0x%04x to 0x%04x", cl->flags, flags));
171 cl->flags = flags;
172 return 0;
173 }
174
175 /**
176 * scols_column_set_json_type:
177 * @cl: a pointer to a struct libscols_column instance
178 * @type: SCOLS_JSON_* type
179 *
180 * Sets the type used for JSON formatting, the default is SCOLS_JSON_STRING.
181 *
182 * Returns: 0, a negative value in case of an error.
183 *
184 * Since: 2.33
185 */
186 int scols_column_set_json_type(struct libscols_column *cl, int type)
187 {
188 if (!cl)
189 return -EINVAL;
190
191 cl->json_type = type;
192 return 0;
193
194 }
195
196 /**
197 * scols_column_get_json_type:
198 * @cl: a pointer to a struct libscols_column instance
199 *
200 * Note that SCOLS_JSON_BOOLEAN interprets NULL, empty strings, '0', 'N' and
201 * 'n' as "false"; and everything else as "true".
202 *
203 * Returns: JSON type used for formatting or a negative value in case of an error.
204 *
205 * Since: 2.33
206 */
207 int scols_column_get_json_type(const struct libscols_column *cl)
208 {
209 return cl ? cl->json_type : -EINVAL;
210 }
211
212
213 /**
214 * scols_column_get_table:
215 * @cl: a pointer to a struct libscols_column instance
216 *
217 * Returns: pointer to the table where columns is used
218 */
219 struct libscols_table *scols_column_get_table(const struct libscols_column *cl)
220 {
221 return cl->table;
222 }
223
224 /**
225 * scols_column_get_flags:
226 * @cl: a pointer to a struct libscols_column instance
227 *
228 * Returns: The flag mask of @cl, a negative value in case of an error.
229 */
230 int scols_column_get_flags(const struct libscols_column *cl)
231 {
232 return cl->flags;
233 }
234
235 /**
236 * scols_column_get_header:
237 * @cl: a pointer to a struct libscols_column instance
238 *
239 * Returns: A pointer to a struct libscols_cell instance, representing the
240 * header info of column @cl or NULL in case of an error.
241 */
242 struct libscols_cell *scols_column_get_header(struct libscols_column *cl)
243 {
244 return &cl->header;
245 }
246
247 /**
248 * scols_column_set_name:
249 * @cl: a pointer to a struct libscols_column instance
250 * @name: column name
251 *
252 * Returns: 0, a negative value in case of an error.
253 *
254 * Since: 2.38
255 */
256 int scols_column_set_name(struct libscols_column *cl, const char *name)
257 {
258 struct libscols_cell *hr = scols_column_get_header(cl);
259
260 if (!hr)
261 return -EINVAL;
262
263 free(cl->shellvar);
264 cl->shellvar = NULL;
265
266 return scols_cell_set_data(hr, name);
267 }
268
269 /**
270 * scols_column_get_name:
271 * @cl: a pointer to a struct libscols_column instance
272 *
273 * Returns: A pointer to a column name, which is stored in column header
274 *
275 * Since: 2.38
276 */
277 const char *scols_column_get_name(struct libscols_column *cl)
278 {
279 return scols_cell_get_data(&cl->header);
280 }
281
282 /**
283 * scols_column_get_name_as_shellvar
284 * @cl: a pointer to a struct libscols_column instance
285 *
286 * Like scols_column_get_name(), but column name is modified to be compatible with shells
287 * requirements for variable names.
288 *
289 * Since: 2.38
290 */
291 const char *scols_column_get_name_as_shellvar(struct libscols_column *cl)
292 {
293 if (!cl->shellvar) {
294 const char *s, *name = scols_column_get_name(cl);
295 char *p;
296 size_t sz;
297
298 if (!name || !*name)
299 return NULL;
300
301 /* "1FOO%" --> "_1FOO_PCT */
302 sz = strlen(name) + 1 + 3;
303 p = cl->shellvar = calloc(1, sz + 1);
304 if (!cl->shellvar)
305 return NULL;
306
307 /* convert "1FOO" to "_1FOO" */
308 if (!isalpha(*name))
309 *p++ = '_';
310
311 /* replace all "bad" chars with "_" */
312 for (s = name; *s; s++)
313 *p++ = !isalnum(*s) ? '_' : *s;
314
315 if (!*s && *(s - 1) == '%') {
316 *p++ = 'P';
317 *p++ = 'C';
318 *p++ = 'T';
319 }
320 }
321 return cl->shellvar;
322 }
323
324
325 /**
326 * scols_column_set_color:
327 * @cl: a pointer to a struct libscols_column instance
328 * @color: color name or ESC sequence
329 *
330 * The default color for data cells and column header.
331 *
332 * If you want to set header specific color then use scols_column_get_header()
333 * and scols_cell_set_color().
334 *
335 * If you want to set data cell specific color the use scols_line_get_cell() +
336 * scols_cell_set_color().
337 *
338 * Returns: 0, a negative value in case of an error.
339 */
340 int scols_column_set_color(struct libscols_column *cl, const char *color)
341 {
342 if (color && !color_is_sequence(color)) {
343 char *seq = color_get_sequence(color);
344 if (!seq)
345 return -EINVAL;
346 free(cl->color);
347 cl->color = seq;
348 return 0;
349 }
350 return strdup_to_struct_member(cl, color, color);
351 }
352
353 /**
354 * scols_column_get_color:
355 * @cl: a pointer to a struct libscols_column instance
356 *
357 * Returns: The current color setting of the column @cl.
358 */
359 const char *scols_column_get_color(const struct libscols_column *cl)
360 {
361 return cl->color;
362 }
363
364 /**
365 * scols_wrapnl_nextchunk:
366 * @cl: a pointer to a struct libscols_column instance
367 * @data: string
368 * @userdata: callback private data
369 *
370 * This is built-in function for scols_column_set_wrapfunc(). This function
371 * terminates the current chunk by \0 and returns pointer to the begin of
372 * the next chunk. The chunks are based on \n.
373 *
374 * For example for data "AAA\nBBB\nCCC" the next chunk is "BBB".
375 *
376 * Returns: next chunk
377 *
378 * Since: 2.29
379 */
380 char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unused)),
381 char *data,
382 void *userdata __attribute__((unused)))
383 {
384 char *p = data ? strchr(data, '\n') : NULL;
385
386 if (p) {
387 *p = '\0';
388 return p + 1;
389 }
390 return NULL;
391 }
392
393 /**
394 * scols_wrapnl_chunksize:
395 * @cl: a pointer to a struct libscols_column instance
396 * @data: string
397 * @userdata: callback private data
398 *
399 * Analyzes @data and returns size of the largest chunk. The chunks are based
400 * on \n. For example for data "AAA\nBBB\nCCCC" the largest chunk size is 4.
401 *
402 * Note that the size has to be based on number of terminal cells rather than
403 * bytes to support multu-byte output.
404 *
405 * Returns: size of the largest chunk.
406 *
407 * Since: 2.29
408 */
409 size_t scols_wrapnl_chunksize(const struct libscols_column *cl __attribute__((unused)),
410 const char *data,
411 void *userdata __attribute__((unused)))
412 {
413 size_t sum = 0;
414
415 while (data && *data) {
416 const char *p;
417 size_t sz;
418
419 p = strchr(data, '\n');
420 if (p) {
421 sz = cl->table && scols_table_is_noencoding(cl->table) ?
422 mbs_nwidth(data, p - data) :
423 mbs_safe_nwidth(data, p - data, NULL);
424 p++;
425 } else {
426 sz = cl->table && scols_table_is_noencoding(cl->table) ?
427 mbs_width(data) :
428 mbs_safe_width(data);
429 }
430 sum = max(sum, sz);
431 data = p;
432 }
433
434 return sum;
435 }
436
437 /**
438 * scols_column_set_cmpfunc:
439 * @cl: column
440 * @cmp: pointer to compare function
441 * @data: private data for cmp function
442 *
443 * Returns: 0, a negative value in case of an error.
444 */
445 int scols_column_set_cmpfunc(struct libscols_column *cl,
446 int (*cmp)(struct libscols_cell *,
447 struct libscols_cell *,
448 void *),
449 void *data)
450 {
451 if (!cl)
452 return -EINVAL;
453
454 cl->cmpfunc = cmp;
455 cl->cmpfunc_data = data;
456 return 0;
457 }
458
459 /**
460 * scols_column_set_wrapfunc:
461 * @cl: a pointer to a struct libscols_column instance
462 * @wrap_chunksize: function to return size of the largest chink of data
463 * @wrap_nextchunk: function to return next zero terminated data
464 * @userdata: optional stuff for callbacks
465 *
466 * Extends SCOLS_FL_WRAP and can be used to set custom wrap function. The default
467 * is to wrap by column size, but you can create functions to wrap for example
468 * after \n or after words, etc.
469 *
470 * Returns: 0, a negative value in case of an error.
471 *
472 * Since: 2.29
473 */
474 int scols_column_set_wrapfunc(struct libscols_column *cl,
475 size_t (*wrap_chunksize)(const struct libscols_column *,
476 const char *,
477 void *),
478 char * (*wrap_nextchunk)(const struct libscols_column *,
479 char *,
480 void *),
481 void *userdata)
482 {
483 if (!cl)
484 return -EINVAL;
485
486 cl->wrap_nextchunk = wrap_nextchunk;
487 cl->wrap_chunksize = wrap_chunksize;
488 cl->wrapfunc_data = userdata;
489 return 0;
490 }
491
492 /**
493 * scols_column_set_safechars:
494 * @cl: a pointer to a struct libscols_column instance
495 * @safe: safe characters (e.g. "\n\t")
496 *
497 * Use for bytes you don't want to encode on output. This is for example
498 * necessary if you want to use custom wrap function based on \n, in this case
499 * you have to set "\n" as a safe char.
500 *
501 * Returns: 0, a negative value in case of an error.
502 *
503 * Since: 2.29
504 */
505 int scols_column_set_safechars(struct libscols_column *cl, const char *safe)
506 {
507 return strdup_to_struct_member(cl, safechars, safe);
508 }
509
510 /**
511 * scols_column_get_safechars:
512 * @cl: a pointer to a struct libscols_column instance
513 *
514 * Returns: safe chars
515 *
516 * Since: 2.29
517 */
518 const char *scols_column_get_safechars(const struct libscols_column *cl)
519 {
520 return cl->safechars;
521 }
522
523 /**
524 * scols_column_get_width:
525 * @cl: a pointer to a struct libscols_column instance
526 *
527 * Important note: the column width is unknown until library starts printing
528 * (width is calculated before printing). The function is usable for example in
529 * nextchunk() callback specified by scols_column_set_wrapfunc().
530 *
531 * See also scols_column_get_whint(), it returns wanted size (!= final size).
532 *
533 * Returns: column width
534 *
535 * Since: 2.29
536 */
537 size_t scols_column_get_width(const struct libscols_column *cl)
538 {
539 return cl->width;
540 }
541
542 /**
543 * scols_column_is_hidden:
544 * @cl: a pointer to a struct libscols_column instance
545 *
546 * Gets the value of @cl's flag hidden.
547 *
548 * Returns: 0 or 1
549 *
550 * Since: 2.27
551 */
552 int scols_column_is_hidden(const struct libscols_column *cl)
553 {
554 return cl->flags & SCOLS_FL_HIDDEN ? 1 : 0;
555 }
556
557 /**
558 * scols_column_is_trunc:
559 * @cl: a pointer to a struct libscols_column instance
560 *
561 * Gets the value of @cl's flag trunc.
562 *
563 * Returns: 0 or 1
564 */
565 int scols_column_is_trunc(const struct libscols_column *cl)
566 {
567 return cl->flags & SCOLS_FL_TRUNC ? 1 : 0;
568 }
569 /**
570 * scols_column_is_tree:
571 * @cl: a pointer to a struct libscols_column instance
572 *
573 * Gets the value of @cl's flag tree.
574 *
575 * Returns: 0 or 1
576 */
577 int scols_column_is_tree(const struct libscols_column *cl)
578 {
579 return cl->flags & SCOLS_FL_TREE ? 1 : 0;
580 }
581 /**
582 * scols_column_is_right:
583 * @cl: a pointer to a struct libscols_column instance
584 *
585 * Gets the value of @cl's flag right.
586 *
587 * Returns: 0 or 1
588 */
589 int scols_column_is_right(const struct libscols_column *cl)
590 {
591 return cl->flags & SCOLS_FL_RIGHT ? 1 : 0;
592 }
593 /**
594 * scols_column_is_strict_width:
595 * @cl: a pointer to a struct libscols_column instance
596 *
597 * Gets the value of @cl's flag strict_width.
598 *
599 * Returns: 0 or 1
600 */
601 int scols_column_is_strict_width(const struct libscols_column *cl)
602 {
603 return cl->flags & SCOLS_FL_STRICTWIDTH ? 1 : 0;
604 }
605 /**
606 * scols_column_is_noextremes:
607 * @cl: a pointer to a struct libscols_column instance
608 *
609 * Gets the value of @cl's flag no_extremes.
610 *
611 * Returns: 0 or 1
612 */
613 int scols_column_is_noextremes(const struct libscols_column *cl)
614 {
615 return cl->flags & SCOLS_FL_NOEXTREMES ? 1 : 0;
616 }
617 /**
618 * scols_column_is_wrap:
619 * @cl: a pointer to a struct libscols_column instance
620 *
621 * Gets the value of @cl's flag wrap.
622 *
623 * Returns: 0 or 1
624 *
625 * Since: 2.28
626 */
627 int scols_column_is_wrap(const struct libscols_column *cl)
628 {
629 return cl->flags & SCOLS_FL_WRAP ? 1 : 0;
630 }
631 /**
632 * scols_column_is_customwrap:
633 * @cl: a pointer to a struct libscols_column instance
634 *
635 * Returns: 0 or 1
636 *
637 * Since: 2.29
638 */
639 int scols_column_is_customwrap(const struct libscols_column *cl)
640 {
641 return (cl->flags & SCOLS_FL_WRAP)
642 && cl->wrap_chunksize
643 && cl->wrap_nextchunk ? 1 : 0;
644 }
645
646 /**
647 * scols_column_set_properties:
648 * @cl: a pointer to a struct libscols_column instance
649 * @opts: options string
650 *
651 * Set properties from string, the string is comma seprated list, like
652 * "trunc,right,json=number", ...
653 *
654 * Returns: 0 on success, <0 on error
655 *
656 * Since: 2.39
657 */
658 int scols_column_set_properties(struct libscols_column *cl, const char *opts)
659 {
660 char *str = (char *) opts;
661 char *name, *value;
662 size_t namesz, valuesz;
663 unsigned int flags = 0;
664 int rc = 0;
665
666 DBG(COL, ul_debugobj(cl, "apply properties '%s'", opts));
667
668 while (rc == 0
669 && !ul_optstr_next(&str, &name, &namesz, &value, &valuesz)) {
670
671 if (strncmp(name, "trunc", namesz) == 0)
672 flags |= SCOLS_FL_TRUNC;
673
674 else if (strncmp(name, "tree", namesz) == 0)
675 flags |= SCOLS_FL_TREE;
676
677 else if (strncmp(name, "right", namesz) == 0)
678 flags |= SCOLS_FL_RIGHT;
679
680 else if (strncmp(name, "strictwidth", namesz) == 0)
681 flags |= SCOLS_FL_STRICTWIDTH;
682
683 else if (strncmp(name, "noextremes", namesz) == 0)
684 flags |= SCOLS_FL_STRICTWIDTH;
685
686 else if (strncmp(name, "hidden", namesz) == 0)
687 flags |= SCOLS_FL_HIDDEN;
688
689 else if (strncmp(name, "wrap", namesz) == 0)
690 flags |= SCOLS_FL_WRAP;
691
692 else if (value && strncmp(name, "json", namesz) == 0) {
693
694 if (strncmp(value, "string", valuesz) == 0)
695 rc = scols_column_set_json_type(cl, SCOLS_JSON_STRING);
696 else if (strncmp(value, "number", valuesz) == 0)
697 rc = scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
698 else if (strncmp(value, "array-string", valuesz) == 0)
699 rc = scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING);
700 else if (strncmp(value, "array-number", valuesz) == 0)
701 rc = scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_NUMBER);
702 else if (strncmp(value, "boolean", valuesz) == 0)
703 rc = scols_column_set_json_type(cl, SCOLS_JSON_BOOLEAN);
704
705 } else if (value && strncmp(name, "width", namesz) == 0) {
706
707 char *end = NULL;
708 double x = strtod(value, &end);
709 if (errno || str == end)
710 return -EINVAL;
711
712 rc = scols_column_set_whint(cl, x);
713
714 } else if (value && strncmp(name, "color", namesz) == 0) {
715
716 char *x = strndup(value, valuesz);
717 if (x) {
718 scols_column_set_color(cl, x);
719 free(x);
720 }
721
722 } else if (value && strncmp(name, "name", namesz) == 0) {
723
724 char *x = strndup(value, valuesz);
725 if (x) {
726 scols_column_set_name(cl, x);
727 free(x);
728 }
729 }
730 }
731
732 if (!rc && flags)
733 rc = scols_column_set_flags(cl, flags);
734
735 return rc;
736 }
737