plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

commit 2ad37309cdb65969b8c5512ce34c2c14c3f98f07
parent 38847929a349c9fe0613c5053213f5b17e9d4d0b
Author: ssnf <ssnf@ssnf.xyz>
Date:   Sun,  3 Aug 2025 02:05:44 +0000

libsdbr: fix parsing, escaping and documentation

Diffstat:
Mman/man3/sdbr.3 | 50+++++++++++++++++++++++++++++++++++---------------
Msrc/libsdbr/mkfile | 2+-
Dsrc/libsdbr/sbdr_val.c | 12------------
Msrc/libsdbr/sdbr_arg2r.c | 2++
Msrc/libsdbr/sdbr_escape.c | 53+++++++++++++++++++++++++++++------------------------
Msrc/libsdbr/sdbr_match.c | 6+++++-
Msrc/libsdbr/sdbr_print.c | 7+++++--
Msrc/libsdbr/sdbr_query.c | 2+-
Msrc/libsdbr/sdbr_str2r.c | 109++++++++++++++++++++++++++++++++++++++++---------------------------------------
Asrc/libsdbr/sdbr_val.c | 12++++++++++++
Msrc/libsdbr/vsdbr_join.c | 2++
11 files changed, 147 insertions(+), 110 deletions(-)

diff --git a/man/man3/sdbr.3 b/man/man3/sdbr.3 @@ -119,8 +119,9 @@ parses a string .I s containing space-separated attribute=value pairs and adds them to record .IR r . +The input string is not modified. Values containing spaces can be quoted with double quotes. -Escape sequences \e0, \et, \en, \e", and \e\e are supported within quoted values. +Escape sequences \e0, \ef, \en, \er, \et, \ev, \e", and \e\e are supported within quoted values. .PP .I Sdbr_arg2r parses command-line arguments @@ -135,7 +136,7 @@ returns the value of attribute in record .IR r . If the attribute is not found, it returns an empty string. -The attribute name can contain glob patterns. +The attribute name can contain wildcards. .PP .I Sdbr_attr returns the index of attribute @@ -149,9 +150,12 @@ tests whether .I value matches .IR pattern . -The pattern supports glob-style wildcards (*) and comparison operators +The pattern supports wildcards (*) and comparison operators (>, >=, <, <=). -These operators perform lexicographic string comparison, not numeric. +The * wildcard matches zero or more characters. +When a * is followed by a literal character, it finds the first occurrence +of that character in the remaining string and continues matching from there. +Comparison operators perform lexicographic string comparison, not numeric. Literal asterisks can be escaped with backslash. .PP .I Sdbr_query @@ -188,12 +192,28 @@ It returns a pointer to the formatted string. prints record .I r to standard output in attribute=value format. +The record must have at least one attribute. .PP .I Vsdbr_join -appends record +adds record .I r to the vector of records .IR v . +The vector must be initialized with +.I Vecinitf(&v, sdbr_init, sdbr_close) +to properly manage the Sdbr structures. +If a record in +.I v +already has the same first attribute name and value as +.IR r , +that record is updated with all attributes from +.IR r . +Otherwise, +.I r +is appended to the vector. +The record +.I r +must have at least one attribute. .SH EXAMPLES .PP Basic record operations: @@ -241,7 +261,7 @@ sdbr_close(&q); Pattern matching: .IP .EX -if(sdbr_match("*user*", "admin_user")) +if(sdbr_match("*user", "admin_user")) print("Pattern matches\en"); if(sdbr_match(">=100", "150")) print("Comparison matches\en"); @@ -253,7 +273,7 @@ Process tracking: Sdbr r; sdbr_init(&r); -sdbr_str2r(&r, "pid=1234 cmd=rc user=glenda cpu=0.5"); +sdbr_str2r(&r, Str("pid=1234 cmd=rc user=glenda cpu=0.5")); sdbr_add(&r, Str("tags"), Str("shell,interactive")); sdbr_add(&r, Str("started"), Str("2025-01-15T10:30:00")); sdbr_print(r); @@ -267,8 +287,8 @@ Sdbr def, usr; sdbr_init(&def); sdbr_init(&usr); -sdbr_str2r(&def, "editor=sam theme=light font=lucsans"); -sdbr_str2r(&usr, "theme=dark font=go"); +sdbr_str2r(&def, Str("editor=sam theme=light font=lucsans")); +sdbr_str2r(&usr, Str("theme=dark font=go")); sdbr_edit(&def, usr); print("Font: %s\en", sdbr_val(def, "font").s); sdbr_close(&def); @@ -282,10 +302,10 @@ Sdbr ev, q; sdbr_init(&ev); sdbr_init(&q); -sdbr_str2r(&ev, "timestamp=2025-01-15T14:30:00 level=error"); -sdbr_str2r(&q, +sdbr_str2r(&ev, Str("timestamp=2025-01-15T14:30:00 level=error")); +sdbr_str2r(&q, Str( "timestamp=>=2025-01-15T14:00:00 " - "timestamp=<=2025-01-15T15:00:00"); + "timestamp=<=2025-01-15T15:00:00")); if(sdbr_query(ev, q)) print("Event in time range\en"); sdbr_close(&ev); @@ -299,10 +319,10 @@ Sdbr loc, q; sdbr_init(&loc); sdbr_init(&q); -sdbr_str2r(&loc, "lat=40.7128 lon=-74.0060 city=\"New York\""); -sdbr_str2r(&q, +sdbr_str2r(&loc, Str("lat=40.7128 lon=-74.0060 city=\"New York\"")); +sdbr_str2r(&q, Str( "lat=>=40.0 lat=<=41.0 " - "lon=>=-73.0 lon=<=-75.0"); + "lon=>=-73.0 lon=<=-75.0")); if(sdbr_query(loc, q)) print("Location in bounding box\en"); sdbr_close(&loc); diff --git a/src/libsdbr/mkfile b/src/libsdbr/mkfile @@ -3,7 +3,6 @@ LIB=libsdbr.a OFILES=\ - sbdr_val.$O\ sdbr_add.$O\ sdbr_arg2r.$O\ sdbr_attr.$O\ @@ -15,6 +14,7 @@ OFILES=\ sdbr_print.$O\ sdbr_query.$O\ sdbr_str2r.$O\ + sdbr_val.$O\ sdbr_zero.$O\ vsdbr_join.$O\ diff --git a/src/libsdbr/sbdr_val.c b/src/libsdbr/sbdr_val.c @@ -1,12 +0,0 @@ -#include "std.h" - -String -sdbr_val(Sdbr r, char *attr) -{ - ulong i; - - for (i = 0; i < Vecsiz(r.attr); ++i) - if (sdbr_match(attr, r.attr[i].s)) - return r.val[i]; - return Strn(NULL, 0); -} diff --git a/src/libsdbr/sdbr_arg2r.c b/src/libsdbr/sdbr_arg2r.c @@ -3,6 +3,8 @@ void sdbr_arg2r(Sdbr* r, char* argv[]) { + if (!argv) + sysfatal("sdbr_arg2r: nil argv"); for (;argv[0] != NULL; argv += 2) { if (argv[1] == NULL) { sdbr_add(r, Str(argv[0]), Str("")); diff --git a/src/libsdbr/sdbr_escape.c b/src/libsdbr/sdbr_escape.c @@ -3,37 +3,42 @@ char* sdbr_escape(String *s, String val) { - String q; - ulong i; - uchar c, w; + ulong i; + uchar c, e; Strzero(s); - w = 0; + e = 0; + if (val.s[0] == '"') + e = 1; + else for (i = 0; i < val.n; ++i) { + c = val.s[i]; + if (c == '\0' || isspace(c)) { + e = 1; + break; + } + } for (i = 0; i < val.n; ++i) { - if (!w && isspace(val.s[i]) && val.s[i] != '\n') - w = 1; - switch (val.s[i]) { - case '\0': c = '0'; break; - case '\t': c = 't'; break; - case '\n': c = 'n'; break; - case '"': c = '"'; break; - case '\\': - if (val.s[i + 1] && val.s[i + 1] != 'n') { - c = '\\'; - break; - } - default: - Straddc(s, val.s[i]); - continue; + c = val.s[i]; + switch (c) { + case '\0': c = '0'; break; + case '\f': c = 'f'; break; + case '\n': c = 'n'; break; + case '\r': c = 'r'; break; + case '\t': c = 't'; break; + case '\v': c = 'v'; break; + case '"': + case '\\': + if (e) + break; + default: + Straddc(s, c); + continue; } Straddc(s, '\\'); Straddc(s, c); } - if (w) { - Strinit(&q); - Straddc(&q, '"'); - Strinsert(s, q, 0); - Strclose(&q); + if (e) { + Strinsert(s, Strn("\"", 1), 0); Straddc(s, '"'); } return s->s; diff --git a/src/libsdbr/sdbr_match.c b/src/libsdbr/sdbr_match.c @@ -5,6 +5,10 @@ sdbr_match(char *k, char *v) { size_t l, n; + if (!k) + sysfatal("sdbr_match: nil key"); + if (!v) + sysfatal("sdbr_match: nil value"); if (*k == '>') { if (*++k == '=') return strcmp(v, k + 1) >= 0 ? 1 : 0; @@ -17,7 +21,7 @@ sdbr_match(char *k, char *v) for (;*k;) { if (*k == '*') { if (!*++k) - return *v ? 1 : 0; + return 1; if (k[strcspn(k, "*\\")]) { v = strchr(v, *k); return v == NULL ? 0 : sdbr_match(k, v); diff --git a/src/libsdbr/sdbr_print.c b/src/libsdbr/sdbr_print.c @@ -6,9 +6,12 @@ sdbr_print(Sdbr r) String s; ulong i; + if (!Vecsiz(r.attr)) + sysfatal("sdbr_print: empty record"); Strinit(&s); - for (i = 0; i < Vecsiz(r.attr); ++i) - print("%s=%s", r.attr[i].s, sdbr_escape(&s, r.val[i])); + print("%s=%s", r.attr[0].s, sdbr_escape(&s, r.val[0])); + for (i = 1; i < Vecsiz(r.attr); ++i) + print(" %s=%s", r.attr[i].s, sdbr_escape(&s, r.val[i])); Strclose(&s); write(1, "\n", 1); } diff --git a/src/libsdbr/sdbr_query.c b/src/libsdbr/sdbr_query.c @@ -15,7 +15,7 @@ sdbr_query(Sdbr r, Sdbr q) continue; return 0; } - if (!sdbr_match(q.val[i].s, r.val[m].s)) + if (sdbr_match(q.val[i].s, r.val[m].s) == (attr[0] == '!')) return 0; } return 1; diff --git a/src/libsdbr/sdbr_str2r.c b/src/libsdbr/sdbr_str2r.c @@ -1,62 +1,63 @@ #include "std.h" -void -sdbr_str2r(Sdbr *sr, String s) +static char* +parsetuple(char *p, Sdbr *r) { - char *p, *q, *k, *v; + char *k, *v, *q; ulong vn; - v = nil; - for (p = s.s; *p;) { - vn = 0; - for (;isspace(*p); ++p); - for (k = p; *p; ++p) { - if (isspace(*p)) { - *p++ = '\0'; - break; - } else if (*p != '=') - continue; - *p++ = '\0'; - for (v = p; *p; ++p) { - if (isspace(*p)) { - *p++ = '\0'; - break; - } else if (*p != '"') { - ++vn; - continue; - } - for (v = q = ++p - ; *q != '"' && *q != '\n' - ; ++q - ) { - if (*q == '\\') - switch (*(q + 1)) { - case '0': - *++q = '\0'; - break; - case 't': - *++q = '\t'; - break; - case 'n': - *++q = '\n'; - break; - case '"': - *++q = '"'; - break; - case '\\': - *++q = '\\'; - break; - } - *p++ = *q; - ++vn; - } - *p = '\0'; - p = q + 1; - break; + for (;isspace(*p); ++p); + k = p; + for (;*p && !isspace(*p) && *p != '='; ++p); + if (!*p || *p != '=') { + if (k != p) + sdbr_add(r, Strn(k, p - k), Str(nil)); + return p; + } + *p++ = '\0'; + if (*p != '"') { + v = p; + for (;*p && !isspace(*p); ++p); + if (v != p) + sdbr_add(r, Str(k), Strn(v, p - v)); + else + sdbr_add(r, Str(k), Str(nil)); + return p; + } + v = ++p; + for (q = p; *q && *q != '"' && *q != '\n'; ++q) { + if (*q == '\\') + switch (*(q + 1)) { + case '\\': ++q; break; + case '0': *++q = '\0'; break; + case 'f': *++q = '\f'; break; + case 'n': *++q = '\n'; break; + case 'r': *++q = '\r'; break; + case 't': *++q = '\t'; break; + case 'v': *++q = '\v'; break; + case '"': *++q = '"'; break; } - break; - } - sdbr_add(sr, Str(k), Strn(v, vn)); - for (;isspace(*p); ++p); + *p++ = *q; + } + sdbr_add(r, Str(k), Strn(v, p - v)); + return *q ? q + 1 : q; +} + +void +sdbr_str2r(Sdbr *sr, String s) +{ + String t; + char *p, *q; + + Strinit(&t); + Strdup(&t, s); + for (p = t.s; *p; ++p) { + if (*p != '#') + continue; + for (q = p; *q && *q != '\n'; ++q); + Strdelete(&t, p - t.s, q - p); + --p; } + for (p = t.s; *p; p = parsetuple(p, sr)); + Strclose(&t); } diff --git a/src/libsdbr/sdbr_val.c b/src/libsdbr/sdbr_val.c @@ -0,0 +1,12 @@ +#include "std.h" + +String +sdbr_val(Sdbr r, char *attr) +{ + ulong i; + + for (i = 0; i < Vecsiz(r.attr); ++i) + if (sdbr_match(attr, r.attr[i].s)) + return r.val[i]; + return Strn(nil, 0); +} diff --git a/src/libsdbr/vsdbr_join.c b/src/libsdbr/vsdbr_join.c @@ -5,6 +5,8 @@ vsdbr_join(Sdbr *v[], Sdbr r) { int i, n; + if (!Vecsiz(r.attr)) + sysfatal("vsdbr_join: empty record"); for (i = 0; i < Vecsiz(*v); ++i) { n = sdbr_attr((*v)[i], r.attr[0].s); if (n < 0 || strcmp((*v)[i].val[n].s, r.val[0].s))