Skip to content

Commit 89a1704

Browse files
committed
lib/format.c: new humanize format !humanize[ ][.][width][minscale[maxscale]][i]
1 parent f40e9c3 commit 89a1704

File tree

2 files changed

+142
-42
lines changed

2 files changed

+142
-42
lines changed

include/xbps.h.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2380,10 +2380,11 @@ struct xbps_fmt;
23802380
*/
23812381
struct xbps_fmt_spec {
23822382
/**
2383+
* @private
23832384
* @var conversion
23842385
* @brief Output conversion.
23852386
*/
2386-
char conversion;
2387+
struct conversion *conversion;
23872388
/**
23882389
* @var fill
23892390
* @brief Padding character.

lib/format.c

Lines changed: 140 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -197,15 +197,44 @@ nexttok(const char **pos, struct strbuf *buf)
197197
return 0;
198198
}
199199

200+
struct conversion {
201+
enum { HUMANIZE = 1, STRMODE } type;
202+
union {
203+
struct humanize {
204+
unsigned width : 8;
205+
unsigned minscale : 8;
206+
unsigned maxscale : 8;
207+
bool decimal : 1;
208+
int flags;
209+
} humanize;
210+
};
211+
};
212+
200213
static int
201-
parse(const char **pos, struct strbuf *buf, struct xbps_fmt_spec *spec)
214+
parse_u(const char **pos, unsigned int *u)
215+
{
216+
char *e = NULL;
217+
long v;
218+
errno = 0;
219+
v = strtoul(*pos, &e, 10);
220+
if (errno != 0)
221+
return -errno;
222+
if (v > UINT_MAX)
223+
return -ERANGE;
224+
*u = v;
225+
*pos = e;
226+
return 0;
227+
}
228+
229+
static int
230+
parse(const char **pos, struct strbuf *buf, struct xbps_fmt_spec *spec, struct conversion *conversion)
202231
{
203232
const char *p = *pos;
204233
const char *e;
205234
int r;
206235
bool fill = false;
207236

208-
spec->conversion = '\0';
237+
spec->conversion = NULL;
209238
spec->fill = ' ';
210239
spec->align = '>';
211240
spec->sign = '-';
@@ -228,8 +257,53 @@ parse(const char **pos, struct strbuf *buf, struct xbps_fmt_spec *spec)
228257
p = e;
229258

230259
if (*p == '!') {
231-
spec->conversion = *++p;
232-
p++;
260+
if (strncmp(p+1, "humanize", sizeof("humanize") - 1) == 0) {
261+
/* humanize[ ][.][i][width][minscale[maxscale]] */
262+
const char *scale = "BKMGTPE";
263+
const char *p1;
264+
p += sizeof("humanize");
265+
conversion->type = HUMANIZE;
266+
if (*p != ':' && *p != '}') {
267+
conversion->humanize.flags = HN_NOSPACE;
268+
if (*p == ' ') {
269+
conversion->humanize.flags &= ~HN_NOSPACE;
270+
p++;
271+
}
272+
if (*p == '.') {
273+
conversion->humanize.flags |= HN_DECIMAL;
274+
p++;
275+
}
276+
if ((*p >= '0' && *p <= '9')) {
277+
unsigned width = 0;
278+
r = parse_u(&p, &width);
279+
if (r < 0)
280+
return r;
281+
conversion->humanize.width = width <= 12 ? width : 12;
282+
}
283+
if ((p1 = strchr(scale, *p))) {
284+
conversion->humanize.minscale = p1-scale+1;
285+
p++;
286+
if ((p1 = strchr(scale, *p))) {
287+
conversion->humanize.maxscale = p1-scale+1;
288+
p++;
289+
}
290+
}
291+
if (*p == 'i') {
292+
conversion->humanize.flags |= HN_IEC_PREFIXES;
293+
p++;
294+
}
295+
} else {
296+
/* default: !humanize .8Ki:8 */
297+
conversion->humanize.width = 8;
298+
conversion->humanize.minscale = 2;
299+
conversion->humanize.flags = HN_DECIMAL|HN_IEC_PREFIXES;
300+
}
301+
} else if (strncmp(p+1, "strmode", sizeof("strmode") - 1) == 0) {
302+
p += sizeof("strmode");
303+
conversion->type = STRMODE;
304+
} else {
305+
return -EINVAL;
306+
}
233307
}
234308

