aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/tads/tads2/vocabulary.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2019-05-17 11:02:01 -1000
committerPaul Gilbert2019-05-24 18:21:06 -0700
commite83972f502142b4e6191b962a7be89829bd8d708 (patch)
treeebb795b1f58f7f8c2acc8cf7e19c3ea7fb2e0077 /engines/glk/tads/tads2/vocabulary.cpp
parent0279143a62c2cdab635894103da03b43eba7cf9c (diff)
downloadscummvm-rg350-e83972f502142b4e6191b962a7be89829bd8d708.tar.gz
scummvm-rg350-e83972f502142b4e6191b962a7be89829bd8d708.tar.bz2
scummvm-rg350-e83972f502142b4e6191b962a7be89829bd8d708.zip
GLK: TADS2: Added code for tokenizer & vocabulary
Diffstat (limited to 'engines/glk/tads/tads2/vocabulary.cpp')
-rw-r--r--engines/glk/tads/tads2/vocabulary.cpp902
1 files changed, 901 insertions, 1 deletions
diff --git a/engines/glk/tads/tads2/vocabulary.cpp b/engines/glk/tads/tads2/vocabulary.cpp
index e20bbe3de8..516e9df3f9 100644
--- a/engines/glk/tads/tads2/vocabulary.cpp
+++ b/engines/glk/tads/tads2/vocabulary.cpp
@@ -22,12 +22,912 @@
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/vocabulary.h"
+#include "glk/tads/tads2/error.h"
+#include "glk/tads/tads2/memory_cache_heap.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
-// TODO: Rest of vocabulary stuff
+
+/*
+ * Main vocabulary context. This can be saved globally if desired, so
+ * that routines that don't have any other access to it (such as
+ * Unix-style signal handlers) can reach it.
+ */
+voccxdef *main_voc_ctx = 0;
+
+#ifdef VOCW_IN_CACHE
+vocwdef *vocwget(voccxdef *ctx, uint idx)
+{
+ uint pg;
+
+ if (idx == VOCCXW_NONE)
+ return 0;
+
+ /* get the page we need */
+ pg = idx/VOCWPGSIZ;
+
+ /* if it's not locked, lock it */
+ if (pg != ctx->voccxwplck)
+ {
+ /* unlock the old page */
+ if (ctx->voccxwplck != MCMONINV)
+ mcmunlck(ctx->voccxmem, ctx->voccxwp[ctx->voccxwplck]);
+
+ /* lock the new page */
+ ctx->voccxwpgptr = (vocwdef *)mcmlck(ctx->voccxmem,
+ ctx->voccxwp[pg]);
+ ctx->voccxwplck = pg;
+ }
+
+ /* return the entry on that page */
+ return ctx->voccxwpgptr + (idx % VOCWPGSIZ);
+}
+#endif /*VOCW_IN_CACHE */
+
+/* hash value is based on first 6 characters only to allow match-in-6 */
+uint vochsh(uchar *t, int len)
+{
+ uint ret = 0;
+
+ if (len > 6) len = 6;
+ for ( ; len ; --len, ++t)
+ ret = (ret + (uint)(vocisupper(*t) ? tolower(*t) : *t))
+ & (VOCHASHSIZ - 1);
+ return(ret);
+}
+
+/* copy vocabulary word, and convert to lower case */
+static void voccpy(uchar *dst, uchar *src, int len)
+{
+ for ( ; len ; --len, ++dst, ++src)
+ *dst = vocisupper(*src) ? tolower(*src) : *src;
+}
+
+/* allocate and set up a new vocwdef record, linking into a vocdef's list */
+static void vocwset(voccxdef *ctx, vocdef *v, prpnum p, objnum objn,
+ int classflg)
+{
+ vocwdef *vw;
+ uint inx;
+ vocwdef *vw2;
+
+ /*
+ * look through the vocdef list to see if there's an existing entry
+ * with the DELETED marker -- if so, simply undelete it
+ */
+ for (inx = v->vocwlst, vw = vocwget(ctx, inx) ; vw ;
+ inx = vw->vocwnxt, vw = vocwget(ctx, inx))
+ {
+ /* if this entry was deleted, and otherwise matches, undelete it */
+ if ((vw->vocwflg & VOCFDEL)
+ && vw->vocwobj == objn && vw->vocwtyp == p)
+ {
+ /*
+ * Remove the deleted flag. We will otherwise leave the
+ * flags unchanged, since the VOCFDEL flag applies only to
+ * statically allocated objects, and hence the original
+ * flags should take precedence over any run-time flags.
+ */
+ vw->vocwflg &= ~VOCFDEL;
+
+ /* we're done */
+ return;
+ }
+ }
+
+ /* make sure the word+object+type record isn't already defined */
+ for (inx = v->vocwlst, vw = vocwget(ctx, inx) ; vw ;
+ inx = vw->vocwnxt, vw = vocwget(ctx, inx))
+ {
+ if (vw->vocwobj == objn && vw->vocwtyp == p
+ && (vw->vocwflg & VOCFCLASS) == (classflg & VOCFCLASS))
+ {
+ /* it matches - don't add a redundant record */
+ return;
+ }
+ }
+
+ /* look in the free list for an available vocwdef */
+ if (ctx->voccxwfre != VOCCXW_NONE)
+ {
+ inx = ctx->voccxwfre;
+ vw = vocwget(ctx, inx); /* get the free vocwdef */
+ ctx->voccxwfre = vw->vocwnxt; /* unlink from free list */
+ }
+ else
+ {
+ /* allocate another page of vocwdef's if necssary */
+ if ((ctx->voccxwalocnt % VOCWPGSIZ) == 0)
+ {
+ int pg = ctx->voccxwalocnt / VOCWPGSIZ;
+
+ /* make sure we haven't exceeded the available page count */
+ if (pg >= VOCWPGMAX) errsig(ctx->voccxerr, ERR_VOCMNPG);
+
+ /* allocate on the new page */
+#ifdef VOCW_IN_CACHE
+ mcmalo(ctx->voccxmem, (ushort)(VOCWPGSIZ * sizeof(vocwdef)),
+ &ctx->voccxwp[pg]);
+ mcmunlck(ctx->voccxmem, ctx->voccxwp[pg]);
+#else
+ ctx->voccxwp[pg] =
+ (vocwdef *)mchalo(ctx->voccxerr,
+ (VOCWPGSIZ * sizeof(vocwdef)),
+ "vocwset");
+#endif
+ }
+
+ /* get the next entry, and increment count of used entries */
+ inx = ctx->voccxwalocnt++;
+ vw = vocwget(ctx, inx);
+ }
+
+ /* link the new vocwdef into the vocdef's relation list */
+ vw->vocwnxt = v->vocwlst;
+ v->vocwlst = inx;
+
+ /* set up the new vocwdef */
+ vw->vocwtyp = (uchar)p;
+ vw->vocwobj = objn;
+ vw->vocwflg = classflg;
+
+ /*
+ * Scan the list and make sure we're not adding a redundant verb.
+ * Don't bother with the warning if this is a class.
+ */
+ if (p == PRP_VERB && (ctx->voccxflg & VOCCXFVWARN)
+ && (vw->vocwflg & VOCFCLASS) == 0)
+ {
+ for (vw2 = vocwget(ctx, v->vocwlst) ; vw2 ;
+ vw2 = vocwget(ctx, vw2->vocwnxt))
+ {
+ /*
+ * if this is a different object, and it's not a class, and
+ * it's defined as a verb, warn about it
+ */
+ if (vw2 != vw
+ && (vw2->vocwflg & VOCFCLASS) == 0
+ && vw2->vocwtyp == PRP_VERB)
+ {
+ if (v->vocln2 != 0)
+ errlog2(ctx->voccxerr, ERR_VOCREVB,
+ ERRTSTR,
+ errstr(ctx->voccxerr,
+ (char *)v->voctxt, v->voclen),
+ ERRTSTR,
+ errstr(ctx->voccxerr,
+ (char *)v->voctxt + v->voclen, v->vocln2));
+ else
+ errlog1(ctx->voccxerr, ERR_VOCREVB,
+ ERRTSTR,
+ errstr(ctx->voccxerr,
+ (char *)v->voctxt, v->voclen));
+ break;
+ }
+ }
+ }
+}
+
+/* set up a vocdef record, and link into hash table */
+static void vocset(voccxdef *ctx, vocdef *v, prpnum p, objnum objn,
+ int classflg, uchar *wrdtxt, int len,
+ uchar *wrd2, int len2)
+{
+ uint hshval = vochsh(wrdtxt, len);
+
+ v->vocnxt = ctx->voccxhsh[hshval];
+ ctx->voccxhsh[hshval] = v;
+
+ v->voclen = len;
+ v->vocln2 = len2;
+ voccpy(v->voctxt, wrdtxt, len);
+ if (wrd2) voccpy(v->voctxt + len, wrd2, len2);
+
+ /* allocate and initialize a vocwdef for the object */
+ vocwset(ctx, v, p, objn, classflg);
+}
+
+/* internal addword - already parsed into two words and have lengths */
+void vocadd2(voccxdef *ctx, prpnum p, objnum objn, int classflg,
+ uchar *wrdtxt, int len, uchar *wrd2, int len2)
+{
+ vocdef *v;
+ vocdef *prv;
+ uint need;
+ uint hshval;
+
+ /* if the word is null, ignore it entirely */
+ if (len == 0 && len2 == 0)
+ return;
+
+ /* look for a vocdef entry with the same word text */
+ hshval = vochsh(wrdtxt, len);
+ for (v = ctx->voccxhsh[hshval] ; v ; v = v->vocnxt)
+ {
+ /* if it matches on both words, use this entry */
+ if (v->voclen == len && !memcmp(wrdtxt, v->voctxt, (size_t)len)
+ && ((!wrd2 && v->vocln2 == 0)
+ || (v->vocln2 == len2 &&
+ !memcmp(wrd2, v->voctxt + len, (size_t)len2))))
+ {
+ vocwset(ctx, v, p, objn, classflg);
+ return;
+ }
+ }
+
+ /* look for a free vocdef entry of the same size */
+ for (prv = (vocdef *)0, v = ctx->voccxfre ; v ; prv = v, v = v->vocnxt)
+ if (v->voclen == len + len2) break;
+
+ if (v)
+ {
+ /* we found something - unlink from free list */
+ if (prv) prv->vocnxt = v->vocnxt;
+ else ctx->voccxfre = v->vocnxt;
+
+ /* reuse the entry */
+ v->vocwlst = VOCCXW_NONE;
+ vocset(ctx, v, p, objn, classflg, wrdtxt, len, wrd2, len2);
+ return;
+ }
+
+ /* didn't find an existing vocdef; allocate a new one */
+ need = sizeof(vocdef) + len + len2 - 1;
+ if (ctx->voccxrem < need)
+ {
+ /* not enough space in current page; allocate a new one */
+ ctx->voccxpool = mchalo(ctx->voccxerr, VOCPGSIZ, "vocadd2");
+ ctx->voccxrem = VOCPGSIZ;
+ }
+
+ /* use top of current pool, and update pool pointer and size */
+ v = (vocdef *)ctx->voccxpool;
+ need = osrndsz(need);
+ ctx->voccxpool += need;
+ if (ctx->voccxrem > need) ctx->voccxrem -= need;
+ else ctx->voccxrem = 0;
+
+ /* set up new vocdef */
+ v->vocwlst = VOCCXW_NONE;
+ vocset(ctx, v, p, objn, classflg, wrdtxt, len, wrd2, len2);
+}
+
+static void voc_parse_words(char **wrdtxt, int *len, char **wrd2, int *len2)
+{
+ /* get length and pointer to actual text */
+ *len = osrp2(*wrdtxt) - 2;
+ *wrdtxt += 2;
+
+ /* see if there's a second word - look for a space */
+ for (*wrd2 = *wrdtxt, *len2 = *len ; *len2 && !vocisspace(**wrd2) ;
+ ++*wrd2, --*len2) ;
+ if (*len2)
+ {
+ *len -= *len2;
+ while (*len2 && vocisspace(**wrd2)) ++*wrd2, --*len2;
+ }
+ else
+ {
+ /* no space ==> no second word */
+ *wrd2 = (char *)0;
+ }
+}
+
+void vocadd(voccxdef *ctx, prpnum p, objnum objn, int classflg, char *wrdtxt)
+{
+ int len;
+ char *wrd2;
+ int len2;
+
+ voc_parse_words(&wrdtxt, &len, &wrd2, &len2);
+ vocadd2(ctx, p, objn, classflg, (uchar *)wrdtxt, len, (uchar *)wrd2, len2);
+}
+
+/* make sure we have a page table entry for an object, allocating one if not */
+void vocialo(voccxdef *ctx, objnum obj)
+{
+ if (!ctx->voccxinh[obj >> 8])
+ {
+ ctx->voccxinh[obj >> 8] =
+ (vocidef **)mchalo(ctx->voccxerr,
+ (256 * sizeof(vocidef *)), "vocialo");
+ memset(ctx->voccxinh[obj >> 8], 0, (size_t)(256 * sizeof(vocidef *)));
+ }
+}
+
+/* add an inheritance/location record */
+void vociadd(voccxdef *ctx, objnum obj, objnum loc,
+ int numsc, objnum *sc, int flags)
+{
+ vocidef *v;
+ vocidef *min;
+ vocidef *prv;
+ vocidef *minprv = nullptr;
+
+ /* make sure we have a page table entry for this object */
+ vocialo(ctx, obj);
+
+ /* look in free list for an entry that's big enough */
+ for (prv = (vocidef *)0, min = (vocidef *)0, v = ctx->voccxifr ; v ;
+ prv = v, v = v->vocinxt)
+ {
+ if (v->vocinsc == numsc)
+ {
+ min = v;
+ minprv = prv;
+ break;
+ }
+ else if (v->vocinsc > numsc)
+ {
+ if (!min || v->vocinsc < min->vocinsc)
+ {
+ min = v;
+ minprv = prv;
+ }
+ }
+ }
+
+ if (!min)
+ {
+ uint need;
+
+ /* nothing in free list; allocate a new entry */
+ need = osrndsz(sizeof(vocidef) + (numsc - 1)*sizeof(objnum));
+ if (ctx->voccxilst + need >= VOCISIZ)
+ {
+ /* nothing left on current page; allocate a new page */
+ ctx->voccxip[++(ctx->voccxiplst)] =
+ mchalo(ctx->voccxerr, VOCISIZ, "vociadd");
+ ctx->voccxilst = 0;
+ }
+
+ /* allocate space out of current page */
+ v = (vocidef *)(ctx->voccxip[ctx->voccxiplst] + ctx->voccxilst);
+ ctx->voccxilst += need;
+ }
+ else
+ {
+ /* unlink from chain and use */
+ v = min;
+ if (minprv)
+ minprv->vocinxt = v->vocinxt;
+ else
+ ctx->voccxifr = v->vocinxt;
+ }
+
+ /* set up the entry */
+ if (vocinh(ctx, obj) != (vocidef *)0) errsig(ctx->voccxerr, ERR_VOCINUS);
+ v->vociloc = loc;
+ v->vociilc = MCMONINV;
+ v->vociflg = (flags & ~VOCIFXLAT);
+ v->vocinsc = numsc;
+ if (numsc)
+ {
+ if (flags & VOCIFXLAT)
+ {
+ int i;
+
+ for (i = 0 ; i < numsc ; ++i)
+ v->vocisc[i] = osrp2(&sc[i]);
+ }
+ else
+ memcpy(v->vocisc, sc, (size_t)(numsc * sizeof(objnum)));
+ }
+ vocinh(ctx, obj) = v; /* set page table entry */
+}
+
+/* revert all objects to original state, using inheritance records */
+void vocrevert(voccxdef *vctx)
+{
+ vocidef ***vpg;
+ vocidef **v;
+ int i;
+ int j;
+ objnum obj;
+
+ /*
+ * Go through the inheritance records. Delete each dynamically
+ * allocated object, and revert each static object to its original
+ * load state.
+ */
+ for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
+ {
+ if (!*vpg) continue;
+ for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
+ {
+ if (*v)
+ {
+ /* if the object was dynamically allocated, delete it */
+ if ((*v)->vociflg & VOCIFNEW)
+ {
+ /* delete vocabulary and inheritance data for the object */
+ vocidel(vctx, obj);
+ vocdel(vctx, obj);
+
+ /* delete the object */
+ mcmfre(vctx->voccxmem, (mcmon)obj);
+ }
+ else
+ {
+ /* revert the object */
+ mcmrevert(vctx->voccxmem, (mcmon)obj);
+ }
+ }
+ }
+ }
+
+ /*
+ * Revert the vocabulary list: delete all newly added words, and
+ * undelete all original words marked as deleted.
+ */
+ vocdel1(vctx, MCMONINV, (char *)0, 0, TRUE, TRUE, FALSE);
+}
+
+/* initialize voc context */
+void vocini(voccxdef *vocctx, errcxdef *errctx, mcmcxdef *memctx,
+ runcxdef *runctx, objucxdef *undoctx,
+ int fuses, int daemons, int notifiers)
+{
+ CLRSTRUCT(*vocctx);
+ vocctx->voccxerr = errctx;
+ vocctx->voccxiplst = (uint)-1;
+ vocctx->voccxilst = VOCISIZ;
+ vocctx->voccxmem = memctx;
+ vocctx->voccxrun = runctx;
+ vocctx->voccxundo = undoctx;
+
+ vocctx->voccxme =
+ vocctx->voccxme_init =
+ vocctx->voccxvtk =
+ vocctx->voccxstr =
+ vocctx->voccxnum =
+ vocctx->voccxit =
+ vocctx->voccxhim =
+ vocctx->voccxprd =
+ vocctx->voccxpre =
+ vocctx->voccxpre2 =
+ vocctx->voccxppc =
+ vocctx->voccxlsv =
+ vocctx->voccxpreinit =
+ vocctx->voccxper =
+ vocctx->voccxprom =
+ vocctx->voccxpostprom =
+ vocctx->voccxpdis =
+ vocctx->voccxper2 =
+ vocctx->voccxperp =
+ vocctx->voccxpdef =
+ vocctx->voccxpdef2 =
+ vocctx->voccxpask =
+ vocctx->voccxpask2 =
+ vocctx->voccxpask3 =
+ vocctx->voccxinitrestore =
+ vocctx->voccxpuv =
+ vocctx->voccxpnp =
+ vocctx->voccxpostact =
+ vocctx->voccxendcmd =
+ vocctx->voccxher = MCMONINV;
+ vocctx->voccxthc = 0;
+#ifdef VOCW_IN_CACHE
+ vocctx->voccxwplck = MCMONINV;
+#endif
+
+ vocctx->voccxactor = MCMONINV;
+ vocctx->voccxverb = MCMONINV;
+ vocctx->voccxprep = MCMONINV;
+ vocctx->voccxdobj = 0;
+ vocctx->voccxiobj = 0;
+
+ vocctx->voccxunknown = 0;
+ vocctx->voccxlastunk = 0;
+
+ vocctx->voc_stk_ptr = 0;
+ vocctx->voc_stk_cur = 0;
+ vocctx->voc_stk_end = 0;
+
+ /* allocate fuses, daemons, notifiers */
+ vocinialo(vocctx, &vocctx->voccxfus, (vocctx->voccxfuc = fuses));
+ vocinialo(vocctx, &vocctx->voccxdmn, (vocctx->voccxdmc = daemons));
+ vocinialo(vocctx, &vocctx->voccxalm, (vocctx->voccxalc = notifiers));
+
+ /* no entries in vocwdef free list yet */
+ vocctx->voccxwfre = VOCCXW_NONE;
+}
+
+/* uninitialize the voc context */
+void vocterm(voccxdef *ctx)
+{
+ /* delete the fuses, daemons, and notifiers */
+ voctermfree(ctx->voccxfus);
+ voctermfree(ctx->voccxdmn);
+ voctermfree(ctx->voccxalm);
+
+ /* delete the private stack */
+ if (ctx->voc_stk_ptr != 0)
+ mchfre(ctx->voc_stk_ptr);
+}
+
+/* clean up the vocab context */
+void voctermfree(vocddef *what)
+{
+ if (what != 0)
+ mchfre(what);
+}
+
+/*
+ * Iterate through all words for a particular object, calling a
+ * function with each vocwdef found. If objn == MCMONINV, we'll call
+ * the callback for every word.
+ */
+void voc_iterate(voccxdef *ctx, objnum objn,
+ void (*fn)(void *, vocdef *, vocwdef *), void *fnctx)
+{
+ int i;
+ vocdef *v;
+ vocdef **vp;
+ vocwdef *vw;
+ uint idx;
+
+ /* go through each hash value looking for matching words */
+ for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i ; ++vp, --i)
+ {
+ /* go through all words in this hash chain */
+ for (v = *vp ; v ; v = v->vocnxt)
+ {
+ /* go through each object relation for this word */
+ for (idx = v->vocwlst, vw = vocwget(ctx, idx) ; vw ;
+ idx = vw->vocwnxt, vw = vocwget(ctx, idx))
+ {
+ /*
+ * if this word is for this object, call the callback
+ */
+ if (objn == MCMONINV || vw->vocwobj == objn)
+ (*fn)(fnctx, v, vw);
+ }
+ }
+ }
+}
+
+/* callback context for voc_count */
+struct voc_count_ctx
+{
+ int cnt;
+ int siz;
+ prpnum prp;
+};
+
+/* callback for voc_count */
+static void voc_count_cb(void *ctx0, vocdef *voc, vocwdef *vocw)
+{
+ struct voc_count_ctx *ctx = (struct voc_count_ctx *)ctx0;
+
+ VARUSED(vocw);
+
+ /*
+ * If it matches the property (or we want all properties), count
+ * it. Don't count deleted objects.
+ */
+ if ((ctx->prp == 0 || ctx->prp == vocw->vocwtyp)
+ && !(vocw->vocwflg & VOCFDEL))
+ {
+ /* count the word */
+ ctx->cnt++;
+
+ /* count the size */
+ ctx->siz += voc->voclen + voc->vocln2;
+ }
+}
+
+/*
+ * Get the number and size of words defined for an object. The size
+ * returns the total byte count from all the words involved. Do not
+ * include deleted words in the count.
+ */
+void voc_count(voccxdef *ctx, objnum objn, prpnum prp, int *cnt, int *siz)
+{
+ struct voc_count_ctx fnctx;
+
+ /* set up the context with zero initial counts */
+ fnctx.cnt = 0;
+ fnctx.siz = 0;
+ fnctx.prp = prp;
+
+ /* iterate over all words for the object with our callback */
+ voc_iterate(ctx, objn, voc_count_cb, &fnctx);
+
+ /* return the data */
+ if (cnt) *cnt = fnctx.cnt;
+ if (siz) *siz = fnctx.siz;
+}
+
+
+/*
+ * Delete a particular word associated with an object, or all words if
+ * the word pointer is null. If the word isn't marked as added at
+ * runtime (i.e., the VOCFNEW flag is not set for the word), the word is
+ * simply marked as deleted, rather than being actually deleted.
+ * However, if the 'really_delete' flag is set, the word is actually
+ * deleted. If the 'revert' flag is true, this routine deletes _every_
+ * dynamically created word, and undeletes all dynamically deleted words
+ * that were in the original vocabulary.
+ */
+void vocdel1(voccxdef *ctx, objnum objn, char *wrd1, prpnum prp,
+ int really_delete, int revert, int keep_undo)
+{
+ int i;
+ vocdef *v;
+ vocdef *prv;
+ vocdef *nxt;
+ vocdef **vp;
+ vocwdef *vw;
+ vocwdef *prvw;
+ vocwdef *nxtw;
+ uint nxtidx;
+ uint idx;
+ int deleted_vocdef;
+ char *wrd2 = nullptr;
+ int len1 = 0, len2 = 0;
+ int do_del;
+ char *orgwrd;
+
+ /* parse the word if provided */
+ orgwrd = wrd1;
+ if (wrd1)
+ voc_parse_words(&wrd1, &len1, &wrd2, &len2);
+
+ /* go through each hash value looking for matching words */
+ for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i ; ++vp, --i)
+ {
+ /* go through all words in this hash chain */
+ for (prv = (vocdef *)0, v = *vp ; v ; v = nxt)
+ {
+ /* remember next word in hash chain */
+ nxt = v->vocnxt;
+
+ /* if this word doesn't match, skip it */
+ if (wrd1)
+ {
+ /* compare the first word */
+ if (v->voclen != len1
+ || memicmp((char *)v->voctxt, wrd1, (size_t)len1))
+ {
+ prv = v;
+ continue;
+ }
+
+ /* if there's a second word, compare it as well */
+ if (wrd2 && (v->vocln2 != len2
+ || memicmp((char *)v->voctxt + len1,
+ wrd2, (size_t)len2)))
+ {
+ prv = v;
+ continue;
+ }
+ }
+
+ /* presume we're not going to delete this vocdef */
+ deleted_vocdef = FALSE;
+
+ /* go through all object relations for this word */
+ for (prvw = 0, idx = v->vocwlst, vw = vocwget(ctx, idx) ; vw ;
+ vw = nxtw, idx = nxtidx)
+ {
+ /* remember next word in relation list */
+ nxtidx = vw->vocwnxt;
+ nxtw = vocwget(ctx, nxtidx);
+
+ /*
+ * figure out whether to delete this word, based on the
+ * caller's specified operating mode
+ */
+ if (revert)
+ {
+ /* if reverting, delete all new words */
+ do_del = (vw->vocwflg & VOCFNEW);
+
+ /* also, remove the DELETED flag if present */
+ vw->vocwflg &= ~VOCFDEL;
+ }
+ else
+ {
+ /*
+ * delete the word if the object number matches,
+ * AND either we're not searching for a specific
+ * vocabulary word (in which case wrd1 will be null)
+ * or the word matches the vocabulary word we're
+ * seeking (in which case the part of speech must
+ * match)
+ */
+ do_del = (vw->vocwobj == objn
+ && (wrd1 == 0 || vw->vocwtyp == prp));
+
+ /*
+ * if we're not in really_delete mode, and the word
+ * matches and it wasn't dynamically added at
+ * run-time, simply mark it as deleted -- this will
+ * allow it to be undeleted if the game is reverted
+ * to RESTART conditions
+ */
+ if (do_del && !really_delete && !(vw->vocwflg & VOCFNEW))
+ {
+ /* geneate undo for the operation */
+ if (keep_undo && orgwrd)
+ vocdusave_delwrd(ctx, objn, prp,
+ vw->vocwflg, orgwrd);
+
+ /* just mark the word for deletion, but keep vw */
+ vw->vocwflg |= VOCFDEL;
+ do_del = FALSE;
+ }
+ }
+
+ /* now delete the structure if we decided we should */
+ if (do_del)
+ {
+ /* geneate undo for the operation */
+ if (keep_undo && orgwrd)
+ vocdusave_delwrd(ctx, objn, prp, vw->vocwflg, orgwrd);
+
+ /* unlink this vocwdef from the vocdef's list */
+ if (prvw)
+ prvw->vocwnxt = vw->vocwnxt;
+ else
+ v->vocwlst = vw->vocwnxt;
+
+ /* link the vocwdef into the vocwdef free list */
+ vw->vocwnxt = ctx->voccxwfre;
+ ctx->voccxwfre = idx;
+
+ /*
+ * if there's nothing left in the vocdef's list,
+ * delete the entire vocdef as well
+ */
+ if (v->vocwlst == VOCCXW_NONE)
+ {
+ if (prv) prv->vocnxt = v->vocnxt;
+ else *vp = v->vocnxt;
+
+ /* link into free chain */
+ v->vocnxt = ctx->voccxfre;
+ ctx->voccxfre = v;
+
+ /* note that it's been deleted */
+ deleted_vocdef = TRUE;
+ }
+ }
+ else
+ {
+ /* we're not deleting the word, so move prvw forward */
+ prvw = vw;
+ }
+ }
+
+ /* if we didn't delete this vocdef, move prv forward onto it */
+ if (!deleted_vocdef)
+ prv = v;
+ }
+ }
+}
+
+/* delete all vocabulary for an object */
+void vocdel(voccxdef *ctx, objnum objn)
+{
+ vocdel1(ctx, objn, (char *)0, (prpnum)0, TRUE, FALSE, FALSE);
+}
+
+/* delete object inheritance records for a particular object */
+void vocidel(voccxdef *ctx, objnum obj)
+{
+ vocidef *v;
+
+ /* get entry out of page table, and clear page table slot */
+ v = vocinh(ctx, obj);
+ vocinh(ctx, obj) = (vocidef *)0;
+
+ /* link into free list */
+ if (v)
+ {
+ v->vocinxt = ctx->voccxifr;
+ ctx->voccxifr = v;
+ }
+}
+
+/*
+ * Find template matching a verb+object+prep combination; return TRUE
+ * if a suitable template is found, FALSE otherwise.
+ */
+int voctplfnd(voccxdef *ctx, objnum verb_in, objnum prep,
+ uchar *tplout, int *newstyle)
+{
+ uchar *tplptr;
+ uchar *thistpl;
+ int found;
+ int tplcnt;
+ uint tplofs;
+ objnum verb;
+
+ /* look for a new-style template first */
+ tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL2, &verb, FALSE);
+ if (tplofs)
+ {
+ /* flag the presence of the new-style template */
+ *newstyle = TRUE;
+ }
+ else
+ {
+ /* no new-style template - look for old version */
+ tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL, &verb, FALSE);
+ *newstyle = FALSE;
+ }
+
+ /* inherit templates until we run out of them */
+ for (;;)
+ {
+ /* if we found something already, use it */
+ if (tplofs)
+ {
+ size_t siz;
+
+ /* figure the size of this template style */
+ siz = (*newstyle ? VOCTPL2SIZ : VOCTPLSIZ);
+
+ /* lock the verb object, and get the property value pointer */
+ tplptr = mcmlck(ctx->voccxmem, verb);
+ thistpl = prpvalp(tplptr + tplofs);
+
+ /* first byte is number of templates in array */
+ tplcnt = *thistpl++;
+
+ /* look for a template that matches the preposition object */
+ for (found = FALSE ; tplcnt ; thistpl += siz, --tplcnt)
+ {
+ if (voctplpr(thistpl) == prep)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ /* unlock the object and return the value if we found one */
+ mcmunlck(ctx->voccxmem, verb);
+ if (found)
+ {
+ memcpy(tplout, thistpl, siz);
+ return(TRUE);
+ }
+ }
+
+ /* try inheriting a template (new-style, then old-style) */
+ tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL2, &verb, TRUE);
+ if (tplofs)
+ *newstyle = TRUE;
+ else
+ {
+ tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL, &verb, TRUE);
+ *newstyle = FALSE;
+ }
+
+ /* return not-found if we couldn't inherit it */
+ if (!tplofs)
+ return FALSE;
+
+ /* use the newly found verb */
+ verb_in = verb;
+ }
+}
+
+/*
+ * Set the "Me" object
+ */
+void voc_set_me(voccxdef *ctx, objnum new_me)
+{
+ /* save undo for the change */
+ vocdusave_me(ctx, ctx->voccxme);
+
+ /* set the new "Me" object */
+ ctx->voccxme = new_me;
+}
} // End of namespace TADS2
} // End of namespace TADS