commit 66bf45d4e933e7d6d91f612cc4bb560ca24a8692
parent d10fbc3951057e3979ff7ee30989c222800e43ef
Author: ssnf <ssnf@ssnf.xyz>
Date: Sun, 31 Aug 2025 15:37:53 +0000
sdb documentation and fixes
Diffstat:
7 files changed, 55 insertions(+), 70 deletions(-)
diff --git a/man/man7/sdb.7 b/man/man7/sdb.7
@@ -2,57 +2,39 @@
.SH NAME
sdb \- simple database format
.SH DESCRIPTION
-The simple database (sdb) format is a text-based database format
-that is 100% backward compatible with Plan 9's network database (ndb) format.
-Database files contain records with attribute=value pairs,
-with records separated by blank lines.
+The simple database (sdb) format is Plan 9's network database
+.MR ndb (7)
+format with arbitrary-length strings for general-purpose use.
.PP
-Each record consists of one or more lines.
-The first line contains space-separated attribute=value pairs.
-Continuation lines begin with whitespace (spaces or tabs) and
-contain additional attribute=value pairs for the same record.
-.PP
-Within a line, attribute=value pairs are separated by spaces or tabs.
-The attribute name and value are separated by an equals sign (=).
-Attribute names cannot contain equals signs or whitespace.
+Database files consist of multi-line records made up of tuples of the
+form attr=value.
+Attributes are any sequence of non-whitespace characters exluding = and #.
+Tuples with no value can omit the =.
+Within a record, tuples are separated by whitespace, and lines starting
+with whitespace continue the current record.
.PP
Values can be quoted with double quotes to include spaces.
When values are quoted, the following escape sequences are recognized:
-\e0 (null), \ef (form feed), \en (newline), \er (carriage return),
-\et (tab), \ev (vertical tab), \e" (quote), and \e\e (backslash).
+\e0 (null), \et (tab), \en (newline), \er (carriage return),
+\e" (quote), and \e\e (backslash).
All other bytes, including those with values above 127, can be stored directly.
-SDB fully supports binary data.
-.PP
-When values are not quoted, tabs cannot appear in values as they
-are field separators. To include a tab in a value, quote the value
-and use \et.
+This allows sdb to fully support binary data.
.PP
-Comments begin with # and extend to the end of the line.
-Comments can appear on their own line or after attribute=value pairs.
+Comments begin with # and extend to the end of the line, except when #
+appears inside a quoted value.
.SH PATTERN MATCHING
-Several sdb tools support pattern matching for attribute values.
+Several sdb tools support pattern matching for attributes and values.
The pattern language includes:
.TP
.B *
Wildcard that matches zero or more characters.
-However, a pattern consisting of a single * matches one or more characters.
-.TP
-.B >
-.PD 0
-.TP
-.B >=
-.TP
-.B <
+A trailing * in a pattern matches one or more characters.
.TP
-.B <=
-.PD
+.B >, >=, < and <=
Comparison operators for lexicographic string comparison.
.TP
.B \e*
Literal asterisk (escaped with backslash).
-.PP
-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.
.SH EXAMPLES
A simple database of network services:
.IP
@@ -70,30 +52,18 @@ service=ssh port=22 protocol=tcp
Using special characters in values:
.IP
.EX
-# Tab-separated data
-data=raw value="one\ttwo\tthree"
+data=raw value="one\ettwo\etthree"
-# Multi-line text
message="First line\enSecond line\enThird line"
-# Path with spaces
file="/home/user/My Documents/report.txt"
-# Binary data with null bytes and high-bit characters
-signature="GIF89a\0\0ÿÿ"
-checksum="Á¢£¤¥¦§¨©"
+signature="GIF89a\e0\e0ÿÿ"
+ checksum=Á¢£¤¥¦§¨©
.EE
.SH SEE ALSO
-.MR ndb (1) ,
-.MR ndb (7) ,
.MR sdb (1) ,
.MR sdb (3) ,
-.MR sdbr (3)
-.SH NOTES
-The sdb format is designed to be both human-readable and
-suitable for processing with standard Unix text tools.
-Single-line record format enables easy sorting and filtering
-with tools like
-.MR sort (1)
-and
-.MR grep (1) .
-\ No newline at end of file
+.MR sdbr (3) ,
+.MR ndb (7) ,
+
diff --git a/src/cmd/sdb/json2sdb.c b/src/cmd/sdb/json2sdb.c
@@ -68,11 +68,9 @@ Ufmt(Fmt *fmt)
case '+': fmtprint(fmt, "\\x2b"); break;
case '=': fmtprint(fmt, "\\x3d"); break;
case '\\': fmtprint(fmt, "\\\\"); break;
- case '\f': fmtprint(fmt, "\\f"); break;
case '\n': fmtprint(fmt, "\\n"); break;
case '\r': fmtprint(fmt, "\\r"); break;
case '\t': fmtprint(fmt, "\\t"); break;
- case '\v': fmtprint(fmt, "\\v"); break;
default: fmtrune(fmt, *s);
}
}
diff --git a/src/cmd/sdb/sdbrval.c b/src/cmd/sdb/sdbrval.c
@@ -5,6 +5,7 @@ static void print_col(Sdbr*, char*);
static String val;
static String ln;
+static int hasdata;
static char *fmt = "%.0s%s";
static void (*print_fn)(Sdbr*, char*) = print_col;
static char* (*escapefn)(String*, String) = sdbr_escape;
@@ -46,6 +47,8 @@ print_col(Sdbr *r, char *attr)
}
escapefn(&val, r->val[n]);
Strprint(&ln, fmt, r->attr[n].s, val.s);
+ if (val.n)
+ hasdata = 1;
}
static void
@@ -60,6 +63,8 @@ print_match(Sdbr *r, char *attr)
Straddc(&ln, '\t');
escapefn(&val, r->val[i]);
Strprint(&ln, fmt, r->attr[i].s, val.s);
+ if (val.n)
+ hasdata = 1;
}
}
@@ -97,10 +102,11 @@ main(int argc, char *argv[])
sdb_open(&db, nil);
for (;sdb_next(&db);) {
r = db.r + db.n;
+ hasdata = 0;
Strzero(&ln);
for (n = 0; n < argc; ++n)
print_fn(r, argv[n]);
- if (ln.n)
+ if (hasdata)
print("%s\n", ln.s);
}
exits(0);
diff --git a/src/libsdbr/sdbr_escape.c b/src/libsdbr/sdbr_escape.c
@@ -12,7 +12,7 @@ sdbr_escape(String *s, String val)
e = 1;
else for (i = 0; i < val.n; ++i) {
c = val.s[i];
- if (c == '\0' || isspace(c)) {
+ if (c == '\0' || ISWHITE(c)) {
e = 1;
break;
}
@@ -21,11 +21,9 @@ sdbr_escape(String *s, String val)
c = val.s[i];
switch (c) {
case '\0': c = '0'; break;
- case '\f': c = 'f'; break;
+ case '\t': c = 't'; 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)
diff --git a/src/libsdbr/sdbr_match.c b/src/libsdbr/sdbr_match.c
@@ -4,6 +4,7 @@ char
sdbr_match(char *k, char *v)
{
size_t l, n;
+ char *t;
if (!k)
sysfatal("sdbr_match: nil key");
@@ -22,11 +23,20 @@ sdbr_match(char *k, char *v)
}
for (;*k;) {
if (*k == '*') {
- if (!*++k)
+ for (;*++k == '*';);
+ if (!*k)
return 1;
- if (k[strcspn(k, "*\\")]) {
- v = strchr(v, *k);
- return v == NULL ? 0 : sdbr_match(k, v);
+ n = strcspn(k, "*\\");
+ if (k[n]) {
+ t = strdup(k);
+ t[n] = '\0';
+ v = strstr(v, t);
+ if (v) {
+ v += n;
+ k += n;
+ }
+ free(t);
+ return !v ? 0 : sdbr_match(k, v);
}
l = strlen(k);
n = strlen(v);
diff --git a/src/libsdbr/sdbr_str2r.c b/src/libsdbr/sdbr_str2r.c
@@ -5,9 +5,9 @@ parsetuple(char *p, Sdbr *r)
{
char *k, *v, *q;
- for (;isspace(*p); ++p);
+ for (;ISWHITE(*p); ++p);
k = p;
- for (;*p && !isspace(*p) && *p != '='; ++p)
+ for (;*p && !ISWHITE(*p) && *p != '='; ++p)
if (*p == '#')
*p-- = '\0';
if (!*p || *p != '=') {
@@ -18,7 +18,7 @@ parsetuple(char *p, Sdbr *r)
*p++ = '\0';
if (*p != '"') {
v = p;
- for (;*p && !isspace(*p); ++p)
+ for (;*p && !ISWHITE(*p); ++p)
if (*p == '#')
*p-- = '\0';
if (v != p)
@@ -33,11 +33,9 @@ parsetuple(char *p, Sdbr *r)
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;
}
*p++ = *q;
diff --git a/src/libsdbr/std.h b/src/libsdbr/std.h
@@ -5,3 +5,9 @@
#include <str.h>
#include <sdbr.h>
+#define ISWHITE(x) (\
+ (x) == ' '\
+ || (x) == '\t'\
+ || (x) == '\n'\
+ || (x) == '\r'\
+)