Skip to content

Commit

Permalink
lib/format.c: add optional default to format string variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Duncaen committed Mar 14, 2023
1 parent 754fae2 commit 6d46066
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 28 deletions.
15 changes: 9 additions & 6 deletions bin/xbps-query/xbps-query.1
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,17 @@ xbps 484 KB
.Pp
Format strings are parsed by the following EBNF:
.Bd -literal
<grammar> ::= (text | escape | substitution)*
<text> ::= [^\\{}]+ -- literal text chunk
<escape> ::= "\\" [abfnrtv0] -- POSIX-like espace sequence
| "\\{" | "\\}" -- escaped "{" and "}"
<grammar> ::= (prefix | "\\" (escape|[{}]) | substitution)*
<prefix> ::= [^\\{}]+ -- Literal text chunk.
<escape> ::= [abfnrtv0] -- POSIX-like espace character.

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

<default> ::= ([-]?[0-9]+) -- default number.
| "true" | "false" -- default boolean.
| ('"' (("\\" (escape|'"')) | [^"])* '"') -- default string.

<conversion> ::= humanize | strmode

-- Convert inode status information into a symbolic string
Expand All @@ -329,7 +332,7 @@ Format strings are parsed by the following EBNF:
| "T" -- tera
| "P" -- peta
| "E" -- exa
<i> ::= "i" -- Use IEE/IEC (and now also SI) power of two prefixes.
<i> ::= "i" -- Use IEEE/IEC (and now also SI) power of two prefixes.

<format> ::= [[fill] align] [sign] [width] ["." precision] [type]
<fill> ::= <any char> -- The character to use when aligning the output.
Expand Down
26 changes: 24 additions & 2 deletions include/xbps.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -2370,7 +2370,7 @@ xbps_plist_dictionary_from_file(const char *path);

/**
* @struct xbps_fmt xbps.h "xbps.h"
* @brief Structure of parsed format string.
* @brief Structure of parsed format string variable.
*/
struct xbps_fmt {
/**
Expand All @@ -2384,6 +2384,11 @@ struct xbps_fmt {
* @brief Variable name.
*/
char *var;
/**
* @var def
* @brief Default value.
*/
struct xbps_fmt_def *def;
/**
* @var conv
* @brief Format conversion.
Expand All @@ -2397,7 +2402,24 @@ struct xbps_fmt {
};

/**
* @struct xbps_fmt xbps.h "xbps.h"
* @struct xbps_fmt_def xbps.h "xbps.h"
* @brief Structure of parsed format specifier.
*/
struct xbps_fmt_def {
enum {
XBPS_FMT_DEF_STR = 1,
XBPS_FMT_DEF_NUM,
XBPS_FMT_DEF_BOOL,
} type;
union {
char *str;
int64_t num;
bool boolean;
} val;
};

/**
* @struct xbps_fmt_spec xbps.h "xbps.h"
* @brief Structure of parsed format specifier.
*/
struct xbps_fmt_spec {
Expand Down
167 changes: 149 additions & 18 deletions lib/format.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,6 @@ nexttok(const char **pos, struct strbuf *buf)
return 0;
}

struct xbps_fmt_conv {
enum { HUMANIZE = 1, STRMODE } type;
union {
struct humanize {
unsigned width : 8;
unsigned minscale : 8;
unsigned maxscale : 8;
bool decimal : 1;
int flags;
} humanize;
};
};

static int
parse_u(const char **pos, unsigned int *u)
{
Expand All @@ -188,6 +175,129 @@ parse_u(const char **pos, unsigned int *u)
return 0;
}

static int
parse_d(const char **pos, int64_t *d)
{
char *e = NULL;
long v;
errno = 0;
v = strtol(*pos, &e, 10);
if (errno != 0)
return -errno;
if (v > UINT_MAX)
return -ERANGE;
*d = v;
*pos = e;
return 0;
}

static int
parse_default(const char **pos, struct xbps_fmt *fmt, struct strbuf *buf,
struct xbps_fmt_def *def_storage)
{
struct strbuf buf2 = {0};
struct xbps_fmt_def *def;
const char *p = *pos;
char *str = NULL;
int r;

if (*p++ != '?')
return 0;
if (!def_storage) {
fmt->def = def = calloc(1, sizeof(*def));
if (!def)
return -errno;
} else {
fmt->def = def = def_storage;
}

if ((*p >= '0' && *p <= '9') || *p == '-') {
r = parse_d(&p, &def->val.num);
if (r < 0)
return r;
def->type = XBPS_FMT_DEF_NUM;
*pos = p;
return 0;
} else if (strncmp(p, "true", sizeof("true") - 1) == 0) {
*pos = p + sizeof("true") - 1;
def->type = XBPS_FMT_DEF_BOOL;
def->val.boolean = true;
return 0;
} else if (strncmp(p, "false", sizeof("false") - 1) == 0) {
*pos = p + sizeof("false") - 1;
def->type = XBPS_FMT_DEF_BOOL;
def->val.boolean = false;
return 0;
}

if (*p++ != '"')
return -EINVAL;

if (!buf) {
buf = &buf2;
} else {
r = strbuf_putc(buf, '\0');
if (r < 0)
return r;
str = buf->mem + buf->len;
}
for (; *p && *p != '"'; p++) {
switch (*p) {
case '\\':
switch (*++p) {
case '\\': r = strbuf_putc(buf, '\\'); break;
case 'a': r = strbuf_putc(buf, '\a'); break;
case 'b': r = strbuf_putc(buf, '\b'); break;
case 'f': r = strbuf_putc(buf, '\f'); break;
case 'n': r = strbuf_putc(buf, '\n'); break;
case 'r': r = strbuf_putc(buf, '\r'); break;
case 't': r = strbuf_putc(buf, '\t'); break;
case '0': r = strbuf_putc(buf, '\0'); break;
case '"': r = strbuf_putc(buf, '"'); break;
default: r = -EINVAL;
}
break;
default:
r = strbuf_putc(buf, *p);
}
if (r < 0)
goto err;
}
if (*p++ != '"') {
r = -EINVAL;
goto err;
}
*pos = p;
def->type = XBPS_FMT_DEF_STR;
if (buf == &buf2) {
def->val.str = strdup(buf2.mem);
if (!def->val.str) {
r = -errno;
goto err;
}
strbuf_release(&buf2);
} else {
def->val.str = str;
}
return 0;
err:
strbuf_release(&buf2);
return r;
}

struct xbps_fmt_conv {
enum { HUMANIZE = 1, STRMODE } type;
union {
struct humanize {
unsigned width : 8;
unsigned minscale : 8;
unsigned maxscale : 8;
bool decimal : 1;
int flags;
} humanize;
};
};

static int
parse_humanize(const char **pos, struct humanize *humanize)
{
Expand Down Expand Up @@ -346,6 +456,7 @@ parse_spec(const char **pos, struct xbps_fmt *fmt, struct xbps_fmt_spec *spec_st
static int
parse(const char **pos, struct xbps_fmt *fmt,
struct strbuf *buf,
struct xbps_fmt_def *def_storage,
struct xbps_fmt_conv *conv_storage,
struct xbps_fmt_spec *spec_storage)
{
Expand All @@ -357,7 +468,7 @@ parse(const char **pos, struct xbps_fmt *fmt,
return -EINVAL;
p++;

/* var ::= '{' name [conversion][format_spec] '}' */
/* var ::= '{' name [default][conversion][format_spec] '}' */

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

/* default ::= ['?' ...] */
r = parse_default(&p, fmt, buf, def_storage);
if (r < 0)
return r;

/* conversion ::= ['!' ...] */
r = parse_conversion(&p, fmt, conv_storage);
if (r < 0)
Expand Down Expand Up @@ -426,7 +542,7 @@ xbps_fmt_parse(const char *format)
t = nexttok(&pos, &buf);
}
if (t == TVAR) {
r = parse(&pos, &fmt[n], NULL, NULL, NULL);
r = parse(&pos, &fmt[n], NULL, NULL, NULL, NULL);
if (r < 0)
goto err;
}
Expand All @@ -453,6 +569,9 @@ xbps_fmt_free(struct xbps_fmt *fmt)
for (struct xbps_fmt *f = fmt; f->prefix || f->var; f++) {
free(f->prefix);
free(f->var);
if (f->def && f->def->type == XBPS_FMT_DEF_STR)
free(f->def->val.str);
free(f->def);
free(f->spec);
free(f->conv);
}
Expand All @@ -477,10 +596,11 @@ xbps_fmts(const char *format, xbps_fmt_cb *cb, void *data, FILE *fp)
t = nexttok(&pos, &buf);
}
if (t == TVAR) {
struct xbps_fmt_spec spec = {0};
struct xbps_fmt_def def = {0};
struct xbps_fmt_conv conv = {0};
struct xbps_fmt_spec spec = {0};
struct xbps_fmt fmt = { .var = buf.mem };
r = parse(&pos, &fmt, &buf, &conv, &spec);
r = parse(&pos, &fmt, &buf, &def, &conv, &spec);
if (r < 0)
goto out;
r = cb(fp, &fmt, data);
Expand Down Expand Up @@ -617,7 +737,18 @@ xbps_fmt_print_object(const struct xbps_fmt *fmt, xbps_object_t obj, FILE *fp)
return xbps_fmt_print_string(fmt, xbps_string_cstring_nocopy(obj),
xbps_string_size(obj), fp);
case XBPS_TYPE_UNKNOWN:
return xbps_fmt_print_string(fmt, "(null)", 0, fp);
if (fmt->def) {
struct xbps_fmt_def *def = fmt->def;
switch (fmt->def->type) {
case XBPS_FMT_DEF_BOOL:
return xbps_fmt_print_string(fmt, def->val.boolean ?
"true" : "false", 0, fp);
case XBPS_FMT_DEF_STR:
return xbps_fmt_print_string(fmt, def->val.str, 0, fp);
case XBPS_FMT_DEF_NUM:
return xbps_fmt_print_number(fmt, def->val.num, fp);
}
}
default:
break;
}
Expand Down
4 changes: 2 additions & 2 deletions tests/xbps/libxbps/fmt/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ ATF_TC_BODY(xbps_fmt_dictionary, tc)
ATF_REQUIRE(dict = xbps_dictionary_create());
ATF_REQUIRE(xbps_dictionary_set_cstring_nocopy(dict, "string", "s"));
ATF_REQUIRE(xbps_dictionary_set_int64(dict, "number", 1));
ATF_REQUIRE(fmt = xbps_fmt_parse(">{string} {number} {number!humanize}<"));
ATF_REQUIRE(fmt = xbps_fmt_parse(">{string} {number} {number!humanize} {foo?\"bar\"} {n?1000!humanize}<"));
ATF_REQUIRE(xbps_fmt_dictionary(fmt, dict, fp) == 0);
ATF_REQUIRE(fflush(fp) == 0);
ATF_CHECK_STREQ(buf, ">s 1 0KB<");
ATF_CHECK_STREQ(buf, ">s 1 0KB bar 1KB<");
ATF_REQUIRE(fclose(fp) == 0);
free(buf);
xbps_object_release(dict);
Expand Down

0 comments on commit 6d46066

Please sign in to comment.