Skip to content

Commit 2eee2ba

Browse files
committed
lib/format.c: add optional default to format string variables
1 parent 754fae2 commit 2eee2ba

File tree

4 files changed

+183
-27
lines changed

4 files changed

+183
-27
lines changed

bin/xbps-query/xbps-query.1

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,17 @@ xbps 484 KB
301301
.Pp
302302
Format strings are parsed by the following EBNF:
303303
.Bd -literal
304-
<grammar> ::= (text | escape | substitution)*
305-
<text> ::= [^\\{}]+ -- literal text chunk
306-
<escape> ::= "\\" [abfnrtv0] -- POSIX-like espace sequence
307-
| "\\{" | "\\}" -- escaped "{" and "}"
304+
<grammar> ::= (prefix | "\\" (escape|[{}]) | substitution)*
305+
<prefix> ::= [^\\{}]+ -- Literal text chunk.
306+
<escape> ::= [abfnrtv0] -- POSIX-like espace character.
308307

309-
<substitution> ::= "{" variable ["!" conversion] [":" format] "}"
308+
<substitution> ::= "{" variable ["?" default] ["!" conversion] [":" format] "}"
310309
<variable> ::= [a-zA-Z0-9_-]
311310

311+
<default> ::= ([-]?[0-9]+) -- default number.
312+
| "true" | "false" -- default boolean.
313+
| ('"' (("\\" (escape|'"')) | [^"])* '"') -- default string.
314+
312315
<conversion> ::= humanize | strmode
313316

314317
-- Convert inode status information into a symbolic string

include/xbps.h.in

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2370,7 +2370,7 @@ xbps_plist_dictionary_from_file(const char *path);
23702370

23712371
/**
23722372
* @struct xbps_fmt xbps.h "xbps.h"
2373-
* @brief Structure of parsed format string.
2373+
* @brief Structure of parsed format string variable.
23742374
*/
23752375
struct xbps_fmt {
23762376
/**
@@ -2384,6 +2384,11 @@ struct xbps_fmt {
23842384
* @brief Variable name.
23852385
*/
23862386
char *var;
2387+
/**
2388+
* @var def
2389+
* @brief Default value.
2390+
*/
2391+
struct xbps_fmt_def *def;
23872392
/**
23882393
* @var conv
23892394
* @brief Format conversion.
@@ -2397,7 +2402,24 @@ struct xbps_fmt {
23972402
};
23982403

23992404
/**
2400-
* @struct xbps_fmt xbps.h "xbps.h"
2405+
* @struct xbps_fmt_def xbps.h "xbps.h"
2406+
* @brief Structure of parsed format specifier.
2407+
*/
2408+
struct xbps_fmt_def {
2409+
enum {
2410+
XBPS_FMT_DEF_STR = 1,
2411+
XBPS_FMT_DEF_NUM,
2412+
XBPS_FMT_DEF_BOOL,
2413+
} type;
2414+
union {
2415+
char *str;
2416+
int64_t num;
2417+
bool boolean;
2418+
} val;
2419+
};
2420+
2421+
/**
2422+
* @struct xbps_fmt_spec xbps.h "xbps.h"
24012423
* @brief Structure of parsed format specifier.
24022424
*/
24032425
struct xbps_fmt_spec {

lib/format.c

Lines changed: 149 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -159,19 +159,6 @@ nexttok(const char **pos, struct strbuf *buf)
159159
return 0;
160160
}
161161

162-
struct xbps_fmt_conv {
163-
enum { HUMANIZE = 1, STRMODE } type;
164-
union {
165-
struct humanize {
166-
unsigned width : 8;
167-
unsigned minscale : 8;
168-
unsigned maxscale : 8;
169-
bool decimal : 1;
170-
int flags;
171-
} humanize;
172-
};
173-
};
174-
175162
static int
176163
parse_u(const char **pos, unsigned int *u)
177164
{
@@ -188,6 +175,129 @@ parse_u(const char **pos, unsigned int *u)
188175
return 0;
189176
}
190177

178+
static int
179+
parse_d(const char **pos, int64_t *d)
180+
{
181+
char *e = NULL;
182+
long v;
183+
errno = 0;
184+
v = strtol(*pos, &e, 10);
185+
if (errno != 0)
186+
return -errno;
187+
if (v > UINT_MAX)
188+
return -ERANGE;
189+
*d = v;
190+
*pos = e;
191+
return 0;
192+
}
193+
194+
static int
195+
parse_default(const char **pos, struct xbps_fmt *fmt, struct strbuf *buf,
196+
struct xbps_fmt_def *def_storage)
197+
{
198+
struct strbuf buf2 = {0};
199+
struct xbps_fmt_def *def;
200+
const char *p = *pos;
201+
char *str = NULL;
202+
int r;
203+
204+
if (*p++ != '?')
205+
return 0;
206+
if (!def_storage) {
207+
fmt->def = def = calloc(1, sizeof(*def));
208+
if (!def)
209+
return -errno;
210+
} else {
211+
fmt->def = def = def_storage;
212+
}
213+
214+
if ((*p >= '0' && *p <= '9') || *p == '-') {
215+
r = parse_d(&p, &def->val.num);
216+
if (r < 0)
217+
return r;
218+
def->type = XBPS_FMT_DEF_NUM;
219+
*pos = p;
220+
return 0;
221+
} else if (strncmp(p, "true", sizeof("true") - 1) == 0) {
222+
*pos = p + sizeof("true") - 1;
223+
def->type = XBPS_FMT_DEF_BOOL;
224+
def->val.boolean = true;
225+
return 0;
226+
} else if (strncmp(p, "false", sizeof("false") - 1) == 0) {
227+
*pos = p + sizeof("false") - 1;
228+
def->type = XBPS_FMT_DEF_BOOL;
229+
def->val.boolean = false;
230+
return 0;
231+
}
232+
233+
if (*p++ != '"')
234+
return -EINVAL;
235+
236+
if (!buf) {
237+
buf = &buf2;
238+
} else {
239+
r = strbuf_putc(buf, '\0');
240+
if (r < 0)
241+
return r;
242+
str = buf->mem + buf->len;
243+
}
244+
for (; *p && *p != '"'; p++) {
245+
switch (*p) {
246+
case '\\':
247+
switch (*++p) {
248+
case '\\': r = strbuf_putc(buf, '\\'); break;
249+
case 'a': r = strbuf_putc(buf, '\a'); break;
250+
case 'b': r = strbuf_putc(buf, '\b'); break;
251+
case 'f': r = strbuf_putc(buf, '\f'); break;
252+
case 'n': r = strbuf_putc(buf, '\n'); break;
253+
case 'r': r = strbuf_putc(buf, '\r'); break;
254+
case 't': r = strbuf_putc(buf, '\t'); break;
255+
case '0': r = strbuf_putc(buf, '\0'); break;
256+
case '"': r = strbuf_putc(buf, '"'); break;
257+
default: r = -EINVAL;
258+
}
259+
break;
260+
default:
261+
r = strbuf_putc(buf, *p);
262+
}
263+
if (r < 0)
264+
goto err;
265+
}
266+
if (*p++ != '"') {
267+
r = -EINVAL;
268+
goto err;
269+
}
270+
*pos = p;
271+
def->type = XBPS_FMT_DEF_STR;
272+
if (buf == &buf2) {
273+
def->val.str = strdup(buf2.mem);
274+
if (!def->val.str) {
275+
r = -errno;
276+
goto err;
277+
}
278+
strbuf_release(&buf2);
279+
} else {
280+
def->val.str = str;
281+
}
282+
return 0;
283+
err:
284+
strbuf_release(&buf2);
285+
return r;
286+
}
287+
288+
struct xbps_fmt_conv {
289+
enum { HUMANIZE = 1, STRMODE } type;
290+
union {
291+
struct humanize {
292+
unsigned width : 8;
293+
unsigned minscale : 8;
294+
unsigned maxscale : 8;
295+
bool decimal : 1;
296+
int flags;
297+
} humanize;
298+
};
299+
};
300+
191301
static int
192302
parse_humanize(const char **pos, struct humanize *humanize)
193303
{
@@ -346,6 +456,7 @@ parse_spec(const char **pos, struct xbps_fmt *fmt, struct xbps_fmt_spec *spec_st
346456
static int
347457
parse(const char **pos, struct xbps_fmt *fmt,
348458
struct strbuf *buf,
459+
struct xbps_fmt_def *def_storage,
349460
struct xbps_fmt_conv *conv_storage,
350461
struct xbps_fmt_spec *spec_storage)
351462
{
@@ -357,7 +468,7 @@ parse(const char **pos, struct xbps_fmt *fmt,
357468
return -EINVAL;
358469
p++;
359470

360-
/* var ::= '{' name [conversion][format_spec] '}' */
471+
/* var ::= '{' name [default][conversion][format_spec] '}' */
361472

362473
/* name ::= [a-zA-Z0-9_-]+ */
363474
for (e = p; (*e >= 'a' && *e <= 'z') ||
@@ -380,6 +491,11 @@ parse(const char **pos, struct xbps_fmt *fmt,
380491
}
381492
p = e;
382493

494+
/* default ::= ['?' ...] */
495+
r = parse_default(&p, fmt, buf, def_storage);
496+
if (r < 0)
497+
return r;
498+
383499
/* conversion ::= ['!' ...] */
384500
r = parse_conversion(&p, fmt, conv_storage);
385501
if (r < 0)
@@ -426,7 +542,7 @@ xbps_fmt_parse(const char *format)
426542
t = nexttok(&pos, &buf);
427543
}
428544
if (t == TVAR) {
429-
r = parse(&pos, &fmt[n], NULL, NULL, NULL);
545+
r = parse(&pos, &fmt[n], NULL, NULL, NULL, NULL);
430546
if (r < 0)
431547
goto err;
432548
}
@@ -453,6 +569,9 @@ xbps_fmt_free(struct xbps_fmt *fmt)
453569
for (struct xbps_fmt *f = fmt; f->prefix || f->var; f++) {
454570
free(f->prefix);
455571
free(f->var);
572+
if (f->def && f->def->type == XBPS_FMT_DEF_STR)
573+
free(f->def->val.str);
574+
free(f->def);
456575
free(f->spec);
457576
free(f->conv);
458577
}
@@ -477,10 +596,11 @@ xbps_fmts(const char *format, xbps_fmt_cb *cb, void *data, FILE *fp)
477596
t = nexttok(&pos, &buf);
478597
}
479598
if (t == TVAR) {
480-
struct xbps_fmt_spec spec = {0};
599+
struct xbps_fmt_def def = {0};
481600
struct xbps_fmt_conv conv = {0};
601+
struct xbps_fmt_spec spec = {0};
482602
struct xbps_fmt fmt = { .var = buf.mem };
483-
r = parse(&pos, &fmt, &buf, &conv, &spec);
603+
r = parse(&pos, &fmt, &buf, &def, &conv, &spec);
484604
if (r < 0)
485605
goto out;
486606
r = cb(fp, &fmt, data);
@@ -617,7 +737,18 @@ xbps_fmt_print_object(const struct xbps_fmt *fmt, xbps_object_t obj, FILE *fp)
617737
return xbps_fmt_print_string(fmt, xbps_string_cstring_nocopy(obj),
618738
xbps_string_size(obj), fp);
619739
case XBPS_TYPE_UNKNOWN:
620-
return xbps_fmt_print_string(fmt, "(null)", 0, fp);
740+
if (fmt->def) {
741+
struct xbps_fmt_def *def = fmt->def;
742+
switch (fmt->def->type) {
743+
case XBPS_FMT_DEF_BOOL:
744+
return xbps_fmt_print_string(fmt, def->val.boolean ?
745+
"true" : "false", 0, fp);
746+
case XBPS_FMT_DEF_STR:
747+
return xbps_fmt_print_string(fmt, def->val.str, 0, fp);
748+
case XBPS_FMT_DEF_NUM:
749+
return xbps_fmt_print_number(fmt, def->val.num, fp);
750+
}
751+
}
621752
default:
622753
break;
623754
}

tests/xbps/libxbps/fmt/main.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,10 @@ ATF_TC_BODY(xbps_fmt_dictionary, tc)
138138
ATF_REQUIRE(dict = xbps_dictionary_create());
139139
ATF_REQUIRE(xbps_dictionary_set_cstring_nocopy(dict, "string", "s"));
140140
ATF_REQUIRE(xbps_dictionary_set_int64(dict, "number", 1));
141-
ATF_REQUIRE(fmt = xbps_fmt_parse(">{string} {number} {number!humanize}<"));
141+
ATF_REQUIRE(fmt = xbps_fmt_parse(">{string} {number} {number!humanize} {foo?\"bar\"} {n?1000!humanize}<"));
142142
ATF_REQUIRE(xbps_fmt_dictionary(fmt, dict, fp) == 0);
143143
ATF_REQUIRE(fflush(fp) == 0);
144-
ATF_CHECK_STREQ(buf, ">s 1 0KB<");
144+
ATF_CHECK_STREQ(buf, ">s 1 0KB bar 1KB<");
145145
ATF_REQUIRE(fclose(fp) == 0);
146146
free(buf);
147147
xbps_object_release(dict);

0 commit comments

Comments
 (0)