diff --git a/include/xdump.h b/include/xdump.h index 34108b3b..9c88a833 100644 --- a/include/xdump.h +++ b/include/xdump.h @@ -42,11 +42,13 @@ struct xdstr { natid cnum; /* dump for this country */ int divine; /* is this a deity dump? */ + int human; /* dump human-readable format */ void (*pr)(char *fmt, ...); /* callback for printing dump */ }; -struct xdstr *xdinit(struct xdstr *, natid, void (*)(char *, ...)); +struct xdstr *xdinit(struct xdstr *, natid, int, void (*)(char *, ...)); extern void xdhdr(struct xdstr *, char *, int); +extern void xdcolhdr(struct xdstr *, struct castr[]); extern void xdflds(struct xdstr *, struct castr[], void *); extern struct valstr *xdeval(struct valstr *, struct xdstr *, struct castr *, void *, int); extern char *xdprval(struct xdstr *, struct valstr *, char *); diff --git a/src/lib/commands/xdump.c b/src/lib/commands/xdump.c index 30e0d073..6b9f0444 100644 --- a/src/lib/commands/xdump.c +++ b/src/lib/commands/xdump.c @@ -167,7 +167,7 @@ xdump(void) if (!p || !*p) return RET_SYN; - xdinit(&xd, player->cnum, pr); + xdinit(&xd, player->cnum, 0, pr); natp = getnatp(player->cnum); type = isdigit(p[0]) ? atoi(p) : ef_byname(p); if (type < 0 || type >= EF_MAX) diff --git a/src/lib/common/xdump.c b/src/lib/common/xdump.c index b2117249..768b9388 100644 --- a/src/lib/common/xdump.c +++ b/src/lib/common/xdump.c @@ -76,20 +76,23 @@ #include #include +#include #include "file.h" #include "nat.h" #include "xdump.h" /* * Initialize XD to dump for country CNUM. + * If HUMAN, dump in human-readable format. * Dump is to be delivered through callback PR. * Return XD. */ struct xdstr * -xdinit(struct xdstr *xd, natid cnum, void (*pr)(char *fmt, ...)) +xdinit(struct xdstr *xd, natid cnum, int human, void (*pr)(char *fmt, ...)) { xd->cnum = cnum; xd->divine = getnatp(cnum)->nat_stat == STAT_GOD; + xd->human = human; xd->pr = pr; return xd; } @@ -110,14 +113,38 @@ xdeval(struct valstr *val, struct xdstr *xd, } /* - * Dump VAL prefixed with SEP, return " ". - * VAL must be evaluated. + * Dump string STR to XD with funny characters escaped. + * Dump at most N characters. */ -char * -xdprval(struct xdstr *xd, struct valstr *val, char *sep) +static void +xdpresc(struct xdstr *xd, char *str, size_t n) { unsigned char *s, *e, *l; + s = (unsigned char *)str; + l = s + n; + for (;;) { + for (e = s; + e < l && *e != '"' && *e != '\\' && isgraph(*e); + ++e) + ; + xd->pr("%.*s", (int)(e-s), s); + if (e < l && *e) + xd->pr("\\%03o", *e++); + else + break; + s = e; + } +} + +/* + * Dump VAL prefixed with SEP to XD, in machine readable format. + * VAL must be evaluated. + * Return " ". + */ +static char * +xdprval_nosym(struct xdstr *xd, struct valstr *val, char *sep) +{ if (CANT_HAPPEN(val->val_cat != NSC_VAL)) { xd->pr("%snil", sep); return " "; @@ -131,23 +158,9 @@ xdprval(struct xdstr *xd, struct valstr *val, char *sep) xd->pr("%s%#g", sep, val->val_as.dbl); break; case NSC_STRING: - s = (unsigned char *)val->val_as.str.base; - if (s) { + if (val->val_as.str.base) { xd->pr("%s\"", sep); - l = s + val->val_as.str.maxsz; - /* FIXME maxsz == INT_MAX ! */ - for (;;) { - for (e = s; - e < l && *e != '"' && *e != '\\' && isgraph(*e); - ++e) - ; - xd->pr("%.*s", (int)(e-s), s); - if (e < l && *e) - xd->pr("\\%03o", *e++); - else - break; - s = e; - } + xdpresc(xd, val->val_as.str.base, val->val_as.str.maxsz); xd->pr("\""); } else xd->pr("%snil", sep); @@ -159,6 +172,70 @@ xdprval(struct xdstr *xd, struct valstr *val, char *sep) return " "; } +/* + * Dump symbol with value KEY from symbol table TYPE to XD. + * Prefix with SEP, return " ". + */ +static char * +xdprsym(struct xdstr *xd, int key, int type, char *sep) +{ + char *sym = symbol_by_value(key, ef_ptr(type, 0)); + + if (CANT_HAPPEN(!sym)) + xd->pr("%s%ld", sep, key); + else { + xd->pr("%s", sep); + xdpresc(xd, sym, INT_MAX); + } + return " "; +} + +/* + * Dump VAL prefixed with SEP to XD, return " ". + * VAL must be evaluated. + * CA describes the field from which the value was fetched. + */ +static char * +xdprval_sym(struct xdstr *xd, struct valstr *val, struct castr *ca, char *sep) +{ + unsigned long bit; + struct castr *ca_sym = ef_cadef(ca->ca_table); + + if (CANT_HAPPEN(val->val_cat != NSC_VAL)) { + xd->pr("%snil", sep); + return " "; + } + + if (!xd->human || val->val_type != NSC_LONG + || ca->ca_table == EF_BAD || ca_sym != symbol_ca) + return xdprval_nosym(xd, val, sep); + + if (ca->ca_flags & NSC_BITS) { + xd->pr("%s(", sep); + sep = ""; + for (bit = 1; bit; bit <<= 1) { + if (bit & val->val_as.lng) + sep = xdprsym(xd, bit, ca->ca_table, sep); + } + xd->pr(")"); + return " "; + } + + return xdprsym(xd, val->val_as.lng, ca->ca_table, sep); +} + +/* + * Dump VAL prefixed with SEP to XD, return " ". + * XD must not be human-readable. + * VAL must be evaluated. + */ +char * +xdprval(struct xdstr *xd, struct valstr *val, char *sep) +{ + CANT_HAPPEN(xd->human); + return xdprval_nosym(xd, val, sep); +} + /* * Dump field values of a context object to XD. * CA[] describes fields. @@ -180,26 +257,67 @@ xdflds(struct xdstr *xd, struct castr ca[], void *ptr) j = 0; do { xdeval(&val, xd, &ca[i], ptr, j); - sep = xdprval(xd, &val, sep); + sep = xdprval_sym(xd, &val, &ca[i], sep); } while (++j < n); } } /* - * Dump header for dump NAME. + * Dump header for dump NAME to XD. * If META, it's for the meta-data dump rather than the data dump. */ void xdhdr(struct xdstr *xd, char *name, int meta) { - xd->pr("XDUMP %s%s %ld\n", meta ? "meta " : "", name, (long)time(NULL)); + if (xd->human) { + xd->pr("config %s\n", name); + } else + xd->pr("XDUMP %s%s %ld\n", + meta ? "meta " : "", + name, (long)time(NULL)); } -/* Dump footer for a dump that dumped N objects. */ +/* + * Dump column header to XD. + * CA[] describes fields. + * Does nothing unless XD is human-readable. + */ +void +xdcolhdr(struct xdstr *xd, struct castr ca[]) +{ + int i, j, n; + char *sep = ""; + + if (!xd->human) + return; + + for (i = 0; ca[i].ca_name; ++i) { + if (ca[i].ca_flags & NSC_DEITY && !xd->divine) + continue; + if (ca[i].ca_flags & NSC_EXTRA) + continue; + n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0; + if (n) { + for (j = 0; j < n; j++) { + xd->pr("%s%s(%d)",sep, ca[i].ca_name, j); + sep = " "; + } + } else { + xd->pr("%s%s", sep, ca[i].ca_name); + sep = " "; + } + } + xd->pr("\n"); +} + +/* Dump footer for a dump that dumped N objects to XD. */ void xdftr(struct xdstr *xd, int n) { - xd->pr("/%d\n", n); + if (xd->human) + xd->pr("/config\n"); + else + xd->pr("/%d\n", n); } /* @@ -217,6 +335,7 @@ xdmeta(struct xdstr *xd, int type) return RET_SYN; xdhdr(xd, ef_nameof(type), 1); + xdcolhdr(xd, ca); for (i = 0; ca[i].ca_name; i++) { if (ca[i].ca_flags & NSC_DEITY && !xd->divine)