@@ -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+
200213static 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+
430534int
431535xbps_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