2 * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file format.c Functions for pretty-printing numbers and strings. */
9 #include <dirent.h> /* readdir() */
11 #include "gcc-compat.h"
16 enum alignment {ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER};
24 struct string_format {
33 struct string_format sf;
35 struct const_string cs;
39 struct atom *atom_ptr;
46 struct format_item **items;
55 static const char units[] = "bkmgth KMGTH";
57 * In a format string, find the next occurrence of %(atom).
59 static char *find_next(char *cp)
63 /* %( is the start of an atom;
64 * %% is a quoted per-cent.
68 else if (cp[1] == '%')
69 cp++; /* skip over two % */
70 /* otherwise this is a singleton, literal % */
77 static struct format_item *make_const_string(char *cp, char *ep)
79 struct format_item *fi = malloc(sizeof *fi);
84 op = fi->af.cs.string = malloc(fi->width + 1);
85 while (*cp && (!ep || cp < ep)) {
86 if (*cp == '%' && cp[1] == '%') {
98 static struct format_item *parse_atom(char *ap, struct atom *atoms)
100 struct format_item *fi = NULL;
102 char *col, *ep = strchr(ap, ')');
103 char *err_msg = "malformed format string";
107 col = memchr(ap, ':', ep - ap);
112 for (i = 0; atoms[i].name; i++) {
114 struct atom *a = atoms + i;
115 if (strlen(a->name) != len)
117 if (strncmp(a->name, ap, len))
119 fi = malloc(sizeof(*fi));
125 fi->af.sf.align = ALIGN_LEFT;
128 fi->af.nf.align = ALIGN_RIGHT;
129 fi->af.nf.unit = ' ';
130 fi->af.nf.supress_unit = 0;
133 fi->af.nf.align = ALIGN_RIGHT;
134 fi->af.nf.unit = 'H';
135 fi->af.nf.supress_unit = 0;
138 fi->af.nf.align = ALIGN_RIGHT;
139 fi->af.nf.unit = 'h';
140 fi->af.nf.supress_unit = 0;
148 fi->af.sf.align = ALIGN_LEFT;
151 fi->af.nf.align = ALIGN_RIGHT;
154 fi->af.nf.align = ALIGN_CENTER;
156 case ':': /* no alignment spec is OK */
160 err_msg = "bad alignment spec";
170 err_msg = "trailing garbage after alignment spec";
175 sscanf(col + 1, "%u%n", &fi->width, &n);
177 switch (col[1 + n]) {
184 err_msg = "trailing garbage after width spec";
187 if (a->type != AT_SIZE && a->type != AT_COUNT) {
188 err_msg = "no unit allowed here";
192 fi->af.nf.supress_unit = 1;
195 for (j = 0; units[j]; j++) {
196 if (units[j] != col[1])
198 fi->af.nf.unit = units[j];
202 err_msg = "bad unit spec";
206 err_msg = "trailing garbage after unit spec";
211 err_msg = "invalid atom";
213 ERROR_LOG("%s\n", err_msg);
220 struct format_info *parse_format_string(char *fmt, struct atom *atoms)
224 struct format_info *info = malloc(sizeof(*info));
228 for (cp = fmt, num_items = 0; *cp && (ap = find_next(cp));
229 cp = ep + 1, num_items++) {
231 info->items = realloc(info->items,
232 (num_items + 1) * sizeof(struct format_info *));
233 info->items[num_items] = make_const_string(cp, ap);
236 info->items = realloc(info->items, (num_items + 1) * sizeof(struct format_info *));
237 info->items[num_items] = parse_atom(ap + 2, atoms);
238 if (!info->items[num_items]) {
242 ep = strchr(ap, ')');
245 ep = cp + strlen(cp);
246 info->items = realloc(info->items, (num_items + 1) * sizeof(struct format_info *));
247 info->items[num_items] = make_const_string(cp, ep);
250 info->items = realloc(info->items, (num_items + 1) * sizeof(struct format_info *));
251 info->items[num_items] = NULL;
254 for (; num_items >= 0; num_items--) {
255 if (!info->items[num_items]->atom_ptr)
256 free(info->items[num_items]->af.cs.string);
257 free(info->items[num_items]);
264 void free_format_info(struct format_info *info)
267 struct format_item *item;
269 for (i = 0; (item = info->items[i]); i++) {
271 free(item->af.cs.string);
272 free(info->items[i]);
278 static char *align_string(const char *src, unsigned int width, enum alignment align)
283 return adu_strdup(src);
284 if (align == ALIGN_LEFT)
285 return make_message("%-*s", width, src);
286 if (align == ALIGN_RIGHT)
287 return make_message("%*s", width, src);
289 return make_message("%*s%*s", (width + len) / 2, src,
290 width - (width + len) / 2, "");
293 /** Compute the number of (decimal) digits of a number. */
294 #define GET_NUM_DIGITS(x, num) { \
295 typeof((x)) _tmp = x; \
298 while ((_tmp) > 9) { \
304 static long long unsigned normalize_number(long long unsigned num, char unit,
305 char *effective_unit)
307 long long unsigned normalized_num;
311 *effective_unit = ' ';
312 else if (num < 1024ULL * 1024ULL)
313 *effective_unit = 'k';
314 else if (num < 1024ULL * 1024ULL * 1024ULL)
315 *effective_unit = 'm';
316 else if (num < 1024ULL * 1024ULL * 1024ULL * 1024ULL)
317 *effective_unit = 'g';
319 *effective_unit = 't';
320 } else if (unit == 'H') {
322 *effective_unit = ' ';
323 else if (num < 1000ULL * 1000ULL)
324 *effective_unit = 'K';
325 else if (num < 1000ULL * 1000ULL * 1000ULL)
326 *effective_unit = 'M';
327 else if (num < 1000ULL * 1000ULL * 1000ULL * 1000ULL)
328 *effective_unit = 'G';
330 *effective_unit = 'T';
332 *effective_unit = unit;
333 switch (*effective_unit) {
335 case 'b': normalized_num = num; break;
336 case 'k': normalized_num = num / 1024; break;
337 case 'm': normalized_num = num / 1024 / 1024; break;
338 case 'g': normalized_num = num / 1024 / 1024 / 1024; break;
339 case 't': normalized_num = num / 1024 / 1024 / 1024 / 1024; break;
340 case 'K': normalized_num = num / 1000; break;
341 case 'M': normalized_num = num / 1000 / 1000; break;
342 case 'G': normalized_num = num / 1000 / 1000 / 1000; break;
343 case 'T': normalized_num = num / 1000 / 1000 / 1000 / 1000; break;
345 EMERG_LOG("BUG: invalid unit %c\n", *effective_unit);
348 return normalized_num;
351 static void get_unit_postfix(struct num_format *nf, char eu, enum atom_type type,
354 if (nf->supress_unit) {
362 if (nf->unit != 'h' && nf->unit != 'H')
366 postfix[0] = 'b'; /* bytes */
369 static char *align_unsigned_int(long long unsigned num, unsigned int width,
370 struct num_format *nf, enum atom_type type)
372 char eu; /* effective unit */
373 long long unsigned nnum = normalize_number(num, nf->unit, &eu);
374 char postfix[2] = "\0\0";
377 get_unit_postfix(nf, eu, type, postfix);
379 return make_message("%llu%s", nnum, postfix);
380 GET_NUM_DIGITS(nnum, &len);
381 len += strlen(postfix);
384 if (nf->align == ALIGN_LEFT)
385 return make_message("%llu%s%*s", nnum, postfix, width - len, "");
386 if (nf->align == ALIGN_RIGHT)
387 return make_message("%*s%llu%s", width - len, "", nnum, postfix);
388 return make_message("%*llu%s%*s", (width + len) / 2,
389 nnum, postfix, width - (width + len) / 2, "");
392 char *format_items(struct format_info *info, union atom_value *values)
397 for (i = 0; info->items[i]; i++) {
399 struct format_item *fi = info->items[i];
400 union atom_format *af = &fi->af;
401 enum alignment align;
406 if (!fi->atom_ptr) { /* const string */
407 buf = adu_strcat(buf, af->cs.string);
412 idx = a - info->atoms;
414 if (type == AT_STRING) {
415 align = af->sf.align;
416 val = align_string(values[idx].string_value, fi->width, align);
419 align = af->nf.align;
421 val = align_unsigned_int(values[idx].num_value,
422 fi->width, &af->nf, type);
424 buf = adu_strcat(buf, val);