235309
if (*p == ':') {
@@ -248,35 +322,22 @@ parse(const char **pos, struct strbuf *buf, struct xbps_fmt_spec *spec)
248322
p += 1;
249323
}
250324
if ((*p >= '0' && *p <= '9')) {
251-
char *e1;
252-
long v;
253325
if (*p == '0') {
254326
if (!fill) {
255327
spec->fill = '0';
256328
spec->align = '=';
257329
}
258330
p++;
259331
}
260-
errno = 0;
261-
v = strtoul(p, &e1, 10);
262-
if (errno != 0)
263-
return -errno;
264-
if (v > INT_MAX)
265-
return -ERANGE;
266-
spec->width = v;
267-
p = e1;
332+
r = parse_u(&p, &spec->width);
333+
if (r < 0)
334+
return r;
268335
}
269336
if (*p == '.') {
270-
char *e1;
271-
long v;
272-
errno = 0;
273-
v = strtoul(p+1, &e1, 10);
274-
if (errno != 0)
275-
return -errno;
276-
if (v > 16)
277-
return -ERANGE;
278-
spec->precision = v;
279-
p = e1;
337+
p++;
338+
r = parse_u(&p, &spec->precision);
339+
if (r < 0)
340+
return r;
280341
}
281342
if (*p != '}')
282343
spec->type = *p++;
@@ -297,7 +358,8 @@ xbps_fmt_parse(const char *format)
297358
int r = 1;
298359

299360
for (;;) {
300-
struct xbps_fmt_spec spec;
361+
struct xbps_fmt_spec spec = {0};
362+
struct conversion conversion = {0};
301363
struct xbps_fmt *tmp;
302364
r = nexttok(&pos, &buf);
303365
if (r < 0)
@@ -318,7 +380,7 @@ xbps_fmt_parse(const char *format)
318380
memcpy(fmt[n].chunk->s, buf.mem, buf.len+1);
319381
break;
320382
case TVAR:
321-
r = parse(&pos, &buf, &spec);
383+
r = parse(&pos, &buf, &spec, &conversion);
322384
if (r < 0)
323385
goto err;
324386
fmt[n].var = calloc(1, sizeof(struct var)+buf.len+1);
@@ -327,6 +389,12 @@ xbps_fmt_parse(const char *format)
327389
fmt[n].common->type = TVAR;
328390
fmt[n].var->spec = spec;
329391
memcpy(fmt[n].var->s, buf.mem, buf.len+1);
392+
if (conversion.type) {
393+
fmt[n].var->spec.conversion = calloc(1, sizeof(struct conversion));
394+
if (!fmt[n].var->spec.conversion)
395+
goto err_errno;
396+
*fmt[n].var->spec.conversion = conversion;
397+
}
330398
break;
331399
}
332400
n++;
@@ -351,7 +419,10 @@ xbps_fmt_free(struct xbps_fmt *fmt)
351419
for (struct xbps_fmt *f = fmt; f->common; f++)
352420
switch (f->common->type) {
353421
case TTEXT: free(f->chunk); break;
354-
case TVAR: free(f->var); break;
422+
case TVAR:
423+
free(f->var->spec.conversion);
424+
free(f->var);
425+
break;
355426
}
356427
free(fmt);
357428
}
@@ -364,7 +435,8 @@ xbps_fmts(const char *format, xbps_fmt_cb *cb, void *data, FILE *fp)
364435
int r = 0;
365436

