Don't misinterpret blank configuration entries as sentinels
authorMarkus Armbruster <armbru@pond.sub.org>
Sat, 21 May 2011 15:49:17 +0000 (17:49 +0200)
committerMarkus Armbruster <armbru@pond.sub.org>
Sat, 25 Jun 2011 14:51:56 +0000 (16:51 +0200)
Configuration table entries not defined by builtin and custom
configuration files remain blank.  They get misinterpreted as sentinel
in tables that use one.  Affected are tables product, ship-chr,
plane-chr, land-chr and nuke-chr.  Tables item, sect-chr and
infrastructure are immune despite using a sentinel, because omitting
entries is not permitted there.

Code relying on the sentinel fails to pick up entries after the first
blank one.  They don't get set up correctly, they're invisible to
build and show, and not recognized as symbolic selector values (the
frg in ship ?type=frg).  xdump is fine, because it doesn't rely on
sentinels.  It dumps blank entries normally.

The bugs don't bite in the stock game, because the builtin
configuration files are all dense.

The sentinels are all null strings.  Set them to "" in the affected
tables' oninit callback.  Fix up code iterating over the tables to
ignore such entries.  This is precisely the code relying on sentinels,
plus xdump's xdvisible().

src/lib/commands/xdump.c
src/lib/common/ef_verify.c
src/lib/common/filetable.c
src/lib/common/stmtch.c
src/lib/common/xundump.c
src/lib/global/init.c
src/lib/subs/show.c

index 8d10669e690898d82f5e49ad3b3099c74d15c913..72d6e713ec58d287fb8ee949653d94cc99889154 100644 (file)
@@ -37,6 +37,7 @@
 #include "empobj.h"
 #include "news.h"
 #include "optlist.h"
+#include "product.h"
 #include "version.h"
 #include "xdump.h"
 
@@ -52,6 +53,7 @@ xdvisible(int type, void *p)
     struct lonstr *lp = p;
     struct natstr *natp;
     int tlev;
