]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libsmartcols/src/column.c
8ebfa1ea27cb0d8e70faa21a330e411a39654b9b
[thirdparty/util-linux.git] / libsmartcols / src / column.c
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 && isalpha(*color)) {
343 color = color_sequence_from_colorname(color);
344 if (!color)
345 return -EINVAL;
346 }
347 return strdup_to_struct_member(cl, color, color);
348 }
349
350 /**
351 * scols_column_get_color:
352 * @cl: a pointer to a struct libscols_column instance
353 *
354 * Returns: The current color setting of the column @cl.
355 */
356 const char *scols_column_get_color(const struct libscols_column *cl)
357 {
358 return cl->color;
359 }
360
361 /**
362 * scols_wrapnl_nextchunk:
363 * @cl: a pointer to a struct libscols_column instance
364 * @data: string
365 * @userdata: callback private data
366 *
367 * This is built-in function for scols_column_set_wrapfunc(). This function
368 * terminates the current chunk by \0 and returns pointer to the begin of
369 * the next chunk. The chunks are based on \n.
370 *
371 * For example for data "AAA\nBBB\nCCC" the next chunk is "BBB".
372 *
373 * Returns: next chunk
374 *
375 * Since: 2.29
376 */
377 char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unused)),
378 char *data,
379 void *userdata __attribute__((unused)))
380 {
381 char *p = data ? strchr(data, '\n') : NULL;
382
383 if (p) {
384 *p = '\0';
385 return p + 1;
386 }
387 return NULL;
388 }
389
390 /**
391 * scols_wrapnl_chunksize:
392 * @cl: a pointer to a struct libscols_column instance
393 * @data: string
394 * @userdata: callback private data
395 *
396 * Analyzes @data and returns size of the largest chunk. The chunks are based
397 * on \n. For example for data "AAA\nBBB\nCCCC" the largest chunk size is 4.
398 *
399 * Note that the size has to be based on number of terminal cells rather than
400 * bytes to support multu-byte output.
401 *
402 * Returns: size of the largest chunk.
403 *
404 * Since: 2.29
405 */
406 size_t scols_wrapnl_chunksize(const struct libscols_column *cl __attribute__((unused)),
407 const char *data,
408 void *userdata __attribute__((unused)))
409 {
410 size_t sum = 0;
411
412 while (data && *data) {
413 const char *p;
414 size_t sz;
415
416 p = strchr(data, '\n');
417 if (p) {
418 sz = cl->table && scols_table_is_noencoding(cl->table) ?
419 mbs_nwidth(data, p - data) :
420 mbs_safe_nwidth(data, p - data, NULL);
421 p++;
422 } else {
423 sz = cl->table && scols_table_is_noencoding(cl->table) ?
424 mbs_width(data) :
425 mbs_safe_width(data);
426 }
427 sum = max(sum, sz);
428 data = p;
429 }
430
431 return sum;
432 }
433
434 /**
435 * scols_column_set_cmpfunc:
436 * @cl: column
437 * @cmp: pointer to compare function
438 * @data: private data for cmp function
439 *
440 * Returns: 0, a negative value in case of an error.
441 */
442 int scols_column_set_cmpfunc(struct libscols_column *cl,
443 int (*cmp)(struct libscols_cell *,
444 struct libscols_cell *,
445 void *),
446 void *data)
447 {
448 if (!cl)
449 return -EINVAL;
450
451 cl->cmpfunc = cmp;
452 cl->cmpfunc_data = data;
453 return 0;
454 }
455
456 /**
457 * scols_column_set_wrapfunc:
458 * @cl: a pointer to a struct libscols_column instance
459 * @wrap_chunksize: function to return size of the largest chink of data
460 * @wrap_nextchunk: function to return next zero terminated data
461 * @userdata: optional stuff for callbacks
462 *
463 * Extends SCOLS_FL_WRAP and can be used to set custom wrap function. The default
464 * is to wrap by column size, but you can create functions to wrap for example
465 * after \n or after words, etc.
466 *
467 * Returns: 0, a negative value in case of an error.
468 *
469 * Since: 2.29
470 */
471 int scols_column_set_wrapfunc(struct libscols_column *cl,
472 size_t (*wrap_chunksize)(const struct libscols_column *,
473 const char *,
474 void *),
475 char * (*wrap_nextchunk)(const struct libscols_column *,
476 char *,
477 void *),
478 void *userdata)
479 {
480 if (!cl)
481 return -EINVAL;
482
483 cl->wrap_nextchunk = wrap_nextchunk;
484 cl->wrap_chunksize = wrap_chunksize;
485 cl->wrapfunc_data = userdata;
486 return 0;
487 }
488
489 /**
490 * scols_column_set_safechars:
491 * @cl: a pointer to a struct libscols_column instance
492 * @safe: safe characters (e.g. "\n\t")
493 *
494 * Use for bytes you don't want to encode on output. This is for example
495 * necessary if you want to use custom wrap function based on \n, in this case
496 * you have to set "\n" as a safe char.
497 *
498 * Returns: 0, a negative value in case of an error.
499 *
500 * Since: 2.29
501 */
502 int scols_column_set_safechars(struct libscols_column *cl, const char *safe)
503 {
504 return strdup_to_struct_member(cl, safechars, safe);
505 }
506
507 /**
508 * scols_column_get_safechars:
509 * @cl: a pointer to a struct libscols_column instance
510 *
511 * Returns: safe chars
512 *
513 * Since: 2.29
514 */
515 const char *scols_column_get_safechars(const struct libscols_column *cl)
516 {
517 return cl->safechars;
518 }
519
520 /**
521 * scols_column_get_width:
522 * @cl: a pointer to a struct libscols_column instance
523 *
524 * Important note: the column width is unknown until library starts printing
525 * (width is calculated before printing). The function is usable for example in
526 * nextchunk() callback specified by scols_column_set_wrapfunc().
527 *
528 * See also scols_column_get_whint(), it returns wanted size (!= final size).
529 *
530 * Returns: column width
531 *
532 * Since: 2.29
533 */
534 size_t scols_column_get_width(const struct libscols_column *cl)
535 {
536 return cl->width;
537 }
538
539 /**
540 * scols_column_is_hidden:
541 * @cl: a pointer to a struct libscols_column instance
542 *
543 * Gets the value of @cl's flag hidden.
544 *
545 * Returns: 0 or 1
546 *
547 * Since: 2.27
548 */
549 int scols_column_is_hidden(const struct libscols_column *cl)
550 {
551 return cl->flags & SCOLS_FL_HIDDEN ? 1 : 0;
552 }
553
554 /**
555 * scols_column_is_trunc:
556 * @cl: a pointer to a struct libscols_column instance
557 *
558 * Gets the value of @cl's flag trunc.
559 *
560 * Returns: 0 or 1
561 */
562 int scols_column_is_trunc(const struct libscols_column *cl)
563 {
564 return cl->flags & SCOLS_FL_TRUNC ? 1 : 0;
565 }
566 /**
567 * scols_column_is_tree:
568 * @cl: a pointer to a struct libscols_column instance
569 *
570 * Gets the value of @cl's flag tree.
571 *
572 * Returns: 0 or 1
573 */
574 int scols_column_is_tree(const struct libscols_column *cl)
575 {
576 return cl->flags & SCOLS_FL_TREE ? 1 : 0;
577 }
578 /**
579 * scols_column_is_right:
580 * @cl: a pointer to a struct libscols_column instance
581 *
582 * Gets the value of @cl's flag right.
583 *
584 * Returns: 0 or 1
585 */
586 int scols_column_is_right(const struct libscols_column *cl)
587 {
588 return cl->flags & SCOLS_FL_RIGHT ? 1 : 0;
589 }
590 /**
591 * scols_column_is_strict_width:
592 * @cl: a pointer to a struct libscols_column instance
593 *
594 * Gets the value of @cl's flag strict_width.
595 *
596 * Returns: 0 or 1
597 */
598 int scols_column_is_strict_width(const struct libscols_column *cl)
599 {
600 return cl->flags & SCOLS_FL_STRICTWIDTH ? 1 : 0;
601 }
602 /**
603 * scols_column_is_noextremes:
604 * @cl: a pointer to a struct libscols_column instance
605 *
606 * Gets the value of @cl's flag no_extremes.
607 *
608 * Returns: 0 or 1
609 */
610 int scols_column_is_noextremes(const struct libscols_column *cl)
611 {
612 return cl->flags & SCOLS_FL_NOEXTREMES ? 1 : 0;
613 }
614 /**
615 * scols_column_is_wrap:
616 * @cl: a pointer to a struct libscols_column instance
617 *
618 * Gets the value of @cl's flag wrap.
619 *
620 * Returns: 0 or 1
621 *
622 * Since: 2.28
623 */
624 int scols_column_is_wrap(const struct libscols_column *cl)
625 {
626 return cl->flags & SCOLS_FL_WRAP ? 1 : 0;
627 }
628 /**
629 * scols_column_is_customwrap:
630 * @cl: a pointer to a struct libscols_column instance
631 *
632 * Returns: 0 or 1
633 *
634 * Since: 2.29
635 */
636 int scols_column_is_customwrap(const struct libscols_column *cl)
637 {
638 return (cl->flags & SCOLS_FL_WRAP)
639 && cl->wrap_chunksize
640 && cl->wrap_nextchunk ? 1 : 0;
641 }
642
643 /**
644 * scols_column_set_properties:
645 * @cl: a pointer to a struct libscols_column instance
646 * @opts: options string
647 *
648 * Set properties from string, the string is comma seprated list, like
649 * "trunc,right,json=number", ...
650 *
651 * Returns: 0 on success, <0 on error
652 *
653 * Since: 2.39
654 */
655 int scols_column_set_properties(struct libscols_column *cl, const char *opts)
656 {
657 char *str = (char *) opts;
658 char *name, *value;
659 size_t namesz, valuesz;
660 unsigned int flags = 0;
661 int rc = 0;
662
663 DBG(COL, ul_debugobj(cl, "apply properties '%s'", opts));
664
665 while (rc == 0
666 && !ul_optstr_next(&str, &name, &namesz, &value, &valuesz)) {
667
668 if (strncmp(name, "trunc", namesz) == 0)
669 flags |= SCOLS_FL_TRUNC;
670
671 else if (strncmp(name, "tree", namesz) == 0)
672 flags |= SCOLS_FL_TREE;
673
674 else if (strncmp(name, "right", namesz) == 0)
675 flags |= SCOLS_FL_RIGHT;
676
677 else if (strncmp(name, "strictwidth", namesz) == 0)
678 flags |= SCOLS_FL_STRICTWIDTH;
679
680 else if (strncmp(name, "noextremes", namesz) == 0)
681 flags |= SCOLS_FL_STRICTWIDTH;
682
683 else if (strncmp(name, "hidden", namesz) == 0)
684 flags |= SCOLS_FL_HIDDEN;
685
686 else if (strncmp(name, "wrap", namesz) == 0)
687 flags |= SCOLS_FL_WRAP;
688
689 else if (value && strncmp(name, "json", namesz) == 0) {
690
691 if (strncmp(value, "string", valuesz) == 0)
692 rc = scols_column_set_json_type(cl, SCOLS_JSON_STRING);
693 else if (strncmp(value, "number", valuesz) == 0)
694 rc = scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
695 else if (strncmp(value, "array-string", valuesz) == 0)
696 rc = scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING);
697 else if (strncmp(value, "array-number", valuesz) == 0)
698 rc = scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_NUMBER);
699 else if (strncmp(value, "boolean", valuesz) == 0)
700 rc = scols_column_set_json_type(cl, SCOLS_JSON_BOOLEAN);
701
702 } else if (value && strncmp(name, "width", namesz) == 0) {
703
704 char *end = NULL;
705 double x = strtod(value, &end);
706 if (errno || str == end)
707 return -EINVAL;
708
709 rc = scols_column_set_whint(cl, x);
710
711 } else if (value && strncmp(name, "color", namesz) == 0) {
712
713 char *x = strndup(value, valuesz);
714 if (x) {
715 scols_column_set_color(cl, x);
716 free(x);
717 }
718
719 } else if (value && strncmp(name, "name", namesz) == 0) {
720
721 char *x = strndup(value, valuesz);
722 if (x) {
723 scols_column_set_name(cl, x);
724 free(x);
725 }
726 }
727 }
728
729 if (!rc && flags)
730 rc = scols_column_set_flags(cl, flags);
731
732 return rc;
733 }
734