2 * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file format.c \brief Functions for pretty-printing numbers and strings. */
9 #include <dirent.h> /* readdir() */
11 #include "gcc-compat.h"
17 /** The three different alignment types. */
18 enum alignment {ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER};
26 struct string_format {
35 struct string_format sf;
37 struct const_string cs;
41 struct atom *atom_ptr;
48 struct format_item **items;
57 static const char units[] = "bkmgth KMGTH";
59 * In a format string, find the next occurrence of %(atom).
61 static char *find_next(char *cp)
65 /* %( is the start of an atom;
66 * %% is a quoted per-cent.
70 else if (cp[1] == '%')
71 cp++; /* skip over two % */
72 /* otherwise this is a singleton, literal % */
79 static struct format_item *make_const_string(char *cp, char *ep)
81 struct format_item *fi = malloc(sizeof *fi);
86 op = fi->af.cs.string = malloc(fi->width + 1);
87 while (*cp && (!ep || cp < ep)) {
88 if (*cp == '%' && cp[1] == '%') {
100 static int parse_atom(char *ap, struct atom *atoms, struct format_item **result)
102 struct format_item *fi = NULL;
104 char *col, *ep = strchr(ap, ')');
105 int ret = -E_MALFORMED_FORMAT;
109 col = memchr(ap, ':', ep - ap);
114 for (i = 0; atoms[i].name; i++) {
116 struct atom *a = atoms + i;
117 if (strlen(a->name) != len)
119 if (strncmp(a->name, ap, len))
121 fi = malloc(sizeof(*fi));
127 fi->af.sf.align = ALIGN_LEFT;
130 fi->af.nf.align = ALIGN_RIGHT;
131 fi->af.nf.unit = ' ';
132 fi->af.nf.supress_unit = 0;
135 fi->af.nf.align = ALIGN_RIGHT;
136 fi->af.nf.unit = 'H';
137 fi->af.nf.supress_unit = 0;
140 fi->af.nf.align = ALIGN_RIGHT;
141 fi->af.nf.unit = 'h';
142 fi->af.nf.supress_unit = 0;
150 fi->af.sf.align = ALIGN_LEFT;
153 fi->af.nf.align = ALIGN_RIGHT;
156 fi->af.nf.align = ALIGN_CENTER;
158 case ':': /* no alignment spec is OK */
162 ret = -E_BAD_ALIGN_SPEC;
172 ret = -E_TRAILING_GARBAGE;
177 sscanf(col + 1, "%u%n", &fi->width, &n);
179 switch (col[1 + n]) {
186 ret = -E_TRAILING_GARBAGE;
189 if (a->type != AT_SIZE && a->type != AT_COUNT) {
194 fi->af.nf.supress_unit = 1;
197 for (j = 0; units[j]; j++) {
198 if (units[j] != col[1])
200 fi->af.nf.unit = units[j];
208 ret = -E_TRAILING_GARBAGE;
223 * Parse the given string according to the list of given atoms.
225 * \param fmt The format string.
226 * \param atoms The array of valid atoms.
227 * \param result Points to a format_info structure for later use.
231 int parse_format_string(char *fmt, struct atom *atoms, struct format_info **result)
235 struct format_info *info = malloc(sizeof(*info));
239 for (cp = fmt, num_items = 0; *cp && (ap = find_next(cp));
240 cp = ep + 1, num_items++) {
242 info->items = realloc(info->items,
243 (num_items + 1) * sizeof(struct format_info *));
244 info->items[num_items] = make_const_string(cp, ap);
247 info->items = realloc(info->items, (num_items + 1) * sizeof(struct format_info *));
248 ret = parse_atom(ap + 2, atoms, info->items + num_items);
253 ep = strchr(ap, ')');
256 ep = cp + strlen(cp);
257 info->items = realloc(info->items, (num_items + 1) * sizeof(struct format_info *));
258 info->items[num_items] = make_const_string(cp, ep);
261 info->items = realloc(info->items, (num_items + 1) * sizeof(struct format_info *));
262 info->items[num_items] = NULL;
266 for (; num_items >= 0; num_items--) {
267 if (!info->items[num_items]->atom_ptr)
268 free(info->items[num_items]->af.cs.string);
269 free(info->items[num_items]);
278 * Free a struct of type \a format_info.
280 * \param info Pointer to the format info to be freed.
282 * It's OK to pass a \p NULL pointer to this function in which case the
283 * function does nothing.
285 void free_format_info(struct format_info *info)
288 struct format_item *item;
293 for (i = 0; (item = info->items[i]); i++) {
295 free(item->af.cs.string);
296 free(info->items[i]);
302 static char *align_string(const char *src, unsigned int width, enum alignment align)
307 return adu_strdup(src);
308 if (align == ALIGN_LEFT)
309 return make_message("%-*s", width, src);
310 if (align == ALIGN_RIGHT)
311 return make_message("%*s", width, src);
313 return make_message("%*s%*s", (width + len) / 2, src,
314 width - (width + len) / 2, "");
317 /** Compute the number of (decimal) digits of a number. */
318 #define GET_NUM_DIGITS(x, num) { \
319 typeof((x)) _tmp = x; \
322 while ((_tmp) > 9) { \
328 static long long unsigned normalize_number(long long unsigned num, char unit,
329 char *effective_unit)
331 long long unsigned normalized_num;
335 *effective_unit = ' ';
336 else if (num < 1024ULL * 1024ULL)
337 *effective_unit = 'k';
338 else if (num < 1024ULL * 1024ULL * 1024ULL)
339 *effective_unit = 'm';
340 else if (num < 1024ULL * 1024ULL * 1024ULL * 1024ULL)
341 *effective_unit = 'g';
343 *effective_unit = 't';
344 } else if (unit == 'H') {
346 *effective_unit = ' ';
347 else if (num < 1000ULL * 1000ULL)
348 *effective_unit = 'K';
349 else if (num < 1000ULL * 1000ULL * 1000ULL)
350 *effective_unit = 'M';
351 else if (num < 1000ULL * 1000ULL * 1000ULL * 1000ULL)
352 *effective_unit = 'G';
354 *effective_unit = 'T';
356 *effective_unit = unit;
357 switch (*effective_unit) {
359 case 'b': normalized_num = num; break;
360 case 'k': normalized_num = num / 1024; break;
361 case 'm': normalized_num = num / 1024 / 1024; break;
362 case 'g': normalized_num = num / 1024 / 1024 / 1024; break;
363 case 't': normalized_num = num / 1024 / 1024 / 1024 / 1024; break;
364 case 'K': normalized_num = num / 1000; break;
365 case 'M': normalized_num = num / 1000 / 1000; break;
366 case 'G': normalized_num = num / 1000 / 1000 / 1000; break;
367 case 'T': normalized_num = num / 1000 / 1000 / 1000 / 1000; break;
369 EMERG_LOG("BUG: invalid unit %c\n", *effective_unit);
372 return normalized_num;
375 static void get_unit_postfix(struct num_format *nf, char eu, enum atom_type type,
378 if (nf->supress_unit) {
386 if (nf->unit != 'h' && nf->unit != 'H')
390 postfix[0] = 'b'; /* bytes */
393 static char *align_unsigned_int(long long unsigned num, unsigned int width,
394 struct num_format *nf, enum atom_type type)
396 char eu; /* effective unit */
397 long long unsigned nnum = normalize_number(num, nf->unit, &eu);
398 char postfix[2] = "\0\0";
401 get_unit_postfix(nf, eu, type, postfix);
403 return make_message("%llu%s", nnum, postfix);
404 GET_NUM_DIGITS(nnum, &len);
405 len += strlen(postfix);
408 if (nf->align == ALIGN_LEFT)
409 return make_message("%llu%s%*s", nnum, postfix, width - len, "");
410 if (nf->align == ALIGN_RIGHT)
411 return make_message("%*s%llu%s", width - len, "", nnum, postfix);
412 return make_message("%*llu%s%*s", (width + len) / 2,
413 nnum, postfix, width - (width + len) / 2, "");
417 * Pretty-format the given values according to \a info.
419 * \param info The formating information.
420 * \param values The contents of the atoms.
422 * \return A string that must be freed by the caller.
424 char *format_items(struct format_info *info, union atom_value *values)
431 for (i = 0; info->items[i]; i++) {
433 struct format_item *fi = info->items[i];
434 union atom_format *af = &fi->af;
435 enum alignment align;
440 if (!fi->atom_ptr) { /* const string */
441 buf = adu_strcat(buf, af->cs.string);
446 idx = a - info->atoms;
448 if (type == AT_STRING) {
449 align = af->sf.align;
450 val = align_string(values[idx].string_value, fi->width, align);
453 align = af->nf.align;
455 val = align_unsigned_int(values[idx].num_value,
456 fi->width, &af->nf, type);
458 buf = adu_strcat(buf, val);
462 buf = adu_strdup("");