+    char *name;
 
     switch (type) {
     case EF_SECTOR:
@@ -84,19 +86,27 @@ xdvisible(int type, void *p)
     case EF_TRADE:
     case EF_COMM:
        return gp->own != 0;
+    case EF_PRODUCT:
+       return ((struct pchrstr *)p)->p_sname[0] != 0;
     case EF_SHIP_CHR:
        tlev = ((struct mchrstr *)p)->m_tech;
+       name = ((struct mchrstr *)p)->m_name;
        goto tech;
     case EF_PLANE_CHR:
        tlev = ((struct plchrstr *)p)->pl_tech;
+       name = ((struct plchrstr *)p)->pl_name;
        goto tech;
     case EF_LAND_CHR:
        tlev = ((struct lchrstr *)p)->l_tech;
+       name = ((struct lchrstr *)p)->l_name;
     tech:
        natp = getnatp(player->cnum);
+       if (!name[0])
+           return 0;
        return player->god || tlev <= (int)(1.25 * natp->nat_level[NAT_TLEV]);
     case EF_NUKE_CHR:
        tlev = ((struct nchrstr *)p)->n_tech;
+       name = ((struct nchrstr *)p)->n_name;
        if (drnuke_const > MIN_DRNUKE_CONST) {
            natp = getnatp(player->cnum);
            if (tlev > (int)((int)(1.25 * natp->nat_level[NAT_RLEV])
index c32b4403ed2bffb1efc7c9a66c92d88d59617e11..34dfacafc2038a58c34ad1a4bc03fb186f79dfaa 100644 (file)
@@ -226,6 +226,8 @@ ef_verify(void)
 
     /* Special checks */
     for (i = 0; pchr[i].p_sname; i++) {
+       if (!pchr[i].p_sname[0])
+           continue;
        if ((pchr[i].p_type >= 0) == (pchr[i].p_level >= 0)) {
            fprintf(stderr,
                "Config %s uid %d field level doesn't match field type\n",
index b67366be82a22a7a222c48d074fa70193fb9fc9a..ba14072cc0051c75407b6727dd0c712c61363442 100644 (file)
@@ -62,6 +62,11 @@ static void nuk_oninit(void *);
 static void nat_oninit(void *);
 static void realm_oninit(void *);
 static void game_oninit(void *);
+static void pchr_oninit(void *);
+static void mchr_oninit(void *);
+static void plchr_oninit(void *);
+static void lchr_oninit(void *);
+static void nchr_oninit(void *);
 
 /* Number of elements in ARRAY.  */
 #define SZ(array) (sizeof(array) / sizeof((array)[0]))
@@ -192,17 +197,17 @@ struct empfile empfile[] = {
     {EF_ITEM, "item", "item.config", ichr_ca, EF_BAD,
      ARRAY_CACHE(ichr, EFF_CFG), NULL, NULL, NULL, NULL},
     {EF_PRODUCT, "product", "product.config", pchr_ca, EF_BAD,
-     ARRAY_CACHE(pchr, EFF_CFG), NULL, NULL, NULL, NULL},
+     ARRAY_CACHE(pchr, EFF_CFG), pchr_oninit, NULL, NULL, NULL},
     {EF_SECTOR_CHR, "sect-chr", "sect.config", dchr_ca, EF_BAD,
      ARRAY_CACHE(dchr, EFF_CFG), NULL, NULL, NULL, NULL},
     {EF_SHIP_CHR, "ship-chr", "ship.config", mchr_ca, EF_BAD,
-     ARRAY_CACHE(mchr, EFF_CFG), NULL, NULL, NULL, NULL},
+     ARRAY_CACHE(mchr, EFF_CFG), mchr_oninit, NULL, NULL, NULL},
     {EF_PLANE_CHR, "plane-chr", "plane.config", plchr_ca, EF_BAD,
-     ARRAY_CACHE(plchr, EFF_CFG), NULL, NULL, NULL, NULL},
+     ARRAY_CACHE(plchr, EFF_CFG), plchr_oninit, NULL, NULL, NULL},
     {EF_LAND_CHR, "land-chr", "land.config", lchr_ca, EF_BAD,
-     ARRAY_CACHE(lchr, EFF_CFG), NULL, NULL, NULL, NULL},
+     ARRAY_CACHE(lchr, EFF_CFG), lchr_oninit, NULL, NULL, NULL},
     {EF_NUKE_CHR, "nuke-chr", "nuke.config", nchr_ca, EF_BAD,
-     ARRAY_CACHE(nchr, EFF_CFG), NULL, NULL, NULL, NULL},
+     ARRAY_CACHE(nchr, EFF_CFG), nchr_oninit, NULL, NULL, NULL},
     {EF_NEWS_CHR, "news-chr", NULL, rpt_ca, EF_BAD,
      ARRAY_TABLE(rpt, N_MAX_VERB + 1, EFF_CFG), NULL, NULL, NULL, NULL},
     {EF_INFRASTRUCTURE, "infrastructure", "infra.config", intrchr_ca, EF_BAD,
@@ -329,6 +334,36 @@ game_oninit(void *ptr)
     ((struct gamestr *)ptr)->game_turn = 1;
 }
 
+static void
+pchr_oninit(void *ptr)
+{
+    ((struct pchrstr *)ptr)->p_sname = "";
+}
+
+static void
+mchr_oninit(void *ptr)
+{
+    ((struct mchrstr *)ptr)->m_name = "";
+}
+
+static void
+plchr_oninit(void *ptr)
+{
+    ((struct plchrstr *)ptr)->pl_name = "";
+}
+
+static void
+lchr_oninit(void *ptr)
+{
+    ((struct lchrstr *)ptr)->l_name = "";
+}
+
+static void
+nchr_oninit(void *ptr)
+{
+    ((struct nchrstr *)ptr)->n_name = "";
+}
+
 static void
 ef_fix_size(struct empfile *ep, int n)
 {
index 9b1074e046a9ec020a523afea6cc41b44bb06df8..10b59a793d8b1eaff46784c93f5ef4dab170da21 100644 (file)
@@ -40,6 +40,7 @@
  * several.
  * Each array element has a pointer to its name stored at offset OFFS.
  * Search stops when this name is a null pointer.
+ * It ignores elements with an empty name.
  * NEEDLE is compared to element names with mineq(NEEDLE, NAME).
  * SIZE gives the size of an array element.
  */
@@ -54,6 +55,8 @@ stmtch(char *needle, void *haystack, ptrdiff_t offs, size_t size)
        name = *(char **)((char *)haystack + i * size + offs);
        if (!name)
            break;
+       if (!*name)
+           continue;
        switch (mineq(needle, name)) {
        case ME_MISMATCH:
            break;
index 387c721c0517598a9f3885d2163769d13abf0be9..d7f7930d072e26b422c5dada651b6053dd3cf3eb 100644 (file)
@@ -43,7 +43,6 @@
  * - Normalize terminology: table/rows/columns or file/records/fields
  * - Loading tables with NSC_STRING elements more than once leaks memory
  * TODO:
- * - Check EFF_CFG tables are dense
  * - Symbolic array indexes
  * - Option to treat missing and unknown fields as warning, not error
  * TODO, but hardly worth the effort:
index e052960771c6a1b74b1bf7a6b499395d313e322e..b1c7e2e363e97e94fdd66c744b1045fa8dc65a4d 100644 (file)
@@ -60,6 +60,8 @@ init_mchr(void)
     struct mchrstr *mp;
 
     for (mp = mchr; mp->m_name; mp++) {
+       if (!mp->m_name[0])
+           continue;
        if (mp->m_nplanes && !(mp->m_flags & M_MSL || mp->m_flags & M_FLY))
            mp->m_flags |= M_MSL;
     }
@@ -71,6 +73,8 @@ init_plchr(void)
     struct plchrstr *pp;
 
     for (pp = plchr; pp->pl_name; pp++) {
+       if (!pp->pl_name[0])
+           continue;
        if (pp->pl_flags & P_M)
            pp->pl_flags |= P_V;
     }
@@ -81,7 +85,9 @@ init_pchr(void)
 {
     struct pchrstr *p;
 
-    for (p = pchr; p->p_name; p++) {
+    for (p = pchr; p->p_sname; p++) {
+       if (!p->p_sname[0])
+           continue;
        if (opt_GO_RENEW) {
            if (p->p_type == I_DUST || p->p_type == I_OIL)
                p->p_nrdep = 0;
index 9225e6747c9c0e984d8684d829ddb8072bda9d54..0362f6d1667cd8147faef01ed198e275525fd24e 100644 (file)
@@ -79,6 +79,8 @@ make_mchr_index(struct chr_index chridx[], int tlev)
 
     n = 0;
     for (i = 0; mchr[i].m_name; i++) {
+       if (!mchr[i].m_name[0])
+           continue;
        if (mchr[i].m_tech > tlev)
            continue;
        if ((mchr[i].m_flags & M_TRADE) && !opt_TRADESHIPS)
@@ -100,6 +102,8 @@ make_plchr_index(struct chr_index chridx[], int tlev)
 
     n = 0;
     for (i = 0; plchr[i].pl_name; i++) {
+       if (!plchr[i].pl_name[0])
+           continue;
        if (plchr[i].pl_tech > tlev)
            continue;
        chridx[n].type = i;
@@ -119,6 +123,8 @@ make_lchr_index(struct chr_index chridx[], int tlev)
 
     n = 0;
     for (i = 0; lchr[i].l_name; i++) {
+       if (!lchr[i].l_name[0])
+           continue;
        if (lchr[i].l_tech > tlev)
            continue;
        if ((lchr[i].l_flags & L_SPY) && !opt_LANDSPIES)
@@ -140,6 +146,8 @@ make_nchr_index(struct chr_index chridx[], int tlev)
 
     n = 0;
     for (i = 0; nchr[i].n_name; i++) {
+       if (!nchr[i].n_name[0])
+           continue;
        if (nchr[i].n_tech > tlev)
            continue;
        chridx[n].type = i;
@@ -506,6 +514,8 @@ show_product(int tlev)
     pr("product    cost  raw materials  reso dep  level p.e.\n");
 
     for (pp = pchr; pp->p_sname; pp++) {
+       if (!pp->p_sname[0])
+           continue;
        pr("%7.7s %c  $%-3d ",
           pp->p_sname,
           pp->p_type < 0 ? ' ' : ichr[pp->p_type].i_mnem,