summaryrefslogtreecommitdiff
path: root/Src/Zle/compmatch.c
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-11-01 09:37:26 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-11-01 09:37:26 +0000
commit82e6f2438e05405ba6b10144834f13546f54fce4 (patch)
tree0ce9a8c8ead0e52e6e00c0a2960350fb8cefe386 /Src/Zle/compmatch.c
parenta17347df5947b438c36b2f3d1eaa966e11e25a83 (diff)
downloadzsh-82e6f2438e05405ba6b10144834f13546f54fce4.tar.gz
zsh-82e6f2438e05405ba6b10144834f13546f54fce4.zip
Initial revision
Diffstat (limited to 'Src/Zle/compmatch.c')
-rw-r--r--Src/Zle/compmatch.c1906
1 files changed, 1906 insertions, 0 deletions
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
new file mode 100644
index 000000000..32e0c3a68
--- /dev/null
+++ b/Src/Zle/compmatch.c
@@ -0,0 +1,1906 @@
+/*
+ * compmatch.c - the complete module, completion matching code
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose. The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complete.mdh"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+#include "compmatch.pro"
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). */
+
+#define quotename(s, e) bslashquote(s, e, instring)
+
+/* This compares two cpattern lists and returns non-zero if they are
+ * equal. */
+
+/**/
+static int
+cmp_cpatterns(Cpattern a, Cpattern b)
+{
+ while (a) {
+ if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256))
+ return 0;
+ a = a->next;
+ b = b->next;
+ }
+ return 1;
+}
+
+/* This compares two cmatchers and returns non-zero if they are equal. */
+
+/**/
+static int
+cmp_cmatchers(Cmatcher a, Cmatcher b)
+{
+ return (a == b ||
+ (a->flags == b->flags &&
+ a->llen == b->llen && a->wlen == b->wlen &&
+ (!a->llen || cmp_cpatterns(a->line, b->line)) &&
+ (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) &&
+ (!(a->flags & CMF_LEFT) ||
+ (a->lalen == b->lalen &&
+ (!a->lalen || cmp_cpatterns(a->left, b->left)))) &&
+ (!(a->flags & CMF_RIGHT) ||
+ (a->ralen == b->ralen &&
+ (!a->ralen || cmp_cpatterns(a->right, b->right))))));
+}
+
+/* Add the given matchers to the bmatcher list. */
+
+/**/
+void
+add_bmatchers(Cmatcher m)
+{
+ Cmlist old = bmatchers, *q = &bmatchers, n;
+
+ for (; m; m = m->next) {
+ if ((!m->flags && m->wlen > 0 && m->llen > 0) ||
+ (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) {
+ *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
+ n->matcher = m;
+ q = &(n->next);
+ }
+ }
+ *q = old;
+}
+
+/* This is called when the matchers in the mstack have changed to
+ * ensure that the bmatchers list contains no matchers not in mstack. */
+
+/**/
+void
+update_bmatchers(void)
+{
+ Cmlist p = bmatchers, q = NULL, ms;
+ Cmatcher mp;
+ int t;
+
+ while (p) {
+ t = 0;
+ for (ms = mstack; ms && !t; ms = ms->next)
+ for (mp = ms->matcher; mp && !t; mp = mp->next)
+ t = cmp_cmatchers(mp, p->matcher);
+
+ p = p->next;
+ if (!t) {
+ if (q)
+ q->next = p;
+ else
+ bmatchers = p;
+ }
+ }
+}
+
+/* This returns a new Cline structure. */
+
+/**/
+Cline
+get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl)
+{
+ Cline r;
+
+ /* Prefer to take it from the buffer list (freecl), if there
+ * is none, allocate a new one. */
+
+ if ((r = freecl))
+ freecl = r->next;
+ else
+ r = (Cline) zhalloc(sizeof(*r));
+
+ r->next = NULL;
+ r->line = l; r->llen = ll;
+ r->word = w; r->wlen = wl;
+ r->orig = o; r->olen = ol;
+ r->slen = 0;
+ r->flags = fl;
+ r->prefix = r->suffix = NULL;
+ return r;
+}
+
+/* This frees a cline list. */
+
+/**/
+void
+free_cline(Cline l)
+{
+ Cline n;
+
+ while (l) {
+ n = l->next;
+ l->next = freecl;
+ freecl = l;
+ free_cline(l->prefix);
+ free_cline(l->suffix);
+ l = n;
+ }
+}
+
+/* Copy a cline list. */
+
+/**/
+Cline
+cp_cline(Cline l, int deep)
+{
+ Cline r = NULL, *p = &r, t, lp = NULL;
+
+ while (l) {
+ if ((t = freecl))
+ freecl = t->next;
+ else
+ t = (Cline) zhalloc(sizeof(*t));
+ memcpy(t, l, sizeof(*t));
+ if (deep) {
+ if (t->prefix)
+ t->prefix = cp_cline(t->prefix, 0);
+ if (t->suffix)
+ t->suffix = cp_cline(t->suffix, 0);
+ }
+ *p = lp = t;
+ p = &(t->next);
+ l = l->next;
+ }
+ *p = NULL;
+
+ return r;
+}
+
+/* Calculate the length of a cline and its sub-lists. */
+
+/**/
+int
+cline_sublen(Cline l)
+{
+ int len = ((l->flags & CLF_LINE) ? l->llen : l->wlen);
+
+ if (l->olen && !((l->flags & CLF_SUF) ? l->suffix : l->prefix))
+ len += l->olen;
+ else {
+ Cline p;
+
+ for (p = l->prefix; p; p = p->next)
+ len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+ for (p = l->suffix; p; p = p->next)
+ len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+ }
+ return len;
+}
+
+/* Set the lengths in the cline lists. */
+
+/**/
+void
+cline_setlens(Cline l, int both)
+{
+ while (l) {
+ l->max = cline_sublen(l);
+ if (both)
+ l->min = l->max;
+ l = l->next;
+ }
+}
+
+/* This sets the CLF_MATCHED flag in the given clines. */
+
+/**/
+void
+cline_matched(Cline p)
+{
+ while (p) {
+ p->flags |= CLF_MATCHED;
+ cline_matched(p->prefix);
+ cline_matched(p->suffix);
+
+ p = p->next;
+ }
+}
+
+/* This reverts the order of the elements of the given cline list and
+ * returns a pointer to the new head. */
+
+/**/
+Cline
+revert_cline(Cline p)
+{
+ Cline r = NULL, n;
+
+ while (p) {
+ n = p->next;
+ p->next = r;
+ r = p;
+ p = n;
+ }
+ return r;
+}
+
+/* Global variables used during matching: a char-buffer for the string to
+ * use for the match, and two cline lists for the two levels we use. */
+
+/**/
+char *matchbuf = NULL;
+/**/
+int matchbuflen = 0, matchbufadded;
+
+/**/
+Cline matchparts, matchlastpart;
+/**/
+Cline matchsubs, matchlastsub;
+
+/* This initialises the variables above. */
+
+/**/
+static void
+start_match(void)
+{
+ if (matchbuf)
+ *matchbuf = '\0';
+ matchbufadded = 0;
+ matchparts = matchlastpart = matchsubs = matchlastsub = NULL;
+}
+
+/* This aborts a matching, freeing the cline lists build. */
+
+/**/
+static void
+abort_match(void)
+{
+ free_cline(matchparts);
+ free_cline(matchsubs);
+ matchparts = matchsubs = NULL;
+}
+
+/* This adds a new string in the static char buffer. The arguments are
+ * the matcher used (if any), the strings from the line and the word
+ * and the length of the string from the word. The last argument is
+ * non-zero if we are matching a suffix (where the given string has to
+ * be prepended to the contents of the buffer). */
+
+/**/
+static void
+add_match_str(Cmatcher m, char *l, char *w, int wl, int sfx)
+{
+ /* Get the string and length to insert: either from the line
+ * or from the match. */
+ if (m && (m->flags & CMF_LINE)) {
+ wl = m->llen; w = l;
+ }
+ if (wl) {
+ /* Probably resize the buffer. */
+ if (matchbuflen - matchbufadded <= wl) {
+ int blen = matchbuflen + wl + 20;
+ char *buf;
+
+ buf = (char *) zalloc(blen);
+ memcpy(buf, matchbuf, matchbuflen);
+ zfree(matchbuf, matchbuflen);
+ matchbuf = buf;
+ matchbuflen = blen;
+ }
+ /* Insert the string. */
+ if (sfx) {
+ memmove(matchbuf + wl, matchbuf, matchbufadded + 1);
+ memcpy(matchbuf, w, wl);
+ } else
+ memcpy(matchbuf + matchbufadded, w, wl);
+ matchbufadded += wl;
+ matchbuf[matchbufadded] = '\0';
+ }
+}
+
+/* This adds a cline for a word-part during matching. Arguments are the
+ * matcher used, pointers to the line and word strings for the anchor,
+ * a pointer to the original line string for the whole part, the string
+ * before (or after) the anchor that has not yet been added, the length
+ * of the line-string for that, and a flag saying if we are matching a
+ * suffix. */
+
+/**/
+static void
+add_match_part(Cmatcher m, char *l, char *w, int wl,
+ char *o, int ol, char *s, int sl, int osl, int sfx)
+{
+ Cline p, lp;
+
+ /* If the anchors are equal, we keep only one. */
+
+ if (!strncmp(l, w, wl))
+ l = NULL;
+
+ /* Split the new part into parts and turn the last one into a
+ * `suffix' if we have a left anchor. */
+
+ p = bld_parts(s, sl, osl, &lp);
+
+ p->flags &= ~CLF_NEW;
+ if (m && (m->flags & CMF_LEFT)) {
+ lp->flags |= CLF_SUF;
+ lp->suffix = lp->prefix;
+ lp->prefix = NULL;
+ }
+ /* cline lists for suffixes are sorted from back to front, so we have
+ * to revert the list we got. */
+ if (sfx)
+ p = revert_cline(lp = p);
+ /* Now add the sub-clines we already had. */
+ if (matchsubs) {
+ if (sfx) {
+ Cline q;
+
+ if ((q = lp->prefix)) {
+ while (q->next)
+ q = q->next;
+ q->next = matchsubs;
+ } else
+ lp->prefix = matchsubs;
+
+ matchlastsub->next = NULL;
+ } else {
+ matchlastsub->next = p->prefix;
+ p->prefix = matchsubs;
+ }
+ matchsubs = matchlastsub = NULL;
+ }
+ /* Store the arguments in the last part-cline. */
+ lp->line = l; lp->llen = wl;
+ lp->word = w; lp->wlen = wl;
+ lp->orig = o; lp->olen = ol;
+ lp->flags &= ~CLF_NEW;
+
+ /* Finally, put the new parts on the list. */
+ if (matchlastpart)
+ matchlastpart->next = p;
+ else
+ matchparts = p;
+ matchlastpart = lp;
+}
+
+/* This adds a new sub-cline. Arguments are the matcher and the strings from
+ * the line and the word. */
+
+/**/
+static void
+add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
+{
+ int flags;
+ Cline n;
+
+ /* Check if we are interested only in the string from the line. */
+ if (m && (m->flags & CMF_LINE)) {
+ w = NULL; wl = 0;
+ flags = CLF_LINE;
+ } else
+ flags = 0;
+
+ /* And add the cline. */
+ if (wl || ll) {
+ n = get_cline(l, ll, w, wl, NULL, 0, flags);
+ if (matchlastsub)
+ matchlastsub->next = n;
+ else
+ matchsubs = n;
+ matchlastsub = n;
+ }
+}
+
+/* This tests if the string from the line l matches the word w. In bp
+ * the offset for the brace is returned, in rwlp the length of the
+ * matched prefix or suffix, not including the stuff before or after
+ * the last anchor is given. When sfx is non-zero matching is done from
+ * the ends of the strings backward, if test is zero, the global variables
+ * above are used to build the string for the match and the cline. If
+ * part is non-zero, we are satisfied if only a part of the line-string
+ * is used (and return the length used). */
+
+/**/
+int
+match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
+ int sfx, int test, int part)
+{
+ int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
+ int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc;
+ VARARR(unsigned char, ea, ll + 1);
+ char *ow;
+ Cmlist ms;
+ Cmatcher mp, lm = NULL;
+ Brinfo bp = NULL;
+
+ if (!test) {
+ start_match();
+ bp = *bpp;
+ }
+ /* Adjust the pointers and get the values for subscripting and
+ * incrementing. */
+
+ if (sfx) {
+ l += ll; w += lw;
+ ind = -1; add = -1;
+ } else {
+ ind = 0; add = 1;
+ }
+ /* ow will always point to the beginning (or end) of that sub-string
+ * in w that wasn't put in the match-variables yet. */
+
+ ow = w;
+
+ /* If the brace is at the beginning, we have to treat it now. */
+
+ if (!test && bp && bc >= bp->pos) {
+ bp->curpos = bc;
+ bp = bp->next;
+ }
+ while (ll && lw) {
+ /* First try the matchers. */
+ for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) {
+ for (mp = ms->matcher; mp; mp = mp->next) {
+ t = 1;
+ if ((lm && lm == mp) ||
+ ((oll == ll || olw == lw) &&
+ (test == 1 || (test && !mp->left && !mp->right)) &&
+ mp->wlen < 0))
+ /* If we were called recursively, don't use `*' patterns
+ * at the beginning (avoiding infinite recursion). */
+ continue;
+
+ if (mp->wlen < 0) {
+ int both, loff, aoff, llen, alen, zoff, moff, ct, ict;
+ char *tp, savl = '\0', savw;
+ Cpattern ap;
+
+ /* This is for `*' patterns, first initialise some
+ * local variables. */
+ llen = mp->llen;
+ alen = (mp->flags & CMF_LEFT ? mp->lalen : mp->ralen);
+
+ /* Give up if we don't have enough characters for the
+ * line-string and the anchor. */
+ if (ll < llen + alen || lw < alen)
+ continue;
+
+ if (mp->flags & CMF_LEFT) {
+ ap = mp->left; zoff = 0; moff = alen;
+ if (sfx) {
+ both = 0; loff = -llen; aoff = -(llen + alen);
+ } else {
+ both = 1; loff = alen; aoff = 0;
+ }
+ } else {
+ ap = mp->right; zoff = alen; moff = 0;
+ if (sfx) {
+ both = 1; loff = -(llen + alen); aoff = -alen;
+ } else {
+ both = 0; loff = 0; aoff = llen;
+ }
+ }
+ /* Try to match the line pattern and the anchor. */
+ if (!pattern_match(mp->line, l + loff, NULL, NULL))
+ continue;
+ if (ap) {
+ if (!pattern_match(ap, l + aoff, NULL, NULL) ||
+ (both && (!pattern_match(ap, w + aoff, NULL, NULL) ||
+ !match_parts(l + aoff, w + aoff, alen,
+ part))))
+ continue;
+ } else if (!both || il || iw)
+ continue;
+
+ /* Fine, now we call ourselves recursively to find the
+ * string matched by the `*'. */
+ if (sfx) {
+ savl = l[-(llen + zoff)];
+ l[-(llen + zoff)] = '\0';
+ }
+ for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
+ ict;
+ tp += add, ct++, ict--) {
+ if ((both &&
+ (!ap || !test ||
+ !pattern_match(ap, tp + aoff, NULL, NULL))) ||
+ (!both &&
+ pattern_match(ap, tp - moff, NULL, NULL) &&
+ match_parts(l + aoff , tp - moff, alen, part))) {
+ if (sfx) {
+ savw = tp[-zoff];
+ tp[-zoff] = '\0';
+ t = match_str(l - ll, w - lw,
+ NULL, 0, NULL, 1, 2, part);
+ tp[-zoff] = savw;
+ } else
+ t = match_str(l + llen + moff, tp + moff,
+ NULL, 0, NULL, 0, 1, part);
+ if (t || !both)
+ break;
+ }
+ }
+ ict = ct;
+ if (sfx)
+ l[-(llen + zoff)] = savl;
+
+ /* Have we found a position in w where the rest of l
+ * matches? */
+ if (!t)
+ continue;
+
+ /* Yes, add the strings and clines if this is a
+ * top-level call. */
+ if (!test && (!he || (llen + alen))) {
+ char *op, *lp, *map, *wap, *wmp;
+ int ol;
+
+ if (sfx) {
+ op = w; ol = ow - w; lp = l - (llen + alen);
+ map = tp - alen;
+ if (mp->flags & CMF_LEFT) {
+ wap = tp - alen; wmp = tp;
+ } else {
+ wap = w - alen; wmp = tp - alen;
+ }
+ } else {
+ op = ow; ol = w - ow; lp = l;
+ map = ow;
+ if (mp->flags & CMF_LEFT) {
+ wap = w; wmp = w + alen;
+ } else {
+ wap = tp; wmp = ow;
+ }
+ }
+ /* If the matcher says that we are only interested
+ * in the line pattern, we just add that and the
+ * anchor and the string not added yet. Otherwise
+ * we add a new part. */
+ if (mp->flags & CMF_LINE) {
+ add_match_str(NULL, NULL, op, ol, sfx);
+ add_match_str(NULL, NULL, lp, llen + alen, sfx);
+ add_match_sub(NULL, NULL, ol, op, ol);
+ add_match_sub(NULL, NULL, llen + alen,
+ lp, llen + alen);
+ } else if (sfx) {
+ add_match_str(NULL, NULL,
+ map, ct + ol + alen, sfx);
+ add_match_part(mp, l + aoff, wap, alen,
+ l + loff, llen, op, ol, ol, sfx);
+ add_match_sub(NULL, NULL, 0, wmp, ct);
+ } else {
+ add_match_str(NULL, NULL,
+ map, ct + ol + alen, sfx);
+ if (both) {
+ add_match_sub(NULL, NULL, ol, op, ol);
+ ol = -1;
+ } else
+ ct += ol;
+ add_match_part(mp, l + aoff, wap, alen,
+ l + loff, llen, wmp, ct, ol, sfx);
+ }
+ }
+ /* Now skip over the matched portion and the anchor. */
+ llen += alen; alen += ict;
+ if (sfx) {
+ l -= llen; w -= alen;
+ } else {
+ l += llen; w += alen;
+ }
+ ll -= llen; il += llen;
+ lw -= alen; iw += alen;
+ bc += llen;
+
+ if (!test)
+ while (bp &&
+ bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
+ bp->curpos = matchbufadded + bpc - bc + obc;
+ bp = bp->next;
+ }
+ ow = w;
+
+ if (!llen && !alen) {
+ lm = mp;
+ if (he)
+ mp = NULL;
+ else
+ he = 1;
+ } else {
+ lm = NULL; he = 0;
+ }
+ break;
+ } else if (ll >= mp->llen && lw >= mp->wlen) {
+ /* Non-`*'-pattern. */
+ char *tl, *tw;
+ int tll, tlw, til, tiw;
+
+ /* We do this only if the line- and word-substrings
+ * are not equal. */
+ if (!(mp->flags & (CMF_LEFT | CMF_RIGHT)) &&
+ mp->llen == mp->wlen &&
+ !(sfx ? strncmp(l - mp->llen, w - mp->wlen, mp->llen) :
+ strncmp(l, w, mp->llen)))
+ continue;
+
+ /* Using local variables to make the following
+ * independent of whether we match a prefix or a
+ * suffix. */
+ if (sfx) {
+ tl = l - mp->llen; tw = w - mp->wlen;
+ til = ll - mp->llen; tiw = lw - mp->wlen;
+ tll = il + mp->llen; tlw = iw + mp->wlen;
+ } else {
+ tl = l; tw = w;
+ til = il; tiw = iw;
+ tll = ll; tlw = lw;
+ }
+ if (mp->flags & CMF_LEFT) {
+ /* Try to match the left anchor, if any. */
+ if (til < mp->lalen || tiw < mp->lalen)
+ continue;
+ else if (mp->left)
+ t = pattern_match(mp->left, tl - mp->lalen,
+ NULL, NULL) &&
+ pattern_match(mp->left, tw - mp->lalen,
+ NULL, NULL);
+ else
+ t = (!sfx && !il && !iw);
+ }
+ if (mp->flags & CMF_RIGHT) {
+ /* Try to match the right anchor, if any. */
+ if (tll < mp->llen + mp->ralen ||
+ tlw < mp->wlen + mp->ralen)
+ continue;
+ else if (mp->left)
+ t = pattern_match(mp->right,
+ tl + mp->llen - mp->ralen,
+ NULL, NULL) &&
+ pattern_match(mp->right,
+ tw + mp->wlen - mp->ralen,
+ NULL, NULL);
+ else
+ t = (sfx && !il && !iw);
+ }
+ /* Now try to match the line and word patterns. */
+ if (!t ||
+ !pattern_match(mp->line, tl, NULL, ea) ||
+ !pattern_match(mp->word, tw, ea, NULL))
+ continue;
+
+ /* Probably add the matched strings. */
+ if (!test) {
+ if (sfx)
+ add_match_str(NULL, NULL, w, ow - w, 0);
+ else
+ add_match_str(NULL, NULL, ow, w - ow, 0);
+ add_match_str(mp, tl, tw, mp->wlen, 0);
+ if (sfx)
+ add_match_sub(NULL, NULL, 0, w, ow - w);
+ else
+ add_match_sub(NULL, NULL, 0, ow, w - ow);
+
+ add_match_sub(mp, tl, mp->llen, tw, mp->wlen);
+ }
+ if (sfx) {
+ l = tl; w = tw;
+ } else {
+ l += mp->llen; w += mp->wlen;
+ }
+ il += mp->llen; iw += mp->wlen;
+ ll -= mp->llen; lw -= mp->wlen;
+ bc += mp->llen;
+
+ if (!test)
+ while (bp &&
+ bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
+ bp->curpos = matchbufadded + bpc - bc + obc;
+ bp = bp->next;
+ }
+ ow = w;
+ lm = NULL;
+ he = 0;
+ break;
+ }
+ }
+ }
+ if (mp)
+ continue;
+
+ if (l[ind] == w[ind]) {
+ /* No matcher could be used, but the strings have the same
+ * character here, skip over it. */
+ l += add; w += add;
+ il++; iw++;
+ ll--; lw--;
+ bc++;
+ if (!test)
+ while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) {
+ bp->curpos = matchbufadded + (sfx ? (ow - w) : (w - ow)) + obc;
+ bp = bp->next;
+ }
+ lm = NULL;
+ he = 0;
+ } else {
+ /* No matcher and different characters: l does not match w. */
+ if (test)
+ return 0;
+
+ abort_match();
+
+ return -1;
+ }
+ }
+ /* If this is a recursive call, we just return if l matched w or not. */
+ if (test)
+ return (part || !ll);
+
+ /* In top-level calls, if ll is non-zero (unmatched portion in l),
+ * we have to free the collected clines. */
+ if (!part && ll) {
+ abort_match();
+
+ return -1;
+ }
+ if (rwlp)
+ *rwlp = iw - (sfx ? ow - w : w - ow);
+
+ /* If we matched a suffix, the anchors stored in the top-clines
+ * will be in the wrong clines: shifted by one. Adjust this. */
+ if (sfx && matchparts) {
+ Cline t, tn, s;
+
+ if (matchparts->prefix || matchparts->suffix) {
+ t = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+ t->next = matchparts;
+ if (matchparts->prefix)
+ t->prefix = (Cline) 1;
+ else
+ t->suffix = (Cline) 1;
+ matchparts = t;
+ }
+ for (t = matchparts; (tn = t->next); t = tn) {
+ s = (tn->prefix ? tn->prefix : tn->suffix);
+ if (t->suffix)
+ t->suffix = s;
+ else
+ t->prefix = s;
+ }
+ t->prefix = t->suffix = NULL;
+ }
+ /* Finally, return the number of matched characters. */
+
+ *bpp = bp;
+ return (part ? il : iw);
+}
+
+/* Wrapper for match_str(), only for a certain length and only doing
+ * the test. */
+
+/**/
+static int
+match_parts(char *l, char *w, int n, int part)
+{
+ char lsav = l[n], wsav = w[n];
+ int ret;
+
+ l[n] = w[n] = '\0';
+ ret = match_str(l, w, NULL, 0, NULL, 0, 1, part);
+ l[n] = lsav;
+ w[n] = wsav;
+
+ return ret;
+}
+
+/* Check if the word w is matched by the strings in pfx and sfx (the prefix
+ * and the suffix from the line) or the pattern cp. In clp a cline list for
+ * w is returned.
+ * qu is non-zero if the words has to be quoted before processed any further.
+ * bpl and bsl are used to report the positions where the brace-strings in
+ * the prefix and the suffix have to be re-inserted if this match is inserted
+ * in the line.
+ * The return value is the string to use as a completion or NULL if the prefix
+ * and the suffix don't match the word w. */
+
+/**/
+char *
+comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
+ Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact)
+{
+ char *r = NULL;
+
+ if (cp) {
+ /* We have a globcomplete-like pattern, just use that. */
+ int wl;
+
+ r = w;
+ if (!pattry(cp, r))
+ return NULL;
+
+ r = (qu ? quotename(r, NULL) : dupstring(r));
+ if (qu == 2 && r[0] == '\\' && r[1] == '~')
+ chuck(r);
+ /* We still break it into parts here, trying to build a sensible
+ * cline list for these matches, too. */
+ w = dupstring(w);
+ wl = strlen(w);
+ *clp = bld_parts(w, wl, wl, NULL);
+ *exact = 0;
+ } else {
+ Cline pli, plil;
+ int mpl, rpl, wl;
+
+ w = (qu ? quotename(w, NULL) : dupstring(w));
+ if (qu == 2 && w[0] == '\\' && w[1] == '~')
+ chuck(w);
+
+ wl = strlen(w);
+
+ /* Always try to match the prefix. */
+
+ useqbr = qu;
+ if ((mpl = match_str(pfx, w, bpl, bcp, &rpl, 0, 0, 0)) < 0)
+ return NULL;
+
+ if (sfx && *sfx) {
+ int wpl = matchbufadded, msl, rsl;
+ VARARR(char, wpfx, wpl);
+ Cline mli, mlil;
+
+ /* We also have a suffix to match, so first save the
+ * contents of the global matching variables. */
+ memcpy(wpfx, matchbuf, wpl);
+ if (matchsubs) {
+ Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+
+ tmp->prefix = matchsubs;
+ if (matchlastpart)
+ matchlastpart->next = tmp;
+ else
+ matchparts = tmp;
+ }
+ pli = matchparts;
+ plil = matchlastpart;
+
+ /* The try to match the suffix. */
+
+ if ((msl = match_str(sfx, w + mpl, bsl, bcs, &rsl, 1, 0, 0)) < 0) {
+ free_cline(pli);
+
+ return NULL;
+ }
+ /* Matched, so add the string in the middle and the saved
+ * string for the prefix, and build a combined cline list
+ * for the prefix and the suffix. */
+ if (matchsubs) {
+ Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
+
+ tmp->suffix = matchsubs;
+ if (matchlastpart)
+ matchlastpart->next = tmp;
+ else
+ matchparts = tmp;
+ }
+ add_match_str(NULL, NULL, w + rpl, wl - rpl - rsl, 1);
+ add_match_str(NULL, NULL, wpfx, wpl, 1);
+
+ mli = bld_parts(w + rpl, wl - rpl - rsl,
+ (mpl - rpl) + (msl - rsl), &mlil);
+ mlil->flags |= CLF_MID;
+ mlil->slen = msl - rsl;
+ mlil->next = revert_cline(matchparts);
+
+ if (plil)
+ plil->next = mli;
+ else
+ pli = mli;
+ } else {
+ /* Only a prefix, add the string and a part-cline for it. */
+ add_match_str(NULL, NULL, w + rpl, wl - rpl, 0);
+
+ add_match_part(NULL, NULL, NULL, 0, NULL, 0, w + rpl, wl - rpl,
+ mpl - rpl, 0);
+ pli = matchparts;
+ }
+ r = dupstring(matchbuf ? matchbuf : "");
+
+ *clp = pli;
+
+ /* Test if the string built is equal to the one from the line. */
+ if (sfx && *sfx) {
+ int pl = strlen(pfx);
+
+ *exact = (!strncmp(pfx, w, pl) && !strcmp(sfx, w + pl));
+ } else
+ *exact = !strcmp(pfx, w);
+ }
+ if (!qu)
+ hasunqu = 1;
+
+ return r;
+}
+
+/* Check if the given pattern matches the given string. *
+ * `in' and `out' are used for {...} classes. In `out' we store the *
+ * character number that was matched. In the word pattern this is *
+ * given in `in' so that we can easily test if we found the *
+ * corresponding character. */
+
+/**/
+static int
+pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
+{
+ unsigned char c;
+
+ while (p) {
+ c = *((unsigned char *) s);
+
+ if (out)
+ *out = 0;
+
+ if (p->equiv) {
+ if (in) {
+ c = p->tab[c];
+ if ((*in && *in != c) || (!*in && !c))
+ return 0;
+ } else if (out) {
+ if (!(*out = p->tab[c]))
+ return 0;
+ } else if (!p->tab[c])
+ return 0;
+
+ if (in && *in)
+ in++;
+ if (out)
+ out++;
+ } else if (!p->tab[c])
+ return 0;
+
+ s++;
+ p = p->next;
+ }
+ return 1;
+}
+
+/* This splits the given string into a list of cline structs, separated
+ * at those places where one of the anchors of an `*' pattern was found.
+ * plen gives the number of characters on the line that matched this
+ * string. In lp we return a pointer to the last cline struct we build. */
+
+/**/
+Cline
+bld_parts(char *str, int len, int plen, Cline *lp)
+{
+ Cline ret = NULL, *q = &ret, n;
+ Cmlist ms;
+ Cmatcher mp;
+ int t, op = plen;
+ char *p = str;
+
+ while (len) {
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
+ !mp->llen && len >= mp->ralen && mp->ralen &&
+ pattern_match(mp->right, str, NULL, NULL)) {
+ int olen = str - p, llen;
+
+ /* We found an anchor, create a new cline. The NEW flag
+ * is set if the characters before the anchor were not
+ * on the line. */
+ *q = n = get_cline(NULL, mp->ralen, str, mp->ralen, NULL, 0,
+ ((plen < 0) ? CLF_NEW : 0));
+
+ /* If there were any characters before the anchor, add
+ * them as a cline struct. */
+
+ if (p != str) {
+ llen = (op < 0 ? 0 : op);
+
+ if (llen > olen)
+ llen = olen;
+ n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
+ }
+ q = &(n->next);
+ str += mp->ralen; len -= mp->ralen;
+ plen -= mp->ralen;
+ op -= olen;
+ p = str;
+ t = 1;
+ }
+ }
+ if (!t) {
+ /* No anchor was found here, skip. */
+ str++; len--;
+ plen--;
+ }
+ }
+ /* This is the cline struct for the remaining string at the end. */
+
+ *q = n = get_cline(NULL, 0, NULL, 0, NULL, 0, (plen < 0 ? CLF_NEW : 0));
+ if (p != str) {
+ int olen = str - p, llen = (op < 0 ? 0 : op);
+
+ if (llen > olen)
+ llen = olen;
+ n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
+ }
+ n->next = NULL;
+
+ if (lp)
+ *lp = n;
+
+ return ret;
+}
+
+/* This builds all the possible line patterns for the pattern pat in the
+ * buffer line. Initially line is the same as lp, but during recursive
+ * calls lp is incremented for storing successive characters. Whenever
+ * a full possible string is build, we test if this line matches the
+ * string given by wlen and word. The in argument contains the characters
+ * to use for the correspondence classes, it was filled by a call to
+ * pattern_match() in the calling function.
+ * The return value is the length of the string matched in the word, it
+ * is zero if we couldn't build a line that matches the word. */
+
+/**/
+static int
+bld_line(Cpattern pat, char *line, char *lp,
+ char *word, int wlen, unsigned char *in, int sfx)
+{
+ if (pat) {
+ /* Still working on the pattern. */
+
+ int i, l;
+ unsigned char c = 0;
+
+ /* Get the number of the character for a correspondence class
+ * if it has a correxponding class. */
+ if (pat->equiv)
+ if ((c = *in))
+ in++;
+
+ /* Walk through the table in the pattern and try the characters
+ * that may appear in the current position. */
+ for (i = 0; i < 256; i++)
+ if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) {
+ *lp = i;
+ /* We stored the character, now call ourselves to build
+ * the rest. */
+ if ((l = bld_line(pat->next, line, lp + 1, word, wlen,
+ in, sfx)))
+ return l;
+ }
+ } else {
+ /* We reached the end, i.e. the line string is fully build, now
+ * see if it matches the given word. */
+
+ Cmlist ms;
+ Cmatcher mp;
+ int l = lp - line, t, rl = 0, ind, add;
+ VARARR(unsigned char, ea, l + 1);
+
+ /* Quick test if the strings are exactly the same. */
+ if (l == wlen && !strncmp(line, word, l))
+ return l;
+
+ if (sfx) {
+ line = lp; word += wlen;
+ ind = -1; add = -1;
+ } else {
+ ind = 0; add = 1;
+ }
+ /* We loop through the whole line string built. */
+ while (l && wlen) {
+ if (word[ind] == line[ind]) {
+ /* The same character in both strings, skip over. */
+ line += add; word += add;
+ l--; wlen--; rl++;
+ } else {
+ t = 0;
+ for (ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
+ pattern_match(mp->line, (sfx ? line - mp->llen : line),
+ NULL, ea) &&
+ pattern_match(mp->word, (sfx ? word - mp->wlen : word),
+ ea, NULL)) {
+ /* Both the line and the word pattern matched,
+ * now skip over the matched portions. */
+ if (sfx) {
+ line -= mp->llen; word -= mp->wlen;
+ } else {
+ line += mp->llen; word += mp->wlen;
+ }
+ l -= mp->llen; wlen -= mp->wlen; rl += mp->wlen;
+ t = 1;
+ }
+ }
+ if (!t)
+ /* Didn't match, give up. */
+ return 0;
+ }
+ }
+ if (!l)
+ /* Unmatched portion in the line built, return matched length. */
+ return rl;
+ }
+ return 0;
+}
+
+/* This builds a string that may be put on the line that fully matches the
+ * given strings. The return value is NULL if no such string could be built
+ * or that string in local static memory, dup it. */
+
+/**/
+static char *
+join_strs(int la, char *sa, int lb, char *sb)
+{
+ static char *rs = NULL;
+ static int rl = 0;
+
+ VARARR(unsigned char, ea, (la > lb ? la : lb) + 1);
+ Cmlist ms;
+ Cmatcher mp;
+ int t, bl, rr = rl;
+ char *rp = rs;
+
+ while (la && lb) {
+ if (*sa != *sb) {
+ /* Different characters, try the matchers. */
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+ mp->wlen <= la && mp->wlen <= lb) {
+ /* The pattern has no anchors and the word
+ * pattern fits, try it. */
+ if ((t = pattern_match(mp->word, sa, NULL, ea)) ||
+ pattern_match(mp->word, sb, NULL, ea)) {
+ /* It matched one of the strings, t says which one. */
+ VARARR(char, line, mp->llen + 1);
+ char **ap, **bp;
+ int *alp, *blp;
+
+ if (t) {
+ ap = &sa; alp = &la;
+ bp = &sb; blp = &lb;
+ } else {
+ ap = &sb; alp = &lb;
+ bp = &sa; blp = &la;
+ }
+ /* Now try to build a string that matches the other
+ * string. */
+ if ((bl = bld_line(mp->line, line, line,
+ *bp, *blp, ea, 0))) {
+ /* Found one, put it into the return string. */
+ line[mp->llen] = '\0';
+ if (rr <= mp->llen) {
+ char *or = rs;
+
+ rs = realloc(rs, (rl += 20));
+ rr += 20;
+ rp += rs - or;
+ }
+ memcpy(rp, line, mp->llen);
+ rp += mp->llen; rr -= mp->llen;
+ *ap += mp->wlen; *alp -= mp->wlen;
+ *bp += bl; *blp -= bl;
+ t = 1;
+ } else
+ t = 0;
+ }
+ }
+ }
+ if (!t)
+ break;
+ } else {
+ /* Same character, just take it. */
+ if (rr <= 1) {
+ char *or = rs;
+
+ rs = realloc(rs, (rl += 20));
+ rr += 20;
+ rp += rs - or;
+ }
+ *rp++ = *sa; rr--;
+ sa++; sb++;
+ la--; lb--;
+ }
+ }
+ if (la || lb)
+ return NULL;
+
+ *rp = '\0';
+
+ return rs;
+}
+
+/* This compares the anchors stored in two top-level clines. */
+
+/**/
+static int
+cmp_anchors(Cline o, Cline n, int join)
+{
+ int line = 0;
+ char *j;
+
+ /* First try the exact strings. */
+ if ((!(o->flags & CLF_LINE) && o->wlen == n->wlen &&
+ (!o->word || !strncmp(o->word, n->word, o->wlen))) ||
+ (line = ((!o->line && !n->line && !o->wlen && !n->wlen) ||
+ (o->llen == n->llen && o->line && n->line &&
+ !strncmp(o->line, n->line, o->llen))))) {
+ if (line) {
+ o->flags |= CLF_LINE;
+ o->word = NULL;
+ n->wlen = 0;
+ }
+ return 1;
+ }
+ /* Didn't work, try to build a string matching both anchors. */
+ if (join && !(o->flags & CLF_JOIN) && o->word && n->word &&
+ (j = join_strs(o->wlen, o->word, n->wlen, n->word))) {
+ o->flags |= CLF_JOIN;
+ o->wlen = strlen(j);
+ o->word = dupstring(j);
+
+ return 2;
+ }
+ return 0;
+}
+
+/* Below is the code to join two cline lists. This struct is used to walk
+ * through a sub-list. */
+
+typedef struct cmdata *Cmdata;
+
+struct cmdata {
+ Cline cl, pcl;
+ char *str, *astr;
+ int len, alen, olen, line;
+};
+
+/* This is used to ensure that a cmdata struct contains usable data.
+ * The return value is non-zero if we reached the end. */
+
+static int
+check_cmdata(Cmdata md, int sfx)
+{
+ /* We will use the str and len fields to contain the next sub-string
+ * in the list. If len is zero, we have to use the next cline. */
+ if (!md->len) {
+ /* If there is none, we reached the end. */
+ if (!md->cl)
+ return 1;
+
+ /* Otherwise, get the string. Only the line-string or both.
+ * We also have to adjust the pointer if this is for a suffix. */
+ if (md->cl->flags & CLF_LINE) {
+ md->line = 1;
+ md->len = md->cl->llen;
+ md->str = md->cl->line;
+ } else {
+ md->line = 0;
+ md->len = md->olen = md->cl->wlen;
+ if ((md->str = md->cl->word) && sfx)
+ md->str += md->len;
+ md->alen = md->cl->llen;
+ if ((md->astr = md->cl->line) && sfx)
+ md->astr += md->alen;
+ }
+ md->pcl = md->cl;
+ md->cl = md->cl->next;
+ }
+ return 0;
+}
+
+/* This puts the not-yet-matched portion back into the last cline and
+ * returns that. */
+
+static Cline
+undo_cmdata(Cmdata md, int sfx)
+{
+ Cline r = md->pcl;
+
+ if (md->line) {
+ r->word = NULL;
+ r->wlen = 0;
+ r->flags |= CLF_LINE;
+ r->llen = md->len;
+ r->line = md->str - (sfx ? md->len : 0);
+ } else if (md->len != md->olen) {
+ r->wlen = md->len;
+ r->word = md->str - (sfx ? md->len : 0);
+ }
+ return r;
+}
+
+/* This tries to build a string matching a sub-string in a sub-cline
+ * that could not be matched otherwise. */
+
+static Cline
+join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join)
+{
+ if (!check_cmdata(md, sfx)) {
+ char *ow = str, *nw = md->str;
+ int ol = len, nl = md->len;
+ Cmlist ms;
+ Cmatcher mp;
+ VARARR(unsigned char, ea, (ol > nl ? ol : nl) + 1);
+ int t;
+
+ if (sfx) {
+ ow += ol; nw += nl;
+ }
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ /* We use only those patterns that match a non-empty
+ * string in both the line and the word and that have
+ * no anchors. */
+ if (!mp->flags && mp->wlen > 0 && mp->llen > 0) {
+ /* We first test, if the old string matches already the
+ * new one. */
+ if (mp->llen <= ol && mp->wlen <= nl &&
+ pattern_match(mp->line, ow - (sfx ? mp->llen : 0),
+ NULL, ea) &&
+ pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
+ ea, NULL)) {
+ /* It did, update the contents of the cmdata struct
+ * and return a cline for the matched part. */
+ if (sfx)
+ md->str -= mp->wlen;
+ else
+ md->str += mp->wlen;
+ md->len -= mp->wlen;
+ *mlen = mp->llen;
+
+ return get_cline(NULL, 0, ow - (sfx ? mp->llen : 0),
+ mp->llen, NULL, 0, 0);
+ }
+ /* Otherwise we will try to build a string that matches
+ * both strings. But try the pattern only if the word-
+ * pattern matches one of the strings. */
+ if (join && mp->wlen <= ol && mp->wlen <= nl &&
+ ((t = pattern_match(mp->word, ow - (sfx ? mp->wlen : 0),
+ NULL, ea)) ||
+ pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
+ NULL, ea))) {
+ VARARR(char, line, mp->llen + 1);
+ int bl;
+
+ /* Then build all the possible lines and see
+ * if one of them matches the other string. */
+ if ((bl = bld_line(mp->line, line, line,
+ (t ? nw : ow), (t ? nl : ol),
+ ea, sfx))) {
+ /* Yep, one of the lines matched the other
+ * string. */
+ line[mp->llen] = '\0';
+
+ if (t) {
+ ol = mp->wlen; nl = bl;
+ } else {
+ ol = bl; nl = mp->wlen;
+ }
+ if (sfx)
+ md->str -= nl;
+ else
+ md->str += nl;
+ md->len -= nl;
+ *mlen = ol;
+
+ return get_cline(NULL, 0, dupstring(line), mp->llen,
+ NULL, 0, CLF_JOIN);
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/* This is used to match a sub-string in a sub-cline. The length of the
+ * matched portion is returned. This tests only for exact equality. */
+
+static int
+sub_match(Cmdata md, char *str, int len, int sfx)
+{
+ int ret = 0, l, ind, add;
+ char *p, *q;
+
+ if (sfx) {
+ str += len;
+ ind = -1; add = -1;
+ } else {
+ ind = 0; add = 1;
+ }
+ /* str and len describe the old string, in md we have the new one. */
+ while (len) {
+ if (check_cmdata(md, sfx))
+ return ret;
+
+ for (l = 0, p = str, q = md->str;
+ l < len && l < md->len && p[ind] == q[ind];
+ l++, p += add, q += add);
+
+ if (l) {
+ /* There was a common prefix, use it. */
+ md->len -= l; len -= l;
+ if (sfx) {
+ md->str -= l; str -= l;
+ } else {
+ md->str += l; str += l;
+ }
+ ret += l;
+ } else if (md->line || md->len != md->olen || !md->astr)
+ return ret;
+ else {
+ /* We still have the line string to try. */
+ md->line = 1;
+ md->len = md->alen;
+ md->str = md->astr;
+ }
+ }
+ return ret;
+}
+
+/* This is used to build a common prefix or suffix sub-list. If requested
+ * it returns the unmatched cline lists in orest and nrest. */
+
+/**/
+static void
+join_psfx(Cline ot, Cline nt, Cline *orest, Cline *nrest, int sfx)
+{
+ Cline p = NULL, o, n;
+ struct cmdata md, omd;
+ char **sstr = NULL;
+ int len, join = 0, line = 0, *slen = NULL;
+
+ if (sfx) {
+ o = ot->suffix; n = nt->suffix;
+ } else {
+ o = ot->prefix; n = nt->prefix;
+ }
+ if (!o) {
+ if (orest)
+ *orest = NULL;
+ if (nrest)
+ *nrest = n;
+
+ return;
+ }
+ if (!n) {
+ if (sfx)
+ ot->suffix = NULL;
+ else
+ ot->prefix = NULL;
+
+ if (orest)
+ *orest = o;
+ else
+ free_cline(o);
+ if (nrest)
+ *nrest = NULL;
+ return;
+ }
+ md.cl = n;
+ md.len = 0;
+
+ /* Walk through the old list. */
+ while (o) {
+ join = 0;
+ memcpy(&omd, &md, sizeof(struct cmdata));
+
+ /* We first get the length of the prefix equal in both strings. */
+ if (o->flags & CLF_LINE) {
+ if ((len = sub_match(&md, o->line, o->llen, sfx)) != o->llen) {
+ join = 1; line = 1; slen = &(o->llen); sstr = &(o->line);
+ }
+ } else if ((len = sub_match(&md, o->word, o->wlen, sfx)) != o->wlen) {
+ if (o->line) {
+ memcpy(&md, &omd, sizeof(struct cmdata));
+ o->flags |= CLF_LINE | CLF_DIFF;
+
+ continue;
+ }
+ join = 1; line = 0; slen = &(o->wlen); sstr = &(o->word);
+ }
+ if (join) {
+ /* There is a rest that is different in the two lists,
+ * we try to build a new cline matching both strings. */
+ Cline joinl;
+ int jlen;
+
+ if ((joinl = join_sub(&md, *sstr + len, *slen - len,
+ &jlen, sfx, !(o->flags & CLF_JOIN)))) {
+ /* We have one, insert it into the list. */
+ joinl->flags |= CLF_DIFF;
+ if (len + jlen != *slen) {
+ Cline rest;
+
+ rest = get_cline(NULL, 0, *sstr + (sfx ? 0 : len + jlen),
+ *slen - len - jlen, NULL, 0, 0);
+
+ rest->next = o->next;
+ joinl->next = rest;
+ } else
+ joinl->next = o->next;
+
+ if (len) {
+ if (sfx)
+ *sstr += *slen - len;
+ *slen = len;
+ o->next = joinl;
+ } else {
+ o->next = NULL;
+ free_cline(o);
+ if (p)
+ p->next = joinl;
+ else if (sfx)
+ ot->suffix = joinl;
+ else
+ ot->prefix = joinl;
+ }
+ o = joinl;
+ join = 0;
+ }
+ }
+ if (join) {
+ /* We couldn't build a cline for a common string, so we
+ * cut the list here. */
+ if (len) {
+ Cline r;
+
+ if (orest) {
+ if (line)
+ r = get_cline(o->line + len, *slen - len,
+ NULL, 0, NULL, 0, o->flags);
+ else
+ r = get_cline(NULL, 0, o->word + len, *slen - len,
+ NULL, 0, o->flags);
+
+ r->next = o->next;
+ *orest = r;
+
+ *slen = len;
+ o->next = NULL;
+ } else {
+ if (sfx)
+ *sstr += *slen - len;
+ *slen = len;
+ free_cline(o->next);
+ o->next = NULL;
+ }
+ } else {
+ if (p)
+ p->next = NULL;
+ else if (sfx)
+ ot->suffix = NULL;
+ else
+ ot->prefix = NULL;
+
+ if (orest)
+ *orest = o;
+ else
+ free_cline(o);
+ }
+ if (!orest || !nrest)
+ ot->flags |= CLF_MISS;
+
+ if (nrest)
+ *nrest = undo_cmdata(&md, sfx);
+
+ return;
+ }
+ p = o;
+ o = o->next;
+ }
+ if (md.len || md.cl)
+ ot->flags |= CLF_MISS;
+ if (orest)
+ *orest = NULL;
+ if (nrest)
+ *nrest = undo_cmdata(&md, sfx);
+}
+
+/* This builds the common prefix and suffix for a mid-cline -- the one
+ * describing the place where the prefix and the suffix meet. */
+
+/**/
+static void
+join_mid(Cline o, Cline n)
+{
+ if (o->flags & CLF_JOIN) {
+ /* The JOIN flag is set in the old cline struct if it was
+ * already joined with another one. In this case the suffix
+ * field contains the suffix from previous calls. */
+ Cline nr;
+
+ join_psfx(o, n, NULL, &nr, 0);
+
+ n->suffix = revert_cline(nr);
+
+ join_psfx(o, n, NULL, NULL, 1);
+ } else {
+ /* This is the first time for both structs, so the prefix field
+ * contains the whole sub-list. */
+ Cline or, nr;
+
+ o->flags |= CLF_JOIN;
+
+ /* We let us give both rests and use them as the suffixes. */
+ join_psfx(o, n, &or, &nr, 0);
+
+ if (or)
+ or->llen = (o->slen > or->wlen ? or->wlen : o->slen);
+ o->suffix = revert_cline(or);
+ n->suffix = revert_cline(nr);
+
+ join_psfx(o, n, NULL, NULL, 1);
+ }
+ n->suffix = NULL;
+}
+
+/* This turns the sequence of anchor cline structs from b to e into a
+ * prefix sequence, puts it before the prefix of e and then tries to
+ * join that with the prefix of a.
+ * This is needed if some matches had a anchor match spec and others
+ * didn't. */
+
+/**/
+static void
+sub_join(Cline a, Cline b, Cline e, int anew)
+{
+ if (!e->suffix && a->prefix) {
+ Cline op = e->prefix, n = NULL, *p = &n, t, ca;
+ int min = 0, max = 0;
+
+ for (; b != e; b = b->next) {
+ if ((*p = t = b->prefix)) {
+ while (t->next)
+ t = t->next;
+ p = &(t->next);
+ }
+ b->suffix = b->prefix = NULL;
+ b->flags &= ~CLF_SUF;
+ min += b->min;
+ max += b->max;
+ *p = b;
+ p = &(b->next);
+ }
+ *p = e->prefix;
+ ca = a->prefix;
+
+ while (n != op) {
+ e->prefix = cp_cline(n, 0);
+ a->prefix = cp_cline(ca, 0);
+
+ if (anew) {
+ join_psfx(e, a, NULL, NULL, 0);
+ if (e->prefix) {
+ e->min += min;
+ e->max += max;
+ break;
+ }
+ } else {
+ join_psfx(e, a, NULL, NULL, 0);
+ if (a->prefix) {
+ a->min += min;
+ a->max += max;
+ break;
+ }
+ }
+ min -= n->min;
+ max -= n->max;
+
+ n = n->next;
+ }
+ }
+}
+
+/* This simplifies the cline list given as the first argument so that
+ * it also matches the second list. */
+
+/**/
+Cline
+join_clines(Cline o, Cline n)
+{
+ cline_setlens(n, 1);
+
+ /* First time called, just return the new list. On further invocations
+ * we will get it as the first argument. */
+ if (!o)
+ return n;
+ else {
+ Cline oo = o, nn = n, po = NULL, pn = NULL;
+
+ /* Walk through the lists. */
+ while (o && n) {
+ /* If one of them describes a new part and the other one does
+ * not, synchronise them by searching an old part in the
+ * other list. */
+ if ((o->flags & CLF_NEW) && !(n->flags & CLF_NEW)) {
+ Cline t, tn;
+
+ for (t = o; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
+ if (tn && cmp_anchors(tn, n, 0)) {
+ sub_join(n, o, tn, 1);
+
+ if (po)
+ po->next = tn;
+ else
+ oo = tn;
+ t->next = NULL;
+ free_cline(o);
+ o = tn;
+ o->flags |= CLF_MISS;
+ continue;
+ }
+ }
+ if (!(o->flags & CLF_NEW) && (n->flags & CLF_NEW)) {
+ Cline t, tn;
+
+ for (t = n; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
+ if (tn && cmp_anchors(o, tn, 0)) {
+ sub_join(o, n, tn, 0);
+
+ n = tn;
+ o->flags |= CLF_MISS;
+ continue;
+ }
+ }
+ /* Almost the same as above, but for the case that they
+ * describe different types of parts (prefix, suffix, or mid). */
+ if ((o->flags & (CLF_SUF | CLF_MID)) !=
+ (n->flags & (CLF_SUF | CLF_MID))) {
+ Cline t, tn;
+
+ for (t = n;
+ (tn = t->next) &&
+ (tn->flags & (CLF_SUF | CLF_MID)) !=
+ (o->flags & (CLF_SUF | CLF_MID));
+ t = tn);
+ if (tn && cmp_anchors(o, tn, 1)) {
+ sub_join(o, n, tn, 0);
+
+ n = tn;
+ continue;
+ }
+ for (t = o;
+ (tn = t->next) &&
+ (tn->flags & (CLF_SUF | CLF_MID)) !=
+ (n->flags & (CLF_SUF | CLF_MID));
+ t = tn);
+ if (tn && cmp_anchors(tn, n, 1)) {
+ sub_join(n, o, tn, 1);
+ if (po)
+ po->next = tn;
+ else
+ oo = tn;
+ t->next = NULL;
+ free_cline(o);
+ o = tn;
+ continue;
+ }
+ if (o->flags & CLF_MID) {
+ o->flags = (o->flags & ~CLF_MID) | (n->flags & CLF_SUF);
+ if (n->flags & CLF_SUF) {
+ free_cline(o->prefix);
+ o->prefix = NULL;
+ } else {
+ free_cline(o->suffix);
+ o->suffix = NULL;
+ }
+ }
+ break;
+ }
+ /* Now see if they have matching anchors. If not, cut the list. */
+ if (!(o->flags & CLF_MID) && !cmp_anchors(o, n, 1)) {
+ Cline t, tn;
+
+ for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn);
+
+ if (tn) {
+ sub_join(o, n, tn, 0);
+
+ n = tn;
+ o->flags |= CLF_MISS;
+ continue;
+ } else {
+ for (t = o; (tn = t->next) && !cmp_anchors(n, tn, 1);
+ t = tn);
+
+ if (tn) {
+ sub_join(n, o, tn, 1);
+
+ if (po)
+ po->next = tn;
+ else
+ oo = tn;
+ o = tn;
+ o->flags |= CLF_MISS;
+ continue;
+ } else {
+ if (o->flags & CLF_SUF)
+ break;
+
+ o->word = o->line = o->orig = NULL;
+ o->wlen = 0;
+ free_cline(o->next);
+ o->next = NULL;
+ o->flags |= CLF_MISS;
+ }
+ }
+ }
+ /* Ok, they are equal, now copy the information about the
+ * original string if needed, calculate minimum and maximum
+ * lengths, and join the sub-lists. */
+ if (!o->orig && !o->olen) {
+ o->orig = n->orig;
+ o->olen = n->olen;
+ }
+ if (n->min < o->min)
+ o->min = n->min;
+ if (n->max > o->max)
+ o->max = n->max;
+ if (o->flags & CLF_MID)
+ join_mid(o, n);
+ else
+ join_psfx(o, n, NULL, NULL, (o->flags & CLF_SUF));
+
+ po = o;
+ o = o->next;
+ pn = n;
+ n = n->next;
+ }
+ /* Free the rest of the old list. */
+ if (o) {
+ if (po)
+ po->next = NULL;
+ else
+ oo = NULL;
+
+ free_cline(o);
+ }
+ free_cline(nn);
+
+ return oo;
+ }
+}
+