366437
for (;;) {
367-
struct xbps_fmt_spec spec;
438+
struct xbps_fmt_spec spec = {0};
439+
struct conversion conversion = {0};
368440
r = nexttok(&pos, &buf);
369441
if (r <= 0)
370442
goto out;
@@ -373,7 +445,7 @@ xbps_fmts(const char *format, xbps_fmt_cb *cb, void *data, FILE *fp)
373445
fprintf(fp, "%s", buf.mem);
374446
break;
375447
case TVAR:
376-
r = parse(&pos, &buf, &spec);
448+
r = parse(&pos, &buf, &spec, &conversion);
377449
if (r < 0)
378450
goto out;
379451
r = cb(fp, &spec, buf.mem, data);
@@ -427,14 +499,52 @@ xbps_fmt_string(const struct xbps_fmt_spec *spec, const char *str, size_t len, F
427499
return 0;
428500
}
429501

502+
static int
503+
humanize(const struct xbps_fmt_spec *spec, int64_t d, FILE *fp)
504+
{
505+
char buf[64];
506+
const struct humanize *h = &spec->conversion->humanize;
507+
int scale = 0;
508+
int width = h->width ? h->width : 8;
509+
int len;
510+
511+
if (h->minscale) {
512+
scale = humanize_number(buf, width, d, "B", HN_GETSCALE, h->flags);
513+
if (scale == -1)
514+
return -EINVAL;
515+
if (scale < h->minscale - 1)
516+
scale = h->minscale - 1;
517+
if (h->maxscale && scale > h->maxscale - 1)
518+
scale = h->maxscale - 1;
519+
} else if (scale == 0) {
520+
scale = HN_AUTOSCALE;
521+
}
522+
len = humanize_number(buf, width, d, "B", scale, h->flags);
523+
if (len == -1)
524+
return -EINVAL;
525+
return xbps_fmt_string(spec, buf, len, fp);
526+
}
527+
528+
static int
529+
tostrmode(const struct xbps_fmt_spec *spec UNUSED, int64_t d UNUSED, FILE *fp UNUSED)
530+
{
531+
return -ENOTSUP;
532+
}
533+
430534
int
431535
xbps_fmt_number(const struct xbps_fmt_spec *spec, int64_t d, FILE *fp)
432536
{
433537
char buf[64];
434538
struct xbps_fmt_spec strspec = *spec;
435539
const char *p = buf;
436540
int len;
437-
int scale;
541+
542+
if (spec->conversion) {
543+
switch (spec->conversion->type) {
544+
case HUMANIZE: return humanize(spec, d, fp);
545+
case STRMODE: return tostrmode(spec, d, fp);
546+
}
547+
}
438548

439549
if (strspec.align == '=')
440550
strspec.align = '>';
@@ -452,17 +562,6 @@ xbps_fmt_number(const struct xbps_fmt_spec *spec, int64_t d, FILE *fp)
452562
fputc(buf[0], fp);
453563
}
454564
break;
455-
case 'h':
456-
len = spec->width < sizeof(buf) ? spec->width : sizeof(buf);
457-
scale = humanize_number(buf, len, d, "B", HN_GETSCALE, HN_DECIMAL|HN_IEC_PREFIXES);
458-
if (scale == -1)
459-
return -EINVAL;
460-
if (spec->precision && (unsigned int)scale < 6-spec->precision)
461-
scale = 6-spec->precision;
462-
len = humanize_number(buf, len, d, "B", scale, HN_DECIMAL|HN_IEC_PREFIXES);
463-
if (scale == -1)
464-
return -EINVAL;
465-
break;
466565
case 'o': len = snprintf(buf, sizeof(buf), "%" PRIo64, d); break;
467566
case 'u': len = snprintf(buf, sizeof(buf), "%" PRIu64, d); break;
468567
case 'x': len = snprintf(buf, sizeof(buf), "%" PRIx64, d); break;

0 commit comments

Comments
 (0)