From 53bcf0466421f90a50918f982cdc48cdab17b57b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 19 Feb 2008 20:27:01 +0100 Subject: [PATCH 01/20] Remove obsolete comment Obsolete since ef_open() locks the file (commit de124108). --- src/lib/common/file.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lib/common/file.c b/src/lib/common/file.c index ea29bb2a..ebf5ab25 100644 --- a/src/lib/common/file.c +++ b/src/lib/common/file.c @@ -148,11 +148,6 @@ ef_open(int type, int how) } } - /* - * Could close fd if both EFF_RDONLY and EFF_MEM, but that doesn't - * happen, so don't bother. - */ - return 1; } From 642c11eb6419e6ce8acabc4d7ebf0124f3bd702c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 21 Feb 2008 21:04:11 +0100 Subject: [PATCH 02/20] Replace EFF_RDONLY by EFF_PRIVATE Read-only was a bit of a misnomer: you could write to the table by obtaining a pointer into it from ef_ptr(), you just couldn't write to the backing file. Semantic changes: * ef_flush() is now allowed when the table is file-backed or privately mapped. Before, it had to be file-backed. Flushing a privately mapped table does nothing, just like flushing a read-only table did. * ef_write() is now allowed when the table is file-backed or fully cached. Before, it had to be file-backed and not read-only. Writing to a privately mapped file-backed table doesn't write to the file. * ef_extend() is not implemented for privately mapped tables, just like it wasn't implemented for read-only tables. --- include/file.h | 9 +++++---- src/lib/common/file.c | 30 +++++++++++++++++++++--------- src/lib/global/file.c | 4 ++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/include/file.h b/include/file.h index 6748ba78..1242a660 100644 --- a/include/file.h +++ b/include/file.h @@ -57,7 +57,7 @@ struct empfile { int cids; /* # entries in cache */ int fids; /* # entries in table */ int fd; /* file descriptor, -1 if not open */ - /* flags bits EFF_RDONLY, EFF_CUSTOM also vary */ + /* flags bits EFF_PRIVATE, EFF_CUSTOM also vary */ /* User callbacks */ void (*init)(int, void *); /* called after entry creation, unless null */ @@ -86,8 +86,8 @@ struct empfile { /* Flags set when table contents is mapped */ /* Table is entirely in memory */ #define EFF_MEM bit(8) -/* Table is read-only */ -#define EFF_RDONLY bit(9) +/* Table is privately mapped: changes don't affect the underlying file */ +#define EFF_PRIVATE bit(9) /* Table is customized (configuration tables only) */ #define EFF_CUSTOM bit(10) /* Transient flags, only occur in argument of ef_open() */ @@ -97,7 +97,8 @@ struct empfile { /* * Empire `file types' * These are really table IDs. Some tables are backed by files, some - * are compiled into the server. + * are compiled into the server, some initialized from configuration + * files. */ enum { /* Error value */ diff --git a/src/lib/common/file.c b/src/lib/common/file.c index ebf5ab25..a4f72859 100644 --- a/src/lib/common/file.c +++ b/src/lib/common/file.c @@ -73,7 +73,7 @@ ef_open(int type, int how) if (CANT_HAPPEN(ep->fd >= 0)) return 0; oflags = O_RDWR; - if (how & EFF_RDONLY) + if (how & EFF_PRIVATE) oflags = O_RDONLY; if (how & EFF_CREATE) oflags |= O_CREAT | O_TRUNC; @@ -85,7 +85,7 @@ ef_open(int type, int how) return 0; } - lock.l_type = how & EFF_RDONLY ? F_RDLCK : F_WRLCK; + lock.l_type = how & EFF_PRIVATE ? F_RDLCK : F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) == -1) { @@ -177,7 +177,8 @@ ef_close(int type) } /* - * Flush file-backed table TYPE (EF_SECTOR, ...) to disk. + * Flush table TYPE (EF_SECTOR, ...) to disk. + * Does nothing if the table is privately mapped. * Return non-zero on success, zero on failure. */ int @@ -188,6 +189,8 @@ ef_flush(int type) if (ef_check(type) < 0) return 0; ep = &empfile[type]; + if (ep->flags & EFF_PRIVATE) + return 1; /* nothing to do */ if (CANT_HAPPEN(ep->fd < 0)) return 0; /* @@ -196,7 +199,7 @@ ef_flush(int type) * allowed only with EFF_MEM. Assume the whole cash is dirty * then. */ - if (!(ep->flags & EFF_RDONLY) && (ep->flags & EFF_MEM)) + if (ep->flags & EFF_MEM) return do_write(ep, ep->cache, ep->baseid, ep->cids) >= 0; return 1; @@ -311,7 +314,8 @@ do_write(struct empfile *ep, void *buf, int id, int count) int n, ret; char *p; - if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0)) + if (CANT_HAPPEN(ep->fd < 0 || (ep->flags & EFF_PRIVATE) + || id < 0 || count < 0)) return -1; if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) { @@ -339,9 +343,10 @@ do_write(struct empfile *ep, void *buf, int id, int count) } /* - * Write element ID into file-backed table TYPE from buffer FROM. + * Write element ID into table TYPE from buffer FROM. * FIXME pass buffer size! - * Write through cache straight to disk. + * If table is file-backed and not privately mapped, write through + * cache straight to disk. * Cannot write beyond the end of fully cached table (flags & EFF_MEM). * Can write at the end of partially cached table. * Return non-zero on success, zero on failure. @@ -355,12 +360,16 @@ ef_write(int type, int id, void *from) if (ef_check(type) < 0) return 0; ep = &empfile[type]; + if (CANT_HAPPEN((ep->flags & (EFF_MEM | EFF_PRIVATE)) == EFF_PRIVATE)) + return 0; if (ep->prewrite) ep->prewrite(id, from); if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids)) return 0; /* not implemented */ - if (do_write(ep, from, id, 1) < 0) - return 0; + if (!(ep->flags & EFF_PRIVATE)) { + if (do_write(ep, from, id, 1) < 0) + return 0; + } if (id >= ep->baseid && id < ep->baseid + ep->cids) { /* update the cache if necessary */ to = ep->cache + (id - ep->baseid) * ep->size; @@ -376,6 +385,7 @@ ef_write(int type, int id, void *from) /* * Extend the file-backed table TYPE by COUNT elements. + * Can't extend privately mapped tables. * Return non-zero on success, zero on failure. */ int @@ -390,6 +400,8 @@ ef_extend(int type, int count) ep = &empfile[type]; if (CANT_HAPPEN(ep->fd < 0 || count < 0)) return 0; + if (CANT_HAPPEN(ep->flags & EFF_PRIVATE)) + return 0; /* not implemented */ tmpobj = calloc(1, ep->size); id = ep->fids; diff --git a/src/lib/global/file.c b/src/lib/global/file.c index 51e28dc1..9db598fc 100644 --- a/src/lib/global/file.c +++ b/src/lib/global/file.c @@ -85,7 +85,7 @@ SZ((array)), 0, SZ((array)) - 1, SZ((array)) - 1, -1, NULL, NULL, NULL /* Common configuration table flags */ -#define EFF_CFG (EFF_RDONLY | EFF_MEM | EFF_STATIC) +#define EFF_CFG (EFF_PRIVATE | EFF_MEM | EFF_STATIC) struct empfile empfile[] = { /* @@ -95,7 +95,7 @@ struct empfile empfile[] = { * bits of flags get their final value. * If flags & EFF_STATIC, the cache is mapped here, and members * cache, csize get their final value. - * Members baseid, cids, fids and the EFF_MEM|EFF_RDONLY bits of + * Members baseid, cids, fids and the EFF_MEM|EFF_PRIVATE bits of * flags are initialized according the initial cache contents. * Member fd is initialized to -1. * Members init, postread, prewrite get initialized to NULL, but From d4ac7d94b2aa4ee7d98f61c339a4fde40d9c254e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 21 Feb 2008 21:12:58 +0100 Subject: [PATCH 03/20] Generalize ef_extend() to non-file-backed tables ef_extend() extended the file bypassing the cache, which screws up the cache if it's EFF_MEM. It fixed that by closing and reopening the table. Cheesy, and worked only for file-backed tables. Rewrite ef_extend() to remap the cache properly for EFF_MEM. While there, simplify the !EFF_MEM case: steal a cache slot instead of allocating a buffer. Factor cache mapping out of ef_open() and ef_extend() into new ef_remap_cache(). --- src/lib/common/file.c | 111 +++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 35 deletions(-) diff --git a/src/lib/common/file.c b/src/lib/common/file.c index a4f72859..f75ada44 100644 --- a/src/lib/common/file.c +++ b/src/lib/common/file.c @@ -46,8 +46,10 @@ #include "nsc.h" #include "prototypes.h" +static int ef_remap_cache(struct empfile *, int); static int fillcache(struct empfile *, int); static int do_write(struct empfile *, void *, int, int); +static void ef_blank(struct empfile *, void *, int, int); /* * Open the file-backed table TYPE (EF_SECTOR, ...). @@ -61,7 +63,7 @@ ef_open(int type, int how) { struct empfile *ep; struct flock lock; - int oflags, fd, fsiz, size; + int oflags, fd, fsiz, nslots; if (ef_check(type) < 0) return 0; @@ -116,18 +118,13 @@ ef_open(int type, int how) } } } else { - if (how & EFF_MEM) - ep->csize = ep->fids; - else - ep->csize = blksize(fd) / ep->size; - /* 0 could lead to null cache, which confuses assertions */ - if (!ep->csize) - ep->csize++; - size = ep->csize * ep->size; if (CANT_HAPPEN(ep->cache)) free(ep->cache); - ep->cache = malloc(size); - if (ep->cache == NULL) { + if (how & EFF_MEM) + nslots = ep->fids; + else + nslots = blksize(fd) / ep->size; + if (!ef_remap_cache(ep, nslots)) { logerror("Can't open %s: out of memory", ep->file); close(fd); return 0; @@ -151,6 +148,31 @@ ef_open(int type, int how) return 1; } +static int +ef_remap_cache(struct empfile *ep, int nslots) +{ + void *cache; + + if (CANT_HAPPEN(ep->flags & EFF_STATIC)) + return 0; + if (CANT_HAPPEN(nslots < 0)) + nslots = 0; + + /* + * Avoid zero slots, because that can lead to null cache, which + * would be interpreted as unmapped cache. + */ + if (nslots == 0) + nslots++; + cache = realloc(ep->cache, nslots * ep->size); + if (!cache) + return 0; + + ep->cache = cache; + ep->csize = nslots; + return 1; +} + /* * Close the file-backed table TYPE (EF_SECTOR, ...). * Return non-zero on success, zero on failure. @@ -384,47 +406,66 @@ ef_write(int type, int id, void *from) } /* - * Extend the file-backed table TYPE by COUNT elements. - * Can't extend privately mapped tables. + * Extend table TYPE by COUNT elements. + * Any pointers obtained from ef_ptr() become invalid. * Return non-zero on success, zero on failure. */ int ef_extend(int type, int count) { struct empfile *ep; - void *tmpobj; - int id, i, how; + char *p; + int i, id; if (ef_check(type) < 0) return 0; ep = &empfile[type]; - if (CANT_HAPPEN(ep->fd < 0 || count < 0)) + if (CANT_HAPPEN(count < 0)) return 0; - if (CANT_HAPPEN(ep->flags & EFF_PRIVATE)) - return 0; /* not implemented */ - tmpobj = calloc(1, ep->size); id = ep->fids; - for (i = 0; i < count; i++) { - if (ep->init) - ep->init(id + i, tmpobj); - if (do_write(ep, tmpobj, id + i, 1) < 0) - break; - } - free(tmpobj); - if (ep->flags & EFF_MEM) { - /* FIXME lazy bastards... do this right */ - /* XXX this will cause problems if there are ef_ptrs (to the - * old allocated structure) active when we do the re-open */ - how = ep->flags & ~EFF_IMMUTABLE; - ef_close(type); - ef_open(type, how); + if (id + count > ep->csize) { + if (ep->flags & EFF_STATIC) + return 0; + if (!ef_remap_cache(ep, id + count)) + return 0; + } + p = ep->cache + id * ep->size; + ef_blank(ep, p, id, count); + if (!(ep->flags & EFF_PRIVATE)) { + if (do_write(ep, p, id, count) < 0) + return 0; + } + ep->cids += count; } else { - ep->fids += i; + /* need a buffer, steal last cache slot */ + if (ep->cids == ep->csize) + ep->cids--; + p = ep->cache + ep->cids * ep->size; + for (i = 0; i < count; i++) { + ef_blank(ep, p, id + i, 1); + if (do_write(ep, p, id + i, 1) < 0) + return 0; + } } + ep->fids += count; + return 1; +} - return i == count; +/* + * Initialize COUNT elements of EP in BUF, starting with element ID. + */ +static void +ef_blank(struct empfile *ep, void *buf, int id, int count) +{ + int i; + + memset(buf, 0, count * ep->size); + if (ep->init) { + for (i = 0; i < count; i++) + ep->init(id + i, (char *)buf + i * ep->size); + } } struct castr * From f8a35dda1e488efe18dc2bca7019f04d60480ff2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 21 Feb 2008 21:17:32 +0100 Subject: [PATCH 04/20] Make xundump capable of extending tables Use ef_ensure_space() in getobj(). This also makes sure objects are properly initialized before undumping writes to them. Clean up how sentinels are appended: instead of keeping its slot reserved while undumping, keep it in the table, and strip it off when done. --- src/lib/common/xundump.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/lib/common/xundump.c b/src/lib/common/xundump.c index 739bddbc..ed31db4f 100644 --- a/src/lib/common/xundump.c +++ b/src/lib/common/xundump.c @@ -479,17 +479,16 @@ getobj(void) int need_sentinel = !EF_IS_GAME_STATE(cur_type); if (!cur_obj) { - cur_obj_is_blank = cur_id >= ep->fids; + cur_obj_is_blank = cur_id >= ep->fids - !!need_sentinel; if (cur_obj_is_blank) { - /* TODO grow cache (and possibly file) unless EFF_STATIC */ - if (cur_id < ep->csize - !!need_sentinel) - ep->cids = ep->fids = cur_id + 1; - /* else: ef_ptr() will fail */ - } - cur_obj = ef_ptr(cur_type, cur_id); - if (!cur_obj) - gripe("Can't put ID %d into table %s, it holds only 0..%d.", - cur_id, ep->name, ep->fids - 1); + if (ef_ensure_space(cur_type, cur_id + !!need_sentinel, 1)) + cur_obj = ef_ptr(cur_type, cur_id); + /* FIXME diagnose out of dynamic memory vs. static table full */ + if (!cur_obj) + gripe("Can't put ID %d into table %s, it holds only 0..%d.", + cur_id, ep->name, ep->fids - !!need_sentinel - 1); + } else + cur_obj = ef_ptr(cur_type, cur_id); } return cur_obj; @@ -958,8 +957,12 @@ xubody(FILE *fp) { struct empfile *ep = &empfile[cur_type]; int need_sentinel = !EF_IS_GAME_STATE(cur_type); + int old_maxid = ep->fids; int i, maxid, ch; + if (old_maxid == 0 && need_sentinel) + ef_ensure_space(cur_type, 0, 1); + maxid = 0; for (i = 0;; ++i) { while ((ch = skipfs(fp)) == '\n') @@ -974,6 +977,12 @@ xubody(FILE *fp) maxid = MAX(maxid, cur_id + 1); } + if (maxid >= old_maxid && need_sentinel) { + /* appended a sentinel, strip it off */ + ep->fids--; + ep->cids--; + } + if (CANT_HAPPEN(maxid > ep->fids)) maxid = ep->fids; if (maxid < ep->fids) { @@ -988,11 +997,5 @@ xubody(FILE *fp) ef_nameof(cur_type), ep->fids, maxid); } - if (need_sentinel) { - if (CANT_HAPPEN(maxid >= ep->csize)) - return gripe("No space for sentinel"); - memset(ep->cache + ep->size * maxid, 0, ep->size); - } - return i; } From fc807a4c0adf9042270dcc8ed27431070da45d36 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 22 Feb 2008 20:58:37 +0100 Subject: [PATCH 05/20] Make sector types signed get_empobj_chr() and emp_obj_chr_name() access struct sctstr member sct_type through struct empobj member type. This is technically non-portable, because the two differ in signedness. It was also undocumented. Fix by making sct_type signed. sct_newtype as well, for consistency. map_char() uses unsigned char for a sector type argument. Change that to int. Matches how this is done elsewhere. --- include/empobj.h | 3 ++- include/sect.h | 4 ++-- src/lib/global/nsc.c | 4 ++-- src/lib/subs/maps.c | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/empobj.h b/include/empobj.h index 2d46022f..59df1367 100644 --- a/include/empobj.h +++ b/include/empobj.h @@ -56,7 +56,8 @@ struct empobj { short uid; coord x; /* is valid if EFF_XY is set in table def. */ coord y; /* is valid if EFF_XY is set in table def. */ - signed char type; + signed char type; /* is valid for sectors and units */ + /* remaining are valid for units */ signed char effic; signed char mobil; unsigned char off; diff --git a/include/sect.h b/include/sect.h index 18610e49..a960706e 100644 --- a/include/sect.h +++ b/include/sect.h @@ -48,8 +48,8 @@ struct sctstr { short sct_elev; /* elevation/depth */ coord sct_x; /* x coord of sector */ coord sct_y; /* y coord of sector */ + signed char sct_type; /* sector type */ /* end of part matching struct empobj */ - unsigned char sct_type; /* sector type */ unsigned char sct_effic; /* 0% to 100% */ short sct_mobil; /* mobility units */ unsigned char sct_loyal; /* updates until civilans "converted" */ @@ -65,7 +65,7 @@ struct sctstr { short sct_fill; /* gunk */ unsigned char sct_work; /* pct of civ actually working */ unsigned char sct_coastal; /* is this a coastal sector? */ - unsigned char sct_newtype; /* for changing designations */ + signed char sct_newtype; /* for changing designations */ unsigned char sct_min; /* ease of mining ore */ unsigned char sct_gmin; /* amount of gold ore */ unsigned char sct_fertil; /* fertility of soil */ diff --git a/src/lib/global/nsc.c b/src/lib/global/nsc.c index a0889763..0c20baae 100644 --- a/src/lib/global/nsc.c +++ b/src/lib/global/nsc.c @@ -108,7 +108,7 @@ struct castr sect_ca[] = { {NSC_SHORT, NSC_DEITY, 0, fldoff(sctstr, sct_elev), "elev", EF_BAD}, {NSC_XCOORD, NSC_CONST, 0, fldoff(sctstr, sct_x), "xloc", EF_BAD}, {NSC_YCOORD, NSC_CONST, 0, fldoff(sctstr, sct_y), "yloc", EF_BAD}, - {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_type), "des", EF_SECTOR_CHR}, + {NSC_CHAR, 0, 0, fldoff(sctstr, sct_type), "des", EF_SECTOR_CHR}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_effic), "effic", EF_BAD}, {NSC_SHORT, 0, 0, fldoff(sctstr, sct_mobil), "mobil", EF_BAD}, {NSC_UCHAR, NSC_DEITY, 0, fldoff(sctstr, sct_loyal), "loyal", EF_BAD}, @@ -123,7 +123,7 @@ struct castr sect_ca[] = { {NSC_SHORT, 0, 0, fldoff(sctstr, sct_avail), "avail", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_work), "work", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_coastal), "coastal", EF_BAD}, - {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_newtype), "newdes", EF_SECTOR_CHR}, + {NSC_CHAR, 0, 0, fldoff(sctstr, sct_newtype), "newdes", EF_SECTOR_CHR}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_min), "min", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_gmin), "gold", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_fertil), "fert", EF_BAD}, diff --git a/src/lib/subs/maps.c b/src/lib/subs/maps.c index b60d5d67..4990e5ab 100644 --- a/src/lib/subs/maps.c +++ b/src/lib/subs/maps.c @@ -54,7 +54,7 @@ #include "xy.h" static int bmnxtsct(struct nstr_sect *); -static char map_char(unsigned char type, natid own, int owner_or_god); +static char map_char(int type, natid own, int owner_or_god); int do_map(int bmap, int unit_type, char *arg, char *map_flags_arg) @@ -330,7 +330,7 @@ bmnxtsct(struct nstr_sect *np) * If OWNER_OR_GOD, the map is for the sector's owner or a deity. */ static char -map_char(unsigned char type, natid own, int owner_or_god) +map_char(int type, natid own, int owner_or_god) { if (CANT_HAPPEN(type > SCT_TYPE_MAX || !dchr[type].d_mnem)) return '?'; From 5a1cfeba95e8f2fd36477186900b2f64f4254d4b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 22 Feb 2008 21:11:30 +0100 Subject: [PATCH 06/20] Fix test for water in explore This led to a bogus message when an interactive explore moved onto a bridge and got prompted, the bridge was destroyed, and the player stopped the explore "on the water". --- src/lib/commands/expl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/commands/expl.c b/src/lib/commands/expl.c index 44988339..4ed6590c 100644 --- a/src/lib/commands/expl.c +++ b/src/lib/commands/expl.c @@ -196,7 +196,7 @@ explore(void) putsect(§); return RET_FAIL; } - if (chksect.sct_type == '.') { + if (chksect.sct_type == SCT_WATER) { pr("Bridge disappeared!\n"); getsect(start.sct_x, start.sct_y, &start); start.sct_flags &= ~MOVE_IN_PROGRESS; From 38ead5e19936315dc27e9df5ca843091e6fc556f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 22 Feb 2008 21:13:32 +0100 Subject: [PATCH 07/20] Clean up ugly line break --- src/lib/commands/nati.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/commands/nati.c b/src/lib/commands/nati.c index 3bc9a7d2..4e42ead0 100644 --- a/src/lib/commands/nati.c +++ b/src/lib/commands/nati.c @@ -74,8 +74,7 @@ nati(void) mil = sect.sct_item[I_MILIT]; pr("%d%% eff %s at %s has %d civilian%s & %d military\n", sect.sct_effic, - (sect.sct_type == - SCT_CAPIT ? "capital" : "mountain capital"), + sect.sct_type == SCT_CAPIT ? "capital" : "mountain capital", xyas(sect.sct_x, sect.sct_y, cnum), civ, splur(civ), mil); } From 2dea1b914d954b35c6582f9ba6d09bf7b42b24a5 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 22 Feb 2008 21:37:04 +0100 Subject: [PATCH 08/20] New realm selector timestamp The timestamp always existed, but the selector was missing. It's needed for incremental xdumps. --- src/lib/global/nsc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/global/nsc.c b/src/lib/global/nsc.c index 0c20baae..b23987e3 100644 --- a/src/lib/global/nsc.c +++ b/src/lib/global/nsc.c @@ -542,6 +542,8 @@ struct castr realm_ca[] = { {NSC_SHORT, 0, 0, fldoff(realmstr, r_xh), "xh", EF_BAD}, {NSC_SHORT, 0, 0, fldoff(realmstr, r_yl), "yl", EF_BAD}, {NSC_SHORT, 0, 0, fldoff(realmstr, r_yh), "yh", EF_BAD}, + {NSC_TIME, NSC_EXTRA, 0, fldoff(realmstr, r_timestamp), "timestamp", + EF_BAD}, {NSC_NOTYPE, 0, 0, 0, NULL, EF_BAD} }; From 3117ef9c7a575cd8a2fdb48d9f369c38476d376c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 22 Feb 2008 21:53:24 +0100 Subject: [PATCH 09/20] Give unit_map() internal linkage --- include/prototypes.h | 1 - src/lib/subs/maps.c | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/prototypes.h b/include/prototypes.h index 080bebcc..9dd12a0a 100644 --- a/include/prototypes.h +++ b/include/prototypes.h @@ -480,7 +480,6 @@ extern int islist(char *); /* maps.c */ extern int do_map(int bmap, int unit_type, char *arg1, char *arg2); extern int draw_map(int, char, int, struct nstr_sect *); -extern int unit_map(int, int, struct nstr_sect *, char *); extern int display_region_map(int bmap, int unit_type, coord curx, coord cury, char *arg); extern int bmaps_intersect(natid, natid); diff --git a/src/lib/subs/maps.c b/src/lib/subs/maps.c index 4990e5ab..5d2eab65 100644 --- a/src/lib/subs/maps.c +++ b/src/lib/subs/maps.c @@ -54,7 +54,8 @@ #include "xy.h" static int bmnxtsct(struct nstr_sect *); -static char map_char(int type, natid own, int owner_or_god); +static char map_char(int, natid, int); +static int unit_map(int, int, struct nstr_sect *, char *); int do_map(int bmap, int unit_type, char *arg, char *map_flags_arg) @@ -341,7 +342,7 @@ map_char(int type, natid own, int owner_or_god) return '?'; } -int +static int unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp) { struct empobj *gp; From e63f38833bd218494d993f2668e460d84a28da13 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 23 Feb 2008 07:48:32 +0100 Subject: [PATCH 10/20] Fix put_empobj() for types other than units put_empobj() used struct empobj member uid, which is valid only for units. Existing users pass only units, fortunately. Fix by making it take type and uid parameters. --- include/empobj.h | 2 +- src/lib/commands/miss.c | 2 +- src/lib/subs/empobj.c | 14 ++------------ src/lib/subs/unitsub.c | 2 +- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/include/empobj.h b/include/empobj.h index 59df1367..364f8e66 100644 --- a/include/empobj.h +++ b/include/empobj.h @@ -91,7 +91,7 @@ struct empobj_chr; extern char *obj_nameof(struct empobj *gp); extern struct empobj *get_empobjp(int type, int id); -extern int put_empobj(struct empobj *gp); +extern int put_empobj(int, int, struct empobj *gp); extern struct empobj_chr *get_empobj_chr(struct empobj *gp); extern char *emp_obj_chr_name(struct empobj *gp); extern int get_empobj_mob_max(int type); diff --git a/src/lib/commands/miss.c b/src/lib/commands/miss.c index b60ff783..d859d564 100644 --- a/src/lib/commands/miss.c +++ b/src/lib/commands/miss.c @@ -321,7 +321,7 @@ mission(void) gp->mission = mission; gp->opx = x; gp->opy = y; - put_empobj(gp); + put_empobj(type, gp->uid, gp); } if (num == 0) { pr("No %s%s\n", ef_nameof(type), splur(num)); diff --git a/src/lib/subs/empobj.c b/src/lib/subs/empobj.c index 35a18d07..2a1cca6b 100644 --- a/src/lib/subs/empobj.c +++ b/src/lib/subs/empobj.c @@ -66,19 +66,9 @@ get_empobjp(int type, int id) } int -put_empobj(struct empobj *gp) +put_empobj(int type, int id, struct empobj *gp) { - switch (gp->ef_type) - { - case EF_SECTOR: - return ef_write(gp->ef_type, sctoff(gp->x, gp->y), gp); - case EF_NATION: - case EF_BMAP: - case EF_MAP: - return ef_write(gp->ef_type, gp->own, gp); - default: - return ef_write(gp->ef_type, gp->uid, gp); - } + return ef_write(type, id, gp); } struct empobj_chr * diff --git a/src/lib/subs/unitsub.c b/src/lib/subs/unitsub.c index ebc341c5..bc13c745 100644 --- a/src/lib/subs/unitsub.c +++ b/src/lib/subs/unitsub.c @@ -118,7 +118,7 @@ unit_put(struct emp_qelem *list, natid actor) } if (ulp->unit.ef_type == EF_SHIP) ulp->unit.ship.shp_mobil = (int)ulp->mobil; - put_empobj(&ulp->unit.gen); + put_empobj(ulp->unit.gen.ef_type, ulp->unit.gen.uid, &ulp->unit.gen); newqp = qp->q_back; emp_remque(qp); free(qp); From 5b7a6af94e344cc9ae6fd7bd6416b9afa7d3b2b4 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 23 Feb 2008 07:59:10 +0100 Subject: [PATCH 11/20] Fix get_empobjp() not to reject EF_SECTOR There's no technical reason for rejecting sector access by id. It's unusual, but not wrong. Also remove the superfluous test for EF_BAD; ef_ptr() covers that. --- src/lib/subs/empobj.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/subs/empobj.c b/src/lib/subs/empobj.c index 2a1cca6b..9df80682 100644 --- a/src/lib/subs/empobj.c +++ b/src/lib/subs/empobj.c @@ -60,8 +60,6 @@ obj_nameof(struct empobj *gp) struct empobj * get_empobjp(int type, int id) { - if (CANT_HAPPEN(type == EF_SECTOR || type == EF_BAD)) - return NULL; return ef_ptr(type, id); } From 9a1fef87fec3007c6791f202308d75690f6bb326 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 23 Feb 2008 08:03:35 +0100 Subject: [PATCH 12/20] Define common get/put macros for empobj This turns existing functions get_empobjp() and put_empobj() into equivalent macros. --- include/empobj.h | 6 ++++-- src/lib/subs/empobj.c | 12 ------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/include/empobj.h b/include/empobj.h index 364f8e66..97dc00e5 100644 --- a/include/empobj.h +++ b/include/empobj.h @@ -89,9 +89,11 @@ union empobj_storage { struct empobj_chr; +#define get_empobj(type, n, p) ef_read((type), (n), (p)) +#define put_empobj(type, n, p) ef_write((type), (n), (p)) +#define get_empobjp(type, n) ((struct empobj *)ef_ptr((type), (n))) + extern char *obj_nameof(struct empobj *gp); -extern struct empobj *get_empobjp(int type, int id); -extern int put_empobj(int, int, struct empobj *gp); extern struct empobj_chr *get_empobj_chr(struct empobj *gp); extern char *emp_obj_chr_name(struct empobj *gp); extern int get_empobj_mob_max(int type); diff --git a/src/lib/subs/empobj.c b/src/lib/subs/empobj.c index 9df80682..3fb59adf 100644 --- a/src/lib/subs/empobj.c +++ b/src/lib/subs/empobj.c @@ -57,18 +57,6 @@ obj_nameof(struct empobj *gp) return "The Beast #666"; } -struct empobj * -get_empobjp(int type, int id) -{ - return ef_ptr(type, id); -} - -int -put_empobj(int type, int id, struct empobj *gp) -{ - return ef_write(type, id, gp); -} - struct empobj_chr * get_empobj_chr(struct empobj *gp) { From e7e61687f4851ea4c929a46ce9a08e4e08edca85 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 23 Feb 2008 08:26:04 +0100 Subject: [PATCH 13/20] Use get_empobj() instead of get_empobjp() in unit_map() The get_FOOp() macros are generally avoided outside the update, because direct access to the sector cache needs synchronization to be safe. unit_map() didn't access the cache directly until it was converted from get_ship() & friends to get_empobjp() in commit fec9878c. Switching to get_empobj() reverts the change to direct access while keeping the simplification. --- src/lib/subs/maps.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lib/subs/maps.c b/src/lib/subs/maps.c index 5d2eab65..6b2c6e36 100644 --- a/src/lib/subs/maps.c +++ b/src/lib/subs/maps.c @@ -345,26 +345,27 @@ map_char(int type, natid own, int owner_or_god) static int unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp) { - struct empobj *gp; + union empobj_storage unit; struct range range; char *name; - gp = get_empobjp(unit_type, uid); - if (!gp || (gp->own != player->cnum && !player->god) || gp->own == 0) + if (!get_empobj(unit_type, uid, &unit)) + return RET_FAIL; + if (!player->owner || unit.gen.own == 0) return RET_FAIL; if (unit_type == EF_NUKE) *originp = 'n'; else { - if ((name = emp_obj_chr_name(gp)) == NULL) + if ((name = emp_obj_chr_name(&unit.gen)) == NULL) return RET_FAIL; *originp = *name; } - range.lx = xnorm(gp->x - 10); - range.hx = xnorm(gp->x + 11); - range.ly = ynorm(gp->y - 5); - range.hy = ynorm(gp->y + 6); + range.lx = xnorm(unit.gen.x - 10); + range.hx = xnorm(unit.gen.x + 11); + range.ly = ynorm(unit.gen.y - 5); + range.hy = ynorm(unit.gen.y + 6); xysize_range(&range); snxtsct_area(nsp, &range); return RET_OK; From 1aa3cdb0cad9e8c9082a14157f729d2fd1a08c9e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 23 Feb 2008 08:35:13 +0100 Subject: [PATCH 14/20] Simplify unit_put() --- src/lib/subs/unitsub.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lib/subs/unitsub.c b/src/lib/subs/unitsub.c index bc13c745..cc895d89 100644 --- a/src/lib/subs/unitsub.c +++ b/src/lib/subs/unitsub.c @@ -102,23 +102,24 @@ unit_put(struct emp_qelem *list, natid actor) struct emp_qelem *qp; struct emp_qelem *newqp; struct ulist *ulp; + struct empobj *unit; qp = list->q_back; while (qp != list) { ulp = (struct ulist *)qp; + unit = &ulp->unit.gen; if (actor) { - mpr(actor, "%s stopped at %s\n", obj_nameof(&ulp->unit.gen), - xyas(ulp->unit.gen.x, ulp->unit.gen.y, - ulp->unit.gen.own)); - if (ulp->unit.ef_type == EF_LAND) { + mpr(actor, "%s stopped at %s\n", obj_nameof(unit), + xyas(unit->x, unit->y, unit->own)); + if (unit->ef_type == EF_LAND) { if (ulp->mobil < -127) ulp->mobil = -127; - ulp->unit.land.lnd_mobil = ulp->mobil; + unit->mobil = ulp->mobil; } } - if (ulp->unit.ef_type == EF_SHIP) - ulp->unit.ship.shp_mobil = (int)ulp->mobil; - put_empobj(ulp->unit.gen.ef_type, ulp->unit.gen.uid, &ulp->unit.gen); + if (unit->ef_type == EF_SHIP) + unit->mobil = (int)ulp->mobil; + put_empobj(unit->ef_type, unit->uid, unit); newqp = qp->q_back; emp_remque(qp); free(qp); From 3f15ea4beabddb487c80e8c26ec57310d5b59d31 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 25 Feb 2008 20:37:01 +0100 Subject: [PATCH 15/20] Document tables EF_COMM, EF_TRADE and EF_COUNTRY have an owner xdvisible() relies on that. Set EFF_OWNER in the table definitions. EFF_OWNER is not actually used right now, so this doesn't fix anything broken. --- include/commodity.h | 2 ++ include/trade.h | 2 ++ src/lib/global/file.c | 7 ++++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/commodity.h b/include/commodity.h index cba8f557..41573303 100644 --- a/include/commodity.h +++ b/include/commodity.h @@ -41,9 +41,11 @@ #include "item.h" struct comstr { + /* initial part must match struct empobj */ short ef_type; natid com_owner; short com_uid; + /* end of part matching struct empobj */ i_type com_type; int com_amount; float com_price; diff --git a/include/trade.h b/include/trade.h index da1b5a8e..45b4dfe0 100644 --- a/include/trade.h +++ b/include/trade.h @@ -41,9 +41,11 @@ #include "types.h" struct trdstr { + /* initial part must match struct empobj */ short ef_type; natid trd_owner; short trd_uid; + /* end of part matching struct empobj */ short trd_type; short trd_unitid; long trd_price; diff --git a/src/lib/global/file.c b/src/lib/global/file.c index 9db598fc..0a0031a6 100644 --- a/src/lib/global/file.c +++ b/src/lib/global/file.c @@ -131,7 +131,7 @@ struct empfile empfile[] = { {EF_TREATY, "treaty", "treaty", treaty_ca, UNMAPPED_CACHE(struct trtstr, 0)}, {EF_TRADE, "trade", "trade", trade_ca, - UNMAPPED_CACHE(struct trdstr, 0)}, + UNMAPPED_CACHE(struct trdstr, EFF_OWNER)}, {EF_POWER, "pow", "power", NULL, UNMAPPED_CACHE(struct powstr, 0)}, {EF_NATION, "nat", "nation", nat_ca, @@ -143,7 +143,7 @@ struct empfile empfile[] = { {EF_BMAP, "bmap", "bmap", NULL, 0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL}, {EF_COMM, "commodity", "commodity", commodity_ca, - UNMAPPED_CACHE(struct comstr, 0)}, + UNMAPPED_CACHE(struct comstr, EFF_OWNER)}, {EF_LOST, "lost", "lostitems", lost_ca, UNMAPPED_CACHE(struct loststr, EFF_OWNER)}, {EF_REALM, "realm", "realms", realm_ca, @@ -214,7 +214,8 @@ struct empfile empfile[] = { SYMTAB(EF_TREATY_FLAGS, "treaty-flags", treaty_flags), /* Views */ - {EF_COUNTRY, "country", NULL, cou_ca, UNMAPPED_CACHE(struct natstr, 0)}, + {EF_COUNTRY, "country", NULL, cou_ca, + UNMAPPED_CACHE(struct natstr, EFF_OWNER)}, /* Sentinel */ {EF_BAD, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL}, From d83dd2f5e4aeb19969c6e5d6c0f77ebd0d1055bb Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 8 Feb 2008 21:12:43 +0100 Subject: [PATCH 16/20] Prepare some xdump code for future use outside the server We don't have player->cnum, player->god and pr() there. Pass parameters instead. --- src/lib/commands/xdump.c | 125 ++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 49 deletions(-) diff --git a/src/lib/commands/xdump.c b/src/lib/commands/xdump.c index 9e93a2f5..f1ca0076 100644 --- a/src/lib/commands/xdump.c +++ b/src/lib/commands/xdump.c @@ -28,7 +28,7 @@ * xdump.c: Extended dump * * Known contributors to this file: - * Markus Armbruster, 2004-2007 + * Markus Armbruster, 2004-2008 */ /* @@ -85,8 +85,30 @@ #include "treaty.h" #include "version.h" +/* xdump descriptor */ +struct xdstr { + natid cnum; /* dump for this country */ + int divine; /* is this a deity dump? */ + void (*pr)(char *fmt, ...); /* callback for printing dump */ +}; + +/* + * Initialize XD to dump for country CNUM. + * Dump is to be delivered through callback PR. + * Return XD. + */ +static struct xdstr * +xdinit(struct xdstr *xd, natid cnum, void (*pr)(char *fmt, ...)) +{ + xd->cnum = cnum; + xd->divine = getnatp(cnum)->nat_stat == STAT_GOD; + xd->pr = pr; + return xd; +} + /* * Evaluate a attribute of an object into VAL, return VAL. + * XD is the xdump descriptor. * TYPE is the attribute's type. * PTR points to the context object. * The attribute is stored there at offset OFF + IDX * S, where S is @@ -94,7 +116,7 @@ * LEN is the #array elements if it is an array, else zero. */ static struct valstr * -xdeval(struct valstr *val, +xdeval(struct valstr *val, struct xdstr *xd, nsc_type type, void *ptr, ptrdiff_t off, int idx, int len) { val->val_type = type; @@ -102,27 +124,27 @@ xdeval(struct valstr *val, val->val_as.sym.off = off; val->val_as.sym.len = len; val->val_as.sym.idx = idx; - nstr_exec_val(val, player->cnum, ptr, NSC_NOTYPE); + nstr_exec_val(val, xd->cnum, ptr, NSC_NOTYPE); return val; /* FIXME nstr_exec_val() should return VAL */ } /* Dump VAL prefixed with SEP, return " ". */ static char * -xdprval(struct valstr *val, char *sep) +xdprval(struct xdstr *xd, struct valstr *val, char *sep) { unsigned char *s, *e, *l; switch (val->val_type) { case NSC_LONG: - pr("%s%ld", sep, val->val_as.lng); + xd->pr("%s%ld", sep, val->val_as.lng); break; case NSC_DOUBLE: - pr("%s%#g", sep, val->val_as.dbl); + xd->pr("%s%#g", sep, val->val_as.dbl); break; case NSC_STRING: s = (unsigned char *)val->val_as.str.base; if (s) { - pr("%s\"", sep); + xd->pr("%s\"", sep); l = s + val->val_as.str.maxsz; /* FIXME maxsz == INT_MAX ! */ for (;;) { @@ -130,46 +152,47 @@ xdprval(struct valstr *val, char *sep) e < l && *e != '"' && *e != '\\' && isgraph(*e); ++e) ; - pr("%.*s", (int)(e-s), s); + xd->pr("%.*s", (int)(e-s), s); if (e < l && *e) - pr("\\%03o", *e++); + xd->pr("\\%03o", *e++); else break; s = e; } - pr("\""); + xd->pr("\""); } else - pr("%snil", sep); + xd->pr("%snil", sep); break; default: CANT_REACH(); - pr("0"); + xd->pr("0"); } return " "; } /* - * Dump field values of a context object. + * Dump field values of a context object to XD. * CA[] describes fields. * PTR points to context object. */ static void -xdflds(struct castr ca[], void *ptr) +xdflds(struct xdstr *xd, struct castr ca[], void *ptr) { int i, j, n; struct valstr val; char *sep = ""; for (i = 0; ca[i].ca_name; ++i) { - if (ca[i].ca_flags & NSC_DEITY && !player->god) + 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; j = 0; do { - xdeval(&val, ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len); - sep = xdprval(&val, sep); + xdeval(&val, xd, + ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len); + sep = xdprval(xd, &val, sep); } while (++j < n); } } @@ -179,16 +202,16 @@ xdflds(struct castr ca[], void *ptr) * If META, it's for the meta-data dump rather than the data dump. */ static void -xdhdr(char *name, int meta) +xdhdr(struct xdstr *xd, char *name, int meta) { - pr("XDUMP %s%s %ld\n", meta ? "meta " : "", name, (long)time(NULL)); + xd->pr("XDUMP %s%s %ld\n", meta ? "meta " : "", name, (long)time(NULL)); } /* Dump footer for a dump that dumped N objects. */ static void -xdftr(int n) +xdftr(struct xdstr *xd, int n) { - pr("/%d\n", n); + xd->pr("/%d\n", n); } /* @@ -265,11 +288,11 @@ xdvisible(int type, void *p) } /* - * Dump items of type TYPE selected by ARG. + * Dump items of type TYPE selected by ARG to XD. * Return RET_OK on success, RET_SYN on error. */ static int -xditem(int type, char *arg) +xditem(struct xdstr *xd, int type, char *arg) { struct castr *ca; struct nstr_item ni; @@ -283,28 +306,28 @@ xditem(int type, char *arg) if (!snxtitem(&ni, type, arg)) return RET_SYN; - xdhdr(ef_nameof(type), 0); + xdhdr(xd, ef_nameof(type), 0); n = 0; while (nxtitem(&ni, buf)) { if (!xdvisible(type, buf)) continue; ++n; - xdflds(ca, buf); - pr("\n"); + xdflds(xd, ca, buf); + xd->pr("\n"); } - xdftr(n); + xdftr(xd, n); return RET_OK; } /* - * Dump meta-data for items of type TYPE. + * Dump meta-data for items of type TYPE to XD. * Return RET_OK. */ static int -xdmeta(int type) +xdmeta(struct xdstr *xd, int type) { struct castr *ca = ef_cadef(type); int i; @@ -313,29 +336,30 @@ xdmeta(int type) if (!ca) return RET_SYN; - xdhdr(ef_nameof(type), 1); + xdhdr(xd, ef_nameof(type), 1); for (i = 0; ca[i].ca_name; i++) { - if (ca[i].ca_flags & NSC_DEITY && !player->god) + if (ca[i].ca_flags & NSC_DEITY && !xd->divine) continue; if (ca[i].ca_flags & NSC_EXTRA) continue; - xdflds(mdchr_ca, &ca[i]); - pr("\n"); + xdflds(xd, mdchr_ca, &ca[i]); + xd->pr("\n"); n++; } - xdftr(n); + xdftr(xd, n); return RET_OK; } /* - * Dump configkeys[], return RET_OK. + * Dump configkeys[] to XD. * If META, dump meta-data rather than data. + * Return RET_OK. */ static int -xdver(int meta) +xdver(struct xdstr *xd, int meta) { static struct castr vers_ca = { NSC_STRINGY, 0, sizeof(PACKAGE_STRING), 0, "version", EF_BAD @@ -346,11 +370,11 @@ xdver(int meta) struct castr ca; struct valstr val; - xdhdr("version", meta); + xdhdr(xd, "version", meta); if (meta) { n = 0; - xdflds(mdchr_ca, &vers_ca); + xdflds(xd, mdchr_ca, &vers_ca); pr("\n"); n++; for (kp = configkeys; kp->km_key; ++kp) { @@ -361,26 +385,27 @@ xdver(int meta) ca.ca_off = 0; ca.ca_name = kp->km_key; ca.ca_table = EF_BAD; - xdflds(mdchr_ca, &ca); + xdflds(xd, mdchr_ca, &ca); pr("\n"); n++; } } - xdftr(n); + xdftr(xd, n); return RET_OK; } - xdeval(&val, vers_ca.ca_type, version, vers_ca.ca_off, 0, vers_ca.ca_len); - sep = xdprval(&val, ""); + xdeval(&val, xd, + vers_ca.ca_type, version, vers_ca.ca_off, 0, vers_ca.ca_len); + sep = xdprval(xd, &val, ""); for (kp = configkeys; kp->km_key; ++kp) { if (kp->km_type != NSC_NOTYPE && !(kp->km_flags & KM_INTERNAL)) { - xdeval(&val, kp->km_type, kp->km_data, 0, 0, 0); - sep = xdprval(&val, sep); + xdeval(&val, xd, kp->km_type, kp->km_data, 0, 0, 0); + sep = xdprval(xd, &val, sep); } } pr("\n"); - xdftr(1); + xdftr(xd, 1); return RET_OK; } @@ -391,9 +416,10 @@ xdump(void) { char *p; char buf[1024]; + struct xdstr xd; + struct natstr *natp; int type; int meta = 0; - struct natstr *natp; p = getstarg(player->argp[1], "Table name, or meta? ", buf); if (p && strcmp(p, "meta") == 0) { @@ -403,19 +429,20 @@ xdump(void) if (!p || !*p) return RET_SYN; + xdinit(&xd, player->cnum, pr); natp = getnatp(player->cnum); type = isdigit(p[0]) ? atoi(p) : ef_byname(p); if (type >= 0 && type < EF_MAX) { if (meta) - return xdmeta(type); + return xdmeta(&xd, type); else if ((EF_IS_GAME_STATE(type) || EF_IS_VIEW(type)) && !(natp->nat_stat == STAT_ACTIVE || player->god)) { pr("Access to table %s denied\n", ef_nameof(type)); return RET_FAIL; } else - return xditem(type, player->argp[2]); + return xditem(&xd, type, player->argp[2]); } else if (!strncmp(p, "ver", strlen(p))) { - return xdver(meta); + return xdver(&xd, meta); } return RET_SYN; From b9c725224e11df61fba6bb16cc84d8abc742ea01 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 8 Feb 2008 21:51:24 +0100 Subject: [PATCH 17/20] Make xdump code available for future use outside the server Move the bits useful there from src/lib/commands/xdump.c to new include/xdump.h and src/lib/common/xdump.c. --- include/prototypes.h | 4 +- include/xdump.h | 57 ++++++++++ src/lib/commands/xdump.c | 208 +--------------------------------- src/lib/common/conftab.c | 1 + src/lib/common/xdump.c | 234 +++++++++++++++++++++++++++++++++++++++ src/lib/common/xundump.c | 4 +- 6 files changed, 298 insertions(+), 210 deletions(-) create mode 100644 include/xdump.h create mode 100644 src/lib/common/xdump.c diff --git a/include/prototypes.h b/include/prototypes.h index 9dd12a0a..623b509f 100644 --- a/include/prototypes.h +++ b/include/prototypes.h @@ -315,8 +315,8 @@ extern int sct_typematch(char *); extern int demand_update_want(int *, int *, int); extern int demand_check(void); extern int demandupdatecheck(void); -/* xundump.c */ -extern int xundump(FILE *, char *, int *, int); +/* xdump.c xundump.c */ +/* in xdump.h */ /* * src/lib/gen/ *.c diff --git a/include/xdump.h b/include/xdump.h new file mode 100644 index 00000000..5e39115b --- /dev/null +++ b/include/xdump.h @@ -0,0 +1,57 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * xdump.h: Extended dumps + * + * Known contributors to this file: + * Markus Armbruster, 2008 + */ + +#ifndef XDUMP_H +#define XDUMP_H + +#include +#include "nsc.h" +#include "types.h" + +/* xdump descriptor */ +struct xdstr { + natid cnum; /* dump for this country */ + int divine; /* is this a deity dump? */ + void (*pr)(char *fmt, ...); /* callback for printing dump */ +}; + +struct xdstr *xdinit(struct xdstr *, natid, void (*)(char *, ...)); +extern void xdhdr(struct xdstr *, char *, int); +extern void xdflds(struct xdstr *, struct castr[], void *); +extern struct valstr *xdeval(struct valstr *, struct xdstr *, nsc_type, void *, ptrdiff_t, int, int); +extern char *xdprval(struct xdstr *, struct valstr *, char *); +extern void xdftr(struct xdstr *, int); +extern int xdmeta(struct xdstr *, int); +extern int xundump(FILE *, char *, int *, int); + +#endif diff --git a/src/lib/commands/xdump.c b/src/lib/commands/xdump.c index f1ca0076..79ae41a2 100644 --- a/src/lib/commands/xdump.c +++ b/src/lib/commands/xdump.c @@ -31,188 +31,14 @@ * Markus Armbruster, 2004-2008 */ -/* - * Dump everything under the sun - * - * Static game data (configuration): - * - Item characteristics: ichr[] - * - Land unit characteristics: lchr[] - * - Nuke characteristics: nchr[] - * - Plane characteristics: plchr[] - * - Product characteristics: pchr[] - * - Sector designation characteristics: dchr[] - * - Sector infrastructure characteristics: intrchr[] - * - Ship characteristics: mchr[] - * - News item characteristics: rpt[] - * - News page headings: page_headings[] - * - Commands: player_coms[] (TODO) - * - Update schedule: update_time[] (not really static) - * - Configuration: configkeys[] - * - * Dynamic game data: - * - Sectors: EF_SECTOR (superseding dump) - * - Land units: EF_LAND (superseding ldump) - * - Lost: EF_LOST (superseding lost) - * - Nukes: EF_NUKE (superseding ndump) - * - Planes: EF_PLANE (superseding pdump) - * - Ships: EF_SHIP (superseding sdump) - * - News: EF_NEWS - * - Treaties: EF_TREATY - * - Power: EF_POWER (TODO) - * - Nations: EF_NATION - * - Loans: EF_LOAN - * - Map: EF_MAP (TODO) - * - Bmap: EF_BMAP (TODO) - * - Market: EF_COMM - */ - -/* - * See doc/xdump for motivation, syntax, semantics, and rationale. - * Make sure to keep it up-to-date! - */ - -/* TODO don't dump stuff that's useless due to options */ - #include #include -#include #include "commands.h" #include "empobj.h" -#include "match.h" -#include "news.h" #include "optlist.h" -#include "treaty.h" #include "version.h" - -/* xdump descriptor */ -struct xdstr { - natid cnum; /* dump for this country */ - int divine; /* is this a deity dump? */ - void (*pr)(char *fmt, ...); /* callback for printing dump */ -}; - -/* - * Initialize XD to dump for country CNUM. - * Dump is to be delivered through callback PR. - * Return XD. - */ -static struct xdstr * -xdinit(struct xdstr *xd, natid cnum, void (*pr)(char *fmt, ...)) -{ - xd->cnum = cnum; - xd->divine = getnatp(cnum)->nat_stat == STAT_GOD; - xd->pr = pr; - return xd; -} - -/* - * Evaluate a attribute of an object into VAL, return VAL. - * XD is the xdump descriptor. - * TYPE is the attribute's type. - * PTR points to the context object. - * The attribute is stored there at offset OFF + IDX * S, where S is - * its size. - * LEN is the #array elements if it is an array, else zero. - */ -static struct valstr * -xdeval(struct valstr *val, struct xdstr *xd, - nsc_type type, void *ptr, ptrdiff_t off, int idx, int len) -{ - val->val_type = type; - val->val_cat = NSC_OFF; - val->val_as.sym.off = off; - val->val_as.sym.len = len; - val->val_as.sym.idx = idx; - nstr_exec_val(val, xd->cnum, ptr, NSC_NOTYPE); - return val; /* FIXME nstr_exec_val() should return VAL */ -} - -/* Dump VAL prefixed with SEP, return " ". */ -static char * -xdprval(struct xdstr *xd, struct valstr *val, char *sep) -{ - unsigned char *s, *e, *l; - - switch (val->val_type) { - case NSC_LONG: - xd->pr("%s%ld", sep, val->val_as.lng); - break; - case NSC_DOUBLE: - xd->pr("%s%#g", sep, val->val_as.dbl); - break; - case NSC_STRING: - s = (unsigned char *)val->val_as.str.base; - if (s) { - 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; - } - xd->pr("\""); - } else - xd->pr("%snil", sep); - break; - default: - CANT_REACH(); - xd->pr("0"); - } - return " "; -} - -/* - * Dump field values of a context object to XD. - * CA[] describes fields. - * PTR points to context object. - */ -static void -xdflds(struct xdstr *xd, struct castr ca[], void *ptr) -{ - int i, j, n; - struct valstr val; - char *sep = ""; - - 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; - j = 0; - do { - xdeval(&val, xd, - ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len); - sep = xdprval(xd, &val, sep); - } while (++j < n); - } -} - -/* - * Dump header for dump NAME. - * If META, it's for the meta-data dump rather than the data dump. - */ -static void -xdhdr(struct xdstr *xd, char *name, int meta) -{ - xd->pr("XDUMP %s%s %ld\n", meta ? "meta " : "", name, (long)time(NULL)); -} - -/* Dump footer for a dump that dumped N objects. */ -static void -xdftr(struct xdstr *xd, int n) -{ - xd->pr("/%d\n", n); -} +#include "xdump.h" /* * Is object P of type TYPE visible to the player? @@ -322,37 +148,6 @@ xditem(struct xdstr *xd, int type, char *arg) return RET_OK; } -/* - * Dump meta-data for items of type TYPE to XD. - * Return RET_OK. - */ -static int -xdmeta(struct xdstr *xd, int type) -{ - struct castr *ca = ef_cadef(type); - int i; - int n = 0; - - if (!ca) - return RET_SYN; - - xdhdr(xd, ef_nameof(type), 1); - - 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; - xdflds(xd, mdchr_ca, &ca[i]); - xd->pr("\n"); - n++; - } - - xdftr(xd, n); - - return RET_OK; -} - /* * Dump configkeys[] to XD. * If META, dump meta-data rather than data. @@ -410,7 +205,6 @@ xdver(struct xdstr *xd, int meta) return RET_OK; } -/* Experimental extended dump command */ int xdump(void) { diff --git a/src/lib/common/conftab.c b/src/lib/common/conftab.c index 980c710d..2848e23e 100644 --- a/src/lib/common/conftab.c +++ b/src/lib/common/conftab.c @@ -39,6 +39,7 @@ #include "optlist.h" #include "product.h" #include "prototypes.h" +#include "xdump.h" static int read_custom_table_file(char *); diff --git a/src/lib/common/xdump.c b/src/lib/common/xdump.c new file mode 100644 index 00000000..5e7f5a0c --- /dev/null +++ b/src/lib/common/xdump.c @@ -0,0 +1,234 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * xdump.c: Extended dumps + * + * Known contributors to this file: + * Markus Armbruster, 2008 + */ + +/* + * Dump everything under the sun + * + * Static game data (configuration): + * - Item characteristics: ichr[] + * - Land unit characteristics: lchr[] + * - Nuke characteristics: nchr[] + * - Plane characteristics: plchr[] + * - Product characteristics: pchr[] + * - Sector designation characteristics: dchr[] + * - Sector infrastructure characteristics: intrchr[] + * - Ship characteristics: mchr[] + * - News item characteristics: rpt[] + * - News page headings: page_headings[] + * - Commands: player_coms[] (TODO) + * - Update schedule: update_time[] (not really static) + * - Configuration: configkeys[] + * + * Dynamic game data: + * - Sectors: EF_SECTOR (superseding dump) + * - Land units: EF_LAND (superseding ldump) + * - Lost: EF_LOST (superseding lost) + * - Nukes: EF_NUKE (superseding ndump) + * - Planes: EF_PLANE (superseding pdump) + * - Ships: EF_SHIP (superseding sdump) + * - News: EF_NEWS + * - Treaties: EF_TREATY + * - Power: EF_POWER (TODO) + * - Nations: EF_NATION + * - Loans: EF_LOAN + * - Map: EF_MAP (TODO) + * - Bmap: EF_BMAP (TODO) + * - Market: EF_COMM + */ + +/* + * See doc/xdump for motivation, syntax, semantics, and rationale. + * Make sure to keep it up-to-date! + */ + +/* TODO don't dump stuff that's useless due to options */ + +#include + +#include +#include "file.h" +#include "nat.h" +#include "xdump.h" + +/* + * Initialize XD to dump for country CNUM. + * Dump is to be delivered through callback PR. + * Return XD. + */ +struct xdstr * +xdinit(struct xdstr *xd, natid cnum, void (*pr)(char *fmt, ...)) +{ + xd->cnum = cnum; + xd->divine = getnatp(cnum)->nat_stat == STAT_GOD; + xd->pr = pr; + return xd; +} + +/* + * Evaluate a attribute of an object into VAL, return VAL. + * XD is the xdump descriptor. + * TYPE is the attribute's type. + * PTR points to the context object. + * The attribute is stored there at offset OFF + IDX * S, where S is + * its size. + * LEN is the #array elements if it is an array, else zero. + */ +struct valstr * +xdeval(struct valstr *val, struct xdstr *xd, + nsc_type type, void *ptr, ptrdiff_t off, int idx, int len) +{ + val->val_type = type; + val->val_cat = NSC_OFF; + val->val_as.sym.off = off; + val->val_as.sym.len = len; + val->val_as.sym.idx = idx; + nstr_exec_val(val, xd->cnum, ptr, NSC_NOTYPE); + return val; /* FIXME nstr_exec_val() should return VAL */ +} + +/* Dump VAL prefixed with SEP, return " ". */ +char * +xdprval(struct xdstr *xd, struct valstr *val, char *sep) +{ + unsigned char *s, *e, *l; + + switch (val->val_type) { + case NSC_LONG: + xd->pr("%s%ld", sep, val->val_as.lng); + break; + case NSC_DOUBLE: + xd->pr("%s%#g", sep, val->val_as.dbl); + break; + case NSC_STRING: + s = (unsigned char *)val->val_as.str.base; + if (s) { + 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; + } + xd->pr("\""); + } else + xd->pr("%snil", sep); + break; + default: + CANT_REACH(); + xd->pr("0"); + } + return " "; +} + +/* + * Dump field values of a context object to XD. + * CA[] describes fields. + * PTR points to context object. + */ +void +xdflds(struct xdstr *xd, struct castr ca[], void *ptr) +{ + int i, j, n; + struct valstr val; + char *sep = ""; + + 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; + j = 0; + do { + xdeval(&val, xd, + ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len); + sep = xdprval(xd, &val, sep); + } while (++j < n); + } +} + +/* + * Dump header for dump NAME. + * 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)); +} + +/* Dump footer for a dump that dumped N objects. */ +void +xdftr(struct xdstr *xd, int n) +{ + xd->pr("/%d\n", n); +} + +/* + * Dump meta-data for items of type TYPE to XD. + * Return RET_OK. + */ +int +xdmeta(struct xdstr *xd, int type) +{ + struct castr *ca = ef_cadef(type); + int i; + int n = 0; + + if (!ca) + return RET_SYN; + + xdhdr(xd, ef_nameof(type), 1); + + 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; + xdflds(xd, mdchr_ca, &ca[i]); + xd->pr("\n"); + n++; + } + + xdftr(xd, n); + + return RET_OK; +} diff --git a/src/lib/common/xundump.c b/src/lib/common/xundump.c index ed31db4f..27791df6 100644 --- a/src/lib/common/xundump.c +++ b/src/lib/common/xundump.c @@ -56,12 +56,14 @@ #include #include #include +#include +#include #include #include "file.h" #include "match.h" #include "nsc.h" #include "optlist.h" -#include "prototypes.h" +#include "xdump.h" static char *fname; /* Name of file being read */ static int lineno; /* Current line number */ From 4ef35744278db86b89bed0567e2c1c6dd88b0770 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 26 Feb 2008 08:18:41 +0100 Subject: [PATCH 18/20] WIP empdump, can't shrink tables --- Make.mk | 2 +- doc/xdump | 25 ++--- include/xdump.h | 4 +- man/empdump.6 | 57 +++++++++++ src/lib/commands/xdump.c | 2 +- src/lib/common/xdump.c | 167 +++++++++++++++++++++++++++----- src/lib/common/xundump.c | 31 +++--- src/util/empdump.c | 200 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 433 insertions(+), 55 deletions(-) create mode 100644 man/empdump.6 create mode 100644 src/util/empdump.c diff --git a/Make.mk b/Make.mk index 2508e1b7..c16d73b8 100644 --- a/Make.mk +++ b/Make.mk @@ -104,7 +104,7 @@ deps := $(obj:.o=.d) # Library archives: libs := $(addprefix lib/, libcommon.a libas.a libgen.a libglobal.a) # Programs: -util := $(addprefix src/util/, $(addsuffix $(EXEEXT), empsched fairland files pconfig)) +util := $(addprefix src/util/, $(addsuffix $(EXEEXT), empdump empsched fairland files pconfig)) client := src/client/empire$(EXEEXT) server := src/server/emp_server$(EXEEXT) # Info subjects: diff --git a/doc/xdump b/doc/xdump index 673a307e..7e82070f 100644 --- a/doc/xdump +++ b/doc/xdump @@ -174,9 +174,10 @@ precisely specified. We use EBNF (ISO 14977) for syntax, except we use '-' in meta-identifiers and omit the concatenation symbol ','. table = header { record } footer ; - header = "XDUMP" space [ "meta" space ] name space timestamp newline ; - name = name-chr { name-chr } ; - name-chr = ? ASCII characters 33..126 ? ; + header = "XDUMP" space [ "meta" space ] + identifier space timestamp newline ; + identifier = id-chr { id-chr } ; + id-char = ? ASCII characters 33..126 except '"#()<>=' ? ; timestamp = intnum ; footer = "/" number newline ; record = [ fields ] newline ; @@ -211,7 +212,7 @@ Notes: Semantics: -* The table name in the header is one of the names in xdump table. +* The table identifier in the header is one of the names in xdump table. * The timestamp increases monotonically. It has a noticeable granularity: game state may change between an xdump and the next @@ -524,7 +525,7 @@ EBNF changes: * Header and footer: - header = "config" name newline colhdr newline ; + header = "config" identifier newline colhdr newline ; colhdr = { identifier [ "(" ( intnum | identifier ) ")" ] } [ "..." ] ; footer = "/config" newline ; @@ -542,7 +543,7 @@ EBNF changes: update. - The column header is due to the self-containedness requirement. - It contains just the essential bit of meta-data: the column name. + It contains just the essential bit of meta-data: the column names. * Symbolic fields: @@ -662,7 +663,7 @@ and section `Objectives': enemy ships, nations). (4) Bandwidth will be minimized (i.e. the format will be as - concise as possible) while remaining human readable (i.e. no + concise as possible) while remaining human-readable (i.e. no binary messages). [Note that data compression may be added at a later date, but if it is added, it will be added on a separate port to maintain backwards compatability.] @@ -735,14 +736,14 @@ f. The data to sync is not readily available the server. limited view on certain parts of server game state on certain events. - To be complete, a machine readable protocol must disclose as much - information as the human readable output. Tracking server game + To be complete, a machine-readable protocol must disclose as much + information as the human-readable output. Tracking server game state changes cannot do that alone. For instance, lookout tells you ship#, owner and location. That event does not trigger any state change on the server! - To be correct, a machine readable protocol must disclose no more - information than the human readable output. When you observe a + To be correct, a machine-readable protocol must disclose no more + information than the human-readable output. When you observe a server game state change, you can only guess what event triggered it, and what it disclosed to which player. You're stuck with conservative assumptions. That's the death knell for completeness. @@ -761,7 +762,7 @@ a shortcut, and it didn't work. That doesn't mean there's no way at all. I believe the only way to get this done right is by tracking *events*. Whenever something is printed to a player, be it live connection or telegram, we need to transmit precisely the same -information in machine readable form. Much more work. +information in machine-readable form. Much more work. xdump shares valuable ideas with C_SYNC, e.g. using selector meta-data. It is, however, much more modest in scope. We're pretty diff --git a/include/xdump.h b/include/xdump.h index 5e39115b..513a71df 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 *, nsc_type, void *, ptrdiff_t, int, int); extern char *xdprval(struct xdstr *, struct valstr *, char *); diff --git a/man/empdump.6 b/man/empdump.6 new file mode 100644 index 00000000..3aac1b2c --- /dev/null +++ b/man/empdump.6 @@ -0,0 +1,57 @@ +.TH EMPDUMP 6 +.SH NAME +empdump \- Export/import Empire game state +.SH SYNOPSIS +.B empdump +[ +.B \-mtxhv +] +[ +.BI \-e " configfile" +] +[ +.I dump-file +] +.br +.SH DESCRIPTION +.B empdump +imports and exports game state as plain text. +.SH OPTIONS +.TP +.BI \-e " configfile" +Use game configuration in \fIconfigfile\fR. +.TP +.B \-h +Help. Print brief usage information and exit. +.TP +.TP +.B \-m +Use machine-readable format for export. Import always recognizes both +machine-readable and human-readable format. +.TP +.B \-t +Test import, don't update game state. +.TP +.B \-v +Print version information and exit. +.TP +.B \-x +Export game state to standard output. +.SH OPERANDS +.TP +.I dump-file +The file to import. +.SH "LIMITATIONS" +.B empdump +can't export player bmaps, power report, telegrams, announcements, +message of the day, no-login message and log files. Exported +floating-point values may be inexact. Importing an exported game +state may not result in identical data files; besides the loss of +floating-point precision just mentioned, coordinates are normalized, +and characters beyond a string's terminating zero in a character array +may be lost. +FIXME can't change file size, fix or document here +.SH "SEE ALSO" +\fIemp_server\fR(6). +.SH AUTHOR +Markus Armbruster diff --git a/src/lib/commands/xdump.c b/src/lib/commands/xdump.c index 79ae41a2..6dc1fe68 100644 --- a/src/lib/commands/xdump.c +++ b/src/lib/commands/xdump.c @@ -223,7 +223,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 5e7f5a0c..aca6b7cb 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; } @@ -116,37 +119,50 @@ xdeval(struct valstr *val, struct xdstr *xd, return val; /* FIXME nstr_exec_val() should return VAL */ } -/* Dump VAL prefixed with SEP, return " ". */ -char * -xdprval(struct xdstr *xd, struct valstr *val, char *sep) +/* + * Dump string STR to XD with funny characters escaped. + * Dump at most N characters. + */ +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 in machine readable format, prefixed with SEP, return " ". */ +static char * +xdprval_nosym(struct xdstr *xd, struct valstr *val, char *sep) +{ switch (val->val_type) { case NSC_LONG: xd->pr("%s%ld", sep, val->val_as.lng); break; case NSC_DOUBLE: +#if 0 /* TODO have %a */ + xd->pr("%s%a", sep, val->val_as.dbl); +#else xd->pr("%s%#g", sep, val->val_as.dbl); +#endif 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); @@ -158,6 +174,66 @@ 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, return " ". + * 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; + char *sym; + + if (xd->human && val->val_type == NSC_LONG && ca->ca_table != EF_BAD) { + ca_sym = ef_cadef(ca->ca_table); + if (ca_sym != symbol_ca) + ; + else 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 " "; + } else + return xdprsym(xd, val->val_as.lng, ca->ca_table, sep); + } + + return xdprval_nosym(xd, val, sep); +} + +/* + * Dump VAL prefixed with SEP, return " ". + * XD must not be human-readable. + */ +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 +256,66 @@ xdflds(struct xdstr *xd, struct castr ca[], void *ptr) do { xdeval(&val, xd, ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len); - 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 +333,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) diff --git a/src/lib/common/xundump.c b/src/lib/common/xundump.c index 27791df6..f84dcb1a 100644 --- a/src/lib/common/xundump.c +++ b/src/lib/common/xundump.c @@ -132,21 +132,6 @@ skipfs(FILE *fp) return ch; } -/* - * Read an identifier from FP into BUF. - * BUF must have space for 1024 characters. - * Return number of characters read on success, -1 on failure. - */ -static int -getid(FILE *fp, char *buf) -{ - int n; - if (fscanf(fp, "%1023[^#()<>=#\" \t\n]%n", buf, &n) != 1 - || !isalpha(buf[0])) - return -1; - return n; -} - /* * Decode escape sequences in BUF. * Return BUF on success, null pointer on failure. @@ -172,6 +157,22 @@ xuesc(char *buf) return buf; } +/* + * Read an identifier from FP into BUF. + * BUF must have space for 1024 characters. + * Return number of characters read on success, -1 on failure. + */ +static int +getid(FILE *fp, char *buf) +{ + int n; + if (fscanf(fp, "%1023[^\"#()<>= \t\n]%n", buf, &n) != 1 + || !isalpha(buf[0])) + return -1; + xuesc(buf); + return n; +} + /* * Try to read a field name from FP. * I is the field number, counting from zero. diff --git a/src/util/empdump.c b/src/util/empdump.c new file mode 100644 index 00000000..2a6de8a6 --- /dev/null +++ b/src/util/empdump.c @@ -0,0 +1,200 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * empdump.c: Export/import Empire game state + * + * Known contributors to this file: + * Markus Armbruster, 2008 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "file.h" +#include "nat.h" +#include "optlist.h" +#include "prototypes.h" +#include "version.h" +#include "xdump.h" + +static void dump_table(int, int); + +int +main(int argc, char *argv[]) +{ + char *config_file = NULL; + char *import = NULL; + int export = 0; + int private = 0; + int human = 1; + int opt, i, lineno, type; + FILE *impf; + int dirty[EF_MAX]; + + while ((opt = getopt(argc, argv, "e:mtxhv")) != EOF) { + switch (opt) { + case 'e': + config_file = optarg; + break; + case 'h': + printf("Usage: %s [OPTION]... [DUMP-FILE]\n" + " -e CONFIG-FILE configuration file\n" + " (default %s)\n" + " -m use machine-readable format\n" + " -t test import, don't update game state\n" + " -x export to standard output\n" + " -h display this help and exit\n" + " -v display version information and exit\n", + argv[0], dflt_econfig); + exit(0); + case 'm': + human = 0; + break; + case 't': + private = EFF_PRIVATE; + break; + case 'x': + export = 1; + break; + case 'v': + printf("%s\n\n%s", version, legal); + exit(0); + default: + fprintf(stderr, "Try -h for help.\n"); + exit(1); + } + } + + if (argv[optind]) + import = argv[optind++]; + + if (import) { + impf = fopen(import, "r"); + if (!impf) { + fprintf(stderr, "Cant open %s for reading (%s)\n", + import, strerror(errno)); + exit(1); + } + } else + private = EFF_PRIVATE; + + /* read configuration & initialize */ + empfile_init(); + if (emp_config(config_file) < 0) + exit(1); + empfile_fixup(); + nsc_init(); + if (read_builtin_tables() < 0) + exit(1); + if (read_custom_tables() < 0) + exit(1); + if (chdir(gamedir)) { + fprintf(stderr, "Can't chdir to %s (%s)\n", + gamedir, strerror(errno)); + exit(1); + } + global_init(); + + for (i = 0; i < EF_MAX; i++) { + if (!EF_IS_GAME_STATE(i)) + continue; + if (!ef_open(i, EFF_MEM | private)) + exit(1); + } + + /* import from IMPORT */ + memset(dirty, 0, sizeof(dirty)); + if (import) { + lineno = 1; + while ((type = xundump(impf, import, &lineno, EF_BAD)) >= 0) + dirty[type] = 1; + if (type == EF_BAD) + exit(1); + } + + if (ef_verify() < 0) + exit(1); + + /* export to stdout */ + if (export) { + for (i = 0; i < EF_MAX; i++) { + if (!EF_IS_GAME_STATE(i)) + continue; + dump_table(i, human); + } + } + + /* write out imported data */ + for (i = 0; i < EF_MAX; i++) { + if (!EF_IS_GAME_STATE(i)) + continue; + if (!private && dirty[i]) { + if (!ef_close(i)) + exit(1); + } + } + + return 0; +} + +static void +printf_wrapper(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +static void +dump_table(int type, int human) +{ + struct xdstr xd; + struct castr *ca; + int i; + void *p; + + ca = ef_cadef(type); + if (!ca) + return; + + xdinit(&xd, 0, human, printf_wrapper); + xdhdr(&xd, ef_nameof(type), 0); + xdcolhdr(&xd, ca); + for (i = 0; (p = ef_ptr(type, i)); i++) { + xdflds(&xd, ca, p); + printf("\n"); + } + xdftr(&xd, i); +} From add345f77fd88d24b63a375e4a3226c1bcf1b81b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 18 Feb 2008 20:57:48 +0100 Subject: [PATCH 19/20] WIP empdump, fill in stuff not in xdump --- src/lib/common/xundump.c | 4 + src/util/empdump.c | 252 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 252 insertions(+), 4 deletions(-) diff --git a/src/lib/common/xundump.c b/src/lib/common/xundump.c index f84dcb1a..76de98ca 100644 --- a/src/lib/common/xundump.c +++ b/src/lib/common/xundump.c @@ -492,6 +492,10 @@ getobj(void) cur_id, ep->name, ep->fids - !!need_sentinel - 1); } else cur_obj = ef_ptr(cur_type, cur_id); + /* TODO find a less ugly solution */ + if (EF_IS_GAME_STATE(cur_type) && cur_type != EF_POWER && + cur_type != EF_MAP && cur_type != EF_BMAP) + *((short *)cur_obj) = cur_type; } return cur_obj; diff --git a/src/util/empdump.c b/src/util/empdump.c index 2a6de8a6..5f7e2441 100644 --- a/src/util/empdump.c +++ b/src/util/empdump.c @@ -33,21 +33,22 @@ #include -#include #include -#include -#include #include #include +#include #include #include "file.h" -#include "nat.h" #include "optlist.h" #include "prototypes.h" #include "version.h" #include "xdump.h" static void dump_table(int, int); +static void shp_fixup(void); +static void pln_fixup(void); +static void lnd_fixup(void); +static void nuk_fixup(void); int main(int argc, char *argv[]) @@ -140,6 +141,10 @@ main(int argc, char *argv[]) dirty[type] = 1; if (type == EF_BAD) exit(1); + shp_fixup(); + pln_fixup(); + lnd_fixup(); + nuk_fixup(); } if (ef_verify() < 0) @@ -198,3 +203,242 @@ dump_table(int type, int human) } xdftr(&xd, i); } + + +/* FIXME remove need for this */ + +#include +#include "ship.h" +#include "plane.h" +#include "land.h" +#include "nuke.h" + +static int fit_plane_on_ship(struct plnstr *, struct shpstr *); +static int fit_plane_on_land(struct plnstr *, struct lndstr *); + +static void +shp_fixup(void) +{ + int i; + struct shpstr *sp; + + for (i = 0; (sp = ef_ptr(EF_SHIP, i)); i++) { + if (!sp->shp_own) + continue; + shp_set_tech(sp, sp->shp_tech); + } +} + +static void +pln_fixup(void) +{ + int i; + struct plnstr *pp; + struct shpstr *csp; + struct lndstr *clp; + + for (i = 0; (pp = ef_ptr(EF_PLANE, i)); i++) { + if (!pp->pln_own) + continue; + pln_set_tech(pp, pp->pln_tech); + csp = ef_ptr(EF_SHIP, pp->pln_ship); + clp = ef_ptr(EF_LAND, pp->pln_land); + if (csp) + fit_plane_on_ship(pp, csp); + else if (clp) + fit_plane_on_land(pp, clp); + } +} + +static void +lnd_fixup(void) +{ + int i; + struct lndstr *lp; + struct shpstr *csp; + struct lndstr *clp; + + for (i = 0; (lp = ef_ptr(EF_LAND, i)); i++) { + if (!lp->lnd_own) + continue; + lnd_set_tech(lp, lp->lnd_tech); + csp = ef_ptr(EF_SHIP, lp->lnd_ship); + clp = ef_ptr(EF_LAND, lp->lnd_land); + if (csp) + csp->shp_nland++; + else if (clp) + clp->lnd_nland++; + } +} + +static void +nuk_fixup(void) +{ + int i; + struct nukstr *np; + struct plnstr *cpp; + + for (i = 0; (np = ef_ptr(EF_NUKE, i)); i++) { + if (!np->nuk_own) + continue; + cpp = ef_ptr(EF_PLANE, np->nuk_plane); + if (cpp) + cpp->pln_nuketype = np->nuk_type; + } +} + +/* FIXME don't copy this from src/lib/subs/???sub.c */ + +/* + * Set SP's tech to TLEV along with everything else that depends on it. + */ +void +shp_set_tech(struct shpstr *sp, int tlev) +{ + struct mchrstr *mcp = mchr + sp->shp_type; + int tech_diff = tlev - mcp->m_tech; + + if (CANT_HAPPEN(tech_diff < 0)) { + tlev -= tech_diff; + tech_diff = 0; + } + + sp->shp_tech = tlev; + sp->shp_armor = (short)SHP_DEF(mcp->m_armor, tech_diff); + sp->shp_speed = (short)SHP_SPD(mcp->m_speed, tech_diff); + sp->shp_visib = (short)SHP_VIS(mcp->m_visib, tech_diff); + sp->shp_frnge = (short)SHP_RNG(mcp->m_frnge, tech_diff); + sp->shp_glim = (short)SHP_FIR(mcp->m_glim, tech_diff); +} + +/* + * Fit a plane of PP's type on ship SP. + * Adjust SP's plane counters. + * Updating the plane accordingly is the caller's job. + * Return whether it fits. + */ +static int +fit_plane_on_ship(struct plnstr *pp, struct shpstr *sp) +{ + struct plchrstr *pcp = plchr + pp->pln_type; + struct mchrstr *mcp = mchr + sp->shp_type; + int wanted; + + if (pcp->pl_flags & P_K) { + /* chopper, try chopper slot first */ + if (sp->shp_nchoppers < mcp->m_nchoppers) + return ++sp->shp_nchoppers; + /* else try plane slot */ + wanted = M_FLY; + } else if (pcp->pl_flags & P_E) { + /* x-light, try x-light slot first */ + if (sp->shp_nxlight < mcp->m_nxlight) + return ++sp->shp_nxlight; + /* else try plane slot */ + wanted = M_MSL | M_FLY; + } else if (!(pcp->pl_flags & P_L)) { + /* not light, no go */ + wanted = 0; + } else if (pcp->pl_flags & P_M) { + /* missile, use plane slot */ + wanted = M_MSL | M_FLY; + } else { + /* fixed-wing plane, use plane slot */ + wanted = M_FLY; + } + + if ((mcp->m_flags & wanted) == 0) + return 0; /* ship not capable */ + + if (sp->shp_nplane < mcp->m_nplanes) + return ++sp->shp_nplane; + + return 0; +} + +/* + * Fit a plane of PP's type on land unit LP. + * Adjust LP's plane counters. + * Updating the plane accordingly is the caller's job. + * Return whether it fits. + */ +static int +fit_plane_on_land(struct plnstr *pp, struct lndstr *lp) +{ + struct plchrstr *pcp = plchr + pp->pln_type; + struct lchrstr *lcp = lchr + lp->lnd_type; + + if ((pcp->pl_flags & P_E) && lp->lnd_nxlight < lcp->l_nxlight) + return ++lp->lnd_nxlight; + + return 0; +} + +/* + * Set PP's tech to TLEV along with everything else that depends on it. + */ +void +pln_set_tech(struct plnstr *pp, int tlev) +{ + struct plchrstr *pcp = plchr + pp->pln_type; + int tech_diff = tlev - pcp->pl_tech; + int limited_range = pp->pln_range < pp->pln_range_max; + + if (CANT_HAPPEN(tech_diff < 0)) { + tlev -= tech_diff; + tech_diff = 0; + } + + pp->pln_tech = tlev; + pp->pln_att = PLN_ATTDEF(pcp->pl_att, tech_diff); + pp->pln_def = PLN_ATTDEF(pcp->pl_def, tech_diff); + pp->pln_acc = PLN_ACC(pcp->pl_acc, tech_diff); + pp->pln_range_max = PLN_RAN(pcp->pl_range, tech_diff); + pp->pln_load = PLN_LOAD(pcp->pl_load, tech_diff); + + if (!limited_range || pp->pln_range > pp->pln_range_max) + pp->pln_range = pp->pln_range_max; +} + +/* + * Set LP's tech to TLEV along with everything else that depends on it. + */ +void +lnd_set_tech(struct lndstr *lp, int tlev) +{ + struct lchrstr *lcp = lchr + lp->lnd_type; + int tech_diff = tlev - lcp->l_tech; + + if (CANT_HAPPEN(tech_diff < 0)) { + tlev -= tech_diff; + tech_diff = 0; + } + + lp->lnd_tech = tlev; + lp->lnd_att = (float)LND_ATTDEF(lcp->l_att, tech_diff); + lp->lnd_def = (float)LND_ATTDEF(lcp->l_def, tech_diff); + lp->lnd_vul = (int)LND_VUL(lcp->l_vul, tech_diff); + lp->lnd_spd = (int)LND_SPD(lcp->l_spd, tech_diff); + lp->lnd_vis = (int)LND_VIS(lcp->l_vis, tech_diff); + lp->lnd_spy = (int)LND_SPY(lcp->l_spy, tech_diff); + lp->lnd_rad = (int)LND_RAD(lcp->l_rad, tech_diff); + lp->lnd_frg = (int)LND_FRG(lcp->l_frg, tech_diff); + lp->lnd_acc = (int)LND_ACC(lcp->l_acc, tech_diff); + lp->lnd_dam = (int)LND_DAM(lcp->l_dam, tech_diff); + lp->lnd_ammo = (int)LND_AMM(lcp->l_ammo, tech_diff); + lp->lnd_aaf = (int)LND_AAF(lcp->l_aaf, tech_diff); + lp->lnd_fuelc = (int)LND_FC(lcp->l_fuelc, tech_diff); + lp->lnd_fuelu = (int)LND_FU(lcp->l_fuelu, tech_diff); + lp->lnd_maxlight = (int)LND_XPL(lcp->l_nxlight, tech_diff); + lp->lnd_maxland = (int)LND_MXL(lcp->l_nland, tech_diff); +} + +/* FIXME don't copy this from src/lib/update/nat.c */ + +double +logx(double d, double base) +{ + if (base == 1.0) + return d; + return log10(d) / log10(base); +} From c313aafd8a8146f22218c96fca2e9e75339e7b84 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 22 Feb 2008 05:57:18 +0100 Subject: [PATCH 20/20] WIP empdump, %a --- src/lib/common/xdump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/common/xdump.c b/src/lib/common/xdump.c index aca6b7cb..6dc0662f 100644 --- a/src/lib/common/xdump.c +++ b/src/lib/common/xdump.c @@ -153,7 +153,7 @@ xdprval_nosym(struct xdstr *xd, struct valstr *val, char *sep) xd->pr("%s%ld", sep, val->val_as.lng); break; case NSC_DOUBLE: -#if 0 /* TODO have %a */ +#if 1 /* TODO have %a */ xd->pr("%s%a", sep, val->val_as.dbl); #else xd->pr("%s%#g", sep, val->val_as.dbl);