/* * Dump items of type @type selected by @arg to @xd. * Return RET_OK on success, RET_SYN on error. */ static int xditem(struct xdstr *xd, int type, char *arg) { struct castr *ca; struct nstr_item ni; int n; unsigned char buf[EF_WITH_CADEF_MAX_ENTRY_SIZE]; ca = ef_cadef(type); if (!ca) return RET_SYN; if (!snxtitem(&ni, type, arg, NULL)) return RET_SYN; xdhdr(xd, ef_nameof(type), 0); n = 0; while (nxtitem(&ni, buf)) { if (!xdvisible(type, buf)) continue; ++n; xdflds(xd, ca, buf); xd->pr("\n"); } xdftr(xd, n); return RET_OK; }
/* * Dump @val prefixed with @sep to @xd, return " ". * @val must be evaluated. * @ca describes the field from which the value was fetched. */ static char * xdprval_sym(struct xdstr *xd, struct valstr *val, struct castr *ca, char *sep) { unsigned long bit; if (CANT_HAPPEN(val->val_cat != NSC_VAL)) { xd->pr("%snil", sep); return " "; } if (!xd->human || val->val_type != NSC_LONG || ca->ca_table == EF_BAD || ef_cadef(ca->ca_table) != symbol_ca) return xdprval_nosym(xd, val, sep); if (ca->ca_flags & NSC_BITS) { xd->pr("%s(", sep); sep = ""; for (bit = 1; bit; bit <<= 1) { if (bit & val->val_as.lng) sep = xdprsym(xd, bit, ca->ca_table, sep); } xd->pr(")"); return " "; } return xdprsym(xd, val->val_as.lng, ca->ca_table, sep); }
/* * Dump meta-data for items of type @type to @xd. * Return RET_SYN when @type doesn't have meta-data, else 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); xdcolhdr(xd, ca); for (i = 0; ca[i].ca_name; i++) { if (ca[i].ca_flags & NSC_DEITY && !xd->divine) continue; if (ca[i].ca_dump == CA_DUMP_NONE) continue; xdflds(xd, mdchr_ca, &ca[i]); xd->pr("\n"); n++; } xdftr(xd, n); return RET_OK; }
/* * Compile a value in @str into @val. * Return a pointer to the first character after the value on success, * NULL on error. * @type is the context type, a file type. */ char * nstr_comp_val(char *str, struct valstr *val, int type) { struct castr *ca = ef_cadef(type); char *tail = nstr_parse_val(str, val); if (!nstr_resolve_id(val, ca, nstr_match_ca(val, ca))) return NULL; return tail; }
static int verify_table(int type) { int retval = 0; int i; if (!ef_cadef(type)) return 0; verify_ca(type); for (i = 0; i < ef_nelem(type); i++) retval |= verify_row(type, i); return retval; }
static int verify_tabref(int type, int row, struct castr *ca, int idx, long val) { int tabno = ca->ca_table; struct castr *ca_sym = ef_cadef(tabno); int i; if (ca->ca_flags & NSC_BITS) { /* symbol set */ if (CANT_HAPPEN(ca_sym != symbol_ca)) return -1; for (i = 0; i < (int)sizeof(long) * 8; i++) { if (val & (1UL << i)) { if (!symbol_by_value(1L << i, ef_ptr(tabno, 0))) { verify_fail(type, row, ca, idx, "bit %d is not in symbol table %s", i, ef_nameof(tabno)); return -1; } } } } else if (ca_sym == symbol_ca) { /* symbol */ if (!symbol_by_value(val, ef_ptr(tabno, 0))) { verify_fail(type, row, ca, idx, "value %ld is not in symbol table %s", val, ef_nameof(tabno)); return -1; } } else { /* table index */ if (val >= ef_nelem(tabno) || val < -1) { verify_fail(type, row, ca, idx, "value %ld indexes table %s out of bounds 0..%d", val, ef_nameof(tabno), ef_nelem(tabno)); return -1; } /* laziness: assumes TABNO is EFF_MEM */ if (val >= 0 && !empobj_in_use(tabno, ef_ptr(tabno, val))) { verify_fail(type, row, ca, idx, "value %ld refers to missing element of table %s", val, ef_nameof(tabno)); return -1; } } return 0; }
/* * Search table @type for an element matching @name, return its index. * Accepts EF_BAD, but of course never finds anything then. * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are * several. */ int ef_elt_byname(int type, char *name) { switch (type) { case EF_BAD: return M_NOTFOUND; case EF_NATION: return cnumb(name); case EF_SECTOR_CHR: return sct_typematch(name); case EF_SHIP_CHR: return stmtch(name, mchr, offsetof(struct mchrstr, m_name), sizeof(mchr[0])); case EF_LAND_CHR: return stmtch(name, lchr, offsetof(struct lchrstr, l_name), sizeof(lchr[0])); case EF_PLANE_CHR: return stmtch(name, plchr, offsetof(struct plchrstr, pl_name), sizeof(plchr[0])); case EF_NUKE_CHR: return stmtch(name, nchr, offsetof(struct nchrstr, n_name), sizeof(nchr[0])); case EF_ITEM: return stmtch(name, ichr, offsetof(struct ichrstr, i_name), sizeof(ichr[0])); case EF_PRODUCT: return stmtch(name, pchr, offsetof(struct pchrstr, p_sname), sizeof(pchr[0])); case EF_TABLE: return stmtch(name, empfile, offsetof(struct empfile, name), sizeof(empfile[0])); default: if (ef_cadef(type) == symbol_ca) return stmtch(name, ef_ptr(type, 0), offsetof(struct symbol, name), sizeof(struct symbol)); } return M_NOTFOUND; }
static int verify_ca(int type) { struct castr *ca = ef_cadef(type); int i; for (i = 0; ca[i].ca_name; i++) { /* * Virtual selectors must be NSC_EXTRA, because xundump can't * cope with them without setter methods. Exception: if * EFF_MEM is not set, xundump doesn't touch the table. */ if (CANT_HAPPEN((ef_flags(type) & EFF_MEM) && ca[i].ca_get && !(ca[i].ca_flags & NSC_EXTRA))) ca[i].ca_flags |= NSC_EXTRA; } return 0; }
static int verify_row(int type, int row) { struct castr *ca = ef_cadef(type); struct empobj *row_ref; int i, j, n; struct valstr val; int ret_val = 0; int flags = ef_flags(type); if (flags & EFF_MEM) row_ref = ef_ptr(type, row); else { row_ref = malloc(empfile[type].size); ef_read(type, row, row_ref); } if ((flags & EFF_TYPED) && !EF_IS_VIEW(type)) { if (row_ref->ef_type != type || row_ref->uid != row) { verify_fail(type, row, NULL, 0, "header corrupt"); ret_val = -1; } } if (!empobj_in_use(type, row_ref)) goto out; for (i = 0; ca[i].ca_name; ++i) { if (ca[i].ca_get) continue; /* virtual */ n = CA_ARRAY_LEN(&ca[i]); j = 0; do { if (ca[i].ca_table == EF_BAD) continue; nstr_mksymval(&val, &ca[i], j); nstr_eval(&val, 0, row_ref, NSC_NOTYPE); if (CANT_HAPPEN(val.val_type != NSC_LONG)) { ret_val = -1; continue; } if (ca[i].ca_table == type && i == 0) { /* uid */ if (val.val_as.lng != row) { verify_fail(type, row, &ca[i], j, "value is %ld instead of %d", val.val_as.lng, row); ret_val = -1; } } else { if (verify_tabref(type, row, &ca[i], j, val.val_as.lng) < 0) ret_val = -1; } } while (++j < n); } out: if (!(flags & EFF_MEM)) free(row_ref); return ret_val; }
/* * Compile conditions into array @np[@len]. * Return number of conditions, or -1 on error. * It is an error if there are more than @len conditions. * @type is the context type, a file type. * @str is the condition string, in Empire syntax, without the leading * '?'. */ int nstr_comp(struct nscstr *np, int len, int type, char *str) { struct castr *ca = ef_cadef(type); char *cond; char *tail; int i; struct nscstr dummy; int lft_caidx, rgt_caidx; int lft_val, rgt_val; int two_sels; cond = str; for (i = 0; ; ++i, ++np) { if (i >= len) np = &dummy; /* left operand */ if (!*cond) { pr("%s -- %scondition expected\n", str, i ? "another " : ""); return -1; } tail = nstr_parse_val(cond, &np->lft); lft_caidx = nstr_match_ca(&np->lft, ca); /* operator */ if (*tail != '<' && *tail != '=' && *tail != '>' && *tail != '#') { if (*tail) pr("%s -- expected condition operator\n", cond); else pr("%s -- missing condition operator\n", cond); return -1; } np->operator = *tail; ++tail; /* right operand */ if (!*tail) { pr("%s -- operand expected\n", cond); return -1; } tail = nstr_parse_val(tail, &np->rgt); rgt_caidx = nstr_match_ca(&np->rgt, ca); /* * Resolve identifiers * * If just one operand is an identifier, it names a selector. * If both operands are identifiers, things get complicated: * either can then name a selector or a symbolic value for the * selector named by the other operand. */ if (np->lft.val_cat == NSC_ID && np->rgt.val_cat == NSC_ID) { lft_val = nstr_match_val(&np->lft, ca, rgt_caidx); rgt_val = nstr_match_val(&np->rgt, ca, lft_caidx); two_sels = nstr_ca_comparable(ca, lft_caidx, rgt_caidx); /* * If lft_val >= 0 interpreting rgt as a selector and lft * as one of its values works. Likewise for rgt_val >= 0. * If two_sels, interpreting both lft and rgt as selector * works. */ switch ((lft_val >= 0) + (rgt_val >= 0) + !!two_sels) { case 0: /* no interpretation */ if (lft_caidx >= 0 && rgt_caidx >= 0) { /* * Both identifiers name selectors. Since * !two_sels, they can't be comparable. * Example: type=civil. */ pr("%.*s -- not comparable\n", (int)(tail-cond), cond); return -1; } /* * At least one identifier doesn't name a selector, * and nstr_resolve_id() will fail for it below */ break; case 1: /* one unambigous interpretation */ break; default: /* multiple interpretations */ /* * Last-resort disambiguation: if the identifier is * the unabbreviated name of a selector, discard * value, else discard selector interpretation. * Example: resolve wing=g to wing='g', not wing=group * or 'wing'=group. */ if (nstr_is_name_of_ca(&np->lft, ca, lft_caidx)) lft_val = -1; else two_sels = 0; if (nstr_is_name_of_ca(&np->rgt, ca, rgt_caidx)) rgt_val = -1; else two_sels = 0; if ((lft_val >= 0) + (rgt_val >= 0) + !!two_sels == 1) break; /* last-resort disambiguation worked */ /* * Example: n<n for sectors could mean newdes<n or * n<newdes. */ pr("%.*s -- condition ambiguous\n", (int)(tail-cond), cond); return -1; } /* resolve identifiers naming values */ if (lft_val >= 0) nstr_resolve_val(&np->lft, lft_val, &ca[rgt_caidx]); if (rgt_val >= 0) nstr_resolve_val(&np->rgt, rgt_val, &ca[lft_caidx]); } /* remaining identifiers name selectors */ if (!nstr_resolve_id(&np->lft, ca, lft_caidx)) return -1; if (!nstr_resolve_id(&np->rgt, ca, rgt_caidx)) return -1; /* find operator type */ np->optype = nstr_optype(np->lft.val_type, np->rgt.val_type); if (np->optype == NSC_NOTYPE) { pr("%.*s -- not comparable\n", (int)(tail-cond), cond); return -1; } /* another condition? */ if (*tail == 0) break; if (*tail != '&') { pr("%s -- expected `&'\n", cond); return -1; } cond = tail + 1; } if (i >= len) { /* could just return I and let caller gripe or enlarge buffer */ pr("%s -- too many conditions\n", str); return -1; } return i + 1; }