/*** analog 4.13 http://www.analog.cx/ ***/ /*** This program is copyright (c) Stephen R. E. Turner 1995 - 2000 except as *** stated otherwise. Distribution, usage and modification of this program is *** subject to the conditions of the Licence which you should have received *** with it. This program comes with no warranty, expressed or implied. ***/ #include "anlghea3.h" /*** tree.c; functions for tree construction and processing. ***/ /* first, treefind(), analogous to hashfind() */ /* NB Don't really need as much data as Hashindex *, but allows us to use previous sort routines etc. */ Hashindex *treefind(char *name, char *nameend, Hashtable **tree, Hashindex *item, cutfnp cutfn, logical build, logical transient, logical reuse, Memman *space) { Hashindex *lp, *lastlp, *newone; /* not called new because of C++ */ unsigned long magic; if (*tree == NULL && !build) return(NULL); else if (*tree == NULL) *tree = rehash(NULL, TREEHASHSIZE, space); else if (TOO_FULL_TREE((*tree)->n, (*tree)->size)) *tree = rehash(*tree, NEW_SIZE_TREE((*tree)->size), space); if (build && (strchr(name, '*') != NULL || strchr(name, '?') != NULL)) magic = 0; else MAGICNOTREE(magic, name, nameend, (*tree)->size); lp = (*tree)->head[magic]; lastlp = NULL; while (TRUE) { if (lp == NULL) { /* not found */ if (build) { newone = newtreeentry(name, nameend, item, transient, reuse, space); if (lastlp == NULL) (*tree)->head[magic] = newone; else lastlp->next = newone; ((*tree)->n)++; if (cutfn != NULL) { cutfn(&name, &nameend, item->name, build); if (name != NULL) (void)treefind(name, nameend, (Hashtable **)&(newone->other), item, cutfn, build, transient, reuse, space); } } else { /* !build */ for (newone = NULL, lp = (*tree)->head[0]; lp != NULL; TO_NEXT(lp)) { if (genwildmatch(name, nameend, lp->name)) { if (newone == NULL) { newone = newtreeentry(name, nameend, item, transient, reuse, space); if (lastlp == NULL) (*tree)->head[magic] = newone; else lastlp->next = newone; ((*tree)->n)++; } graft((Hashtable **)&(newone->other), (Hashtable *)(lp->other), space); } } if (newone != NULL) { if (cutfn != NULL) { cutfn(&name, &nameend, item->name, build); if (name != NULL) (void)treefind(name, nameend, (Hashtable **)&(newone->other), item, cutfn, build, transient, reuse, space); } } } return(newone); } else if (genstreq(name, nameend, lp->name)) { /* found it */ if (IS_REUSED(lp->own)) lp->own = newtreedata(lp->own, space); treescore(lp->own, item->own); if (cutfn != NULL) { cutfn(&name, &nameend, item->name, build); if (name != NULL) (void)treefind(name, nameend, (Hashtable **)&(lp->other), item, cutfn, build, transient, reuse, space); } return(lp); } else { lastlp = lp; TO_NEXT(lp); } } } void graft(Hashtable **newone, Hashtable *old, Memman *space) { Hashindex *lp, *found; unsigned long magic; if (old != NULL) { for (magic = 0; magic < old->size; magic++) { for (lp = old->head[magic]; lp != NULL; TO_NEXT(lp)) { found = treefind(lp->name, strchr(lp->name, '\0'), newone, lp, NULL, TRUE, FALSE, FALSE, space); graft((Hashtable **)&(found->other), (Hashtable *)(lp->other), space); } } } } void allgraft(Hashtable *t, Memman *space) { Hashindex *lp, *lp0; unsigned long magic; if (t != NULL) { for (magic = 1; magic < t->size; magic++) { for (lp = t->head[magic]; lp != NULL; TO_NEXT(lp)) { for (lp0 = t->head[0]; lp0 != NULL; TO_NEXT(lp0)) { if (MATCHES(lp->name, lp0->name)) { graft((Hashtable **)&(lp->other), (Hashtable *)(lp0->other), space); } } allgraft((Hashtable *)(lp->other), space); } } } } Hashindex *newtreeentry(char *name, char *nameend, Hashindex *item, logical transient, logical reuse, Memman *space) { Hashindex *ans = (Hashindex *)submalloc(space, sizeof(Hashindex)); if (*nameend == '\0' && !transient) ans->name = name; else { ans->name = (char *)submalloc(space, (size_t)(nameend - name + 1)); memcpy((void *)(ans->name), (void *)name, (size_t)(nameend - name)); ans->name[(size_t)(nameend - name)] = '\0'; } if (reuse) ans->own = item->own; /* will get newtreedata if hit a 2nd time */ else ans->own = newtreedata(item->own, space); ans->other = NULL; ans->next = NULL; return(ans); } Hashentry *newtreedata(Hashentry *from, Memman *space) { Hashentry *ans = (Hashentry *)submalloc(space, sizeof(Hashentry)); int i; for (i = 0; i < DATA_NUMBER; i++) ans->data[i] = 0; ans->bytes = 0.0; ans->ispage = from->ispage; /* this is good enough */ ans->ispage += 16; /* to indicate not reused */ treescore(ans, from); return(ans); } void treescore(Hashentry *to, Hashentry *from) { int i; for (i = 0; i < COUNT_NUMBER; i++) to->data[i] += from->data[i]; for ( ; i < DATA_NUMBER; i++) to->data[i] = MAX(to->data[i], from->data[i]); to->bytes += from->bytes; } Hashindex *sorttree(Hashtable *tree, choice rep, Floor *floor, choice sortby, Floor *subfloor, choice subsortby, logical alphaback, unsigned int level, Strlist *partname, Include *wanthead, Alias *notcorrupt, choice requests, choice date, unsigned long totr, unsigned long totp, double totb, unsigned long maxr, unsigned long maxp, double maxb, Hashentry **badp, unsigned long *badn, Memman *space) { Hashindex *gooditems, *baditems, *p, *np; Strlist *pn, sp; size_t need = (size_t)level + 3; Alias *ap; unsigned long i; logical ok; unhash(tree, &gooditems, &baditems); if (notcorrupt != NULL) { /* NB notcorrupt only used for doms */ baditems = (Hashindex *)submalloc(space, sizeof(Hashindex)); baditems->name = "\v"; baditems->own = (Hashentry *)submalloc(space, sizeof(Hashentry)); for (i = 0; i < DATA_NUMBER; i++) baditems->own->data[i] = 0; baditems->own->bytes = 0.0; baditems->own->ispage = FALSE; baditems->other = NULL; baditems->next = NULL; for (p = gooditems, np = NULL; p != NULL; TO_NEXT(p)) { for (ok = FALSE, ap = notcorrupt; !ok && ap != NULL; TO_NEXT(ap)) { if (STREQ(p->name, ap->from)) ok = TRUE; } if (ok) np = p; else { /* erase p from list */ if (strchr(p->name, '*') == NULL && strchr(p->name, '?') == NULL) debug('U', "%s", p->name); treescore(baditems->own, p->own); if (np == NULL) gooditems = p->next; else np->next = p->next; } } } /* end notcorrupt != NULL */ for (i = 0; i < tree->size; i++) tree->head[i] = NULL; for (pn = partname; pn != NULL; TO_NEXT(pn)) need += strlen(pn->name); my_sort(&gooditems, &baditems, partname, &pn, &sp, need, rep, floor, sortby, alphaback, wanthead, requests, date, totr, totp, totb, maxr, maxp, maxb); if (badp != NULL) { *badp = newhashentry(FALSE); *badn = 0; for (p = baditems; p != NULL; TO_NEXT(p)) { if (p->own != NULL && p->own->data[requests] > 0) { (*badp)->data[requests] += p->own->data[requests]; (*badp)->data[PAGES] += p->own->data[PAGES]; (*badp)->bytes += p->own->bytes; (*badp)->data[date] = MAX((*badp)->data[date], p->own->data[date]); (*badn)++; } } } for (p = gooditems; p != NULL; TO_NEXT(p)) { if (p->other != NULL) { (void)maketreename(partname, p, &pn, &sp, need, rep, FALSE); ((Hashtable *)(p->other))->head[0] = sorttree((Hashtable *)(p->other), rep, subfloor, subsortby, subfloor, subsortby, alphaback, level + 1, pn, wanthead, NULL, requests, date, totr, totp, totb, maxr, maxp, maxb, NULL, NULL, space); } } return(gooditems); } void maketree(Tree *treex, Hashindex *gooditems, Hashindex *baditems) { Hashindex *p; char *name, *nameend; for (p = gooditems; p != NULL; TO_NEXT(p)) { if (p->own != NULL) { name = NULL; treex->cutfn(&name, &nameend, p->name, FALSE); (void)treefind(name, nameend, &(treex->tree), p, treex->cutfn, FALSE, FALSE, TRUE, treex->space); } } for (p = baditems; p != NULL; TO_NEXT(p)) { if (p->own != NULL) { name = NULL; treex->cutfn(&name, &nameend, p->name, FALSE); (void)treefind(name, nameend, &(treex->tree), p, treex->cutfn, FALSE, FALSE, TRUE, treex->space); } } } void makederived(Derv *derv, Hashindex *gooditems, Hashindex *baditems, unsigned char convfloor, choice rep) { extern Hashentry *blank_entry; Hashindex *p; Hashentry *f; char *name, *nameend; size_t len; logical donegood; for (p = gooditems, donegood = FALSE; p != NULL || !donegood; ) { if (p == NULL) { donegood = TRUE; p = baditems; } else { if (p->own != NULL) { name = NULL; for (derv->cutfn(&name, &nameend, p->name, *(derv->list)); name != NULL; derv->cutfn(&name, &nameend, p->name, *(derv->list))) { len = nameend - name; memcpy(submalloc(derv->space, len + 1), (void *)name, len); *((char *)(derv->space->next_pos) - 1) = '\0'; /* = curr_pos + len */ f = (Hashentry *)(hashfind(derv->space, &(derv->table), NULL, UNSET, NULL, NULL, "", 0, FALSE, convfloor, rep, FALSE)->other); if (!ENTRY_BLANK(f)) treescore(f, p->own); } } TO_NEXT(p); } } } char *maketreename(Strlist *pn, Hashindex *p, Strlist **newpn, Strlist *space, size_t need, choice rep, logical delims) { /* Compile name from strlist. We end up doing the most-significant portion several times, but it really is the best way, at least if the name is little-endian, otherwise we have to try and work out which portion we want when we go back down a level. */ static char *name = NULL; static size_t len = 0; logical back; char glue; char *t; Strlist *sp; size_t l; if (rep == REP_OS) /* just use last component of name */ return(p->name); if (rep == REP_TYPE || rep == REP_DOM || rep == REP_ORG) { back = !(rep == REP_DOM && pn != NULL && STREQ(pn->name, "\f")); /* back = TRUE unless numerical subdomains */ glue = '.'; } else { back = FALSE; if (rep == REP_DIR || rep == REP_REFSITE) glue = '/'; else if (rep == REP_BROWSUM) glue = '.'; /* see below */ else glue = '?'; } /* make newpn */ space->name = p->name; space->next = NULL; ENSURE_LEN(name, len, need + strlen(p->name)); if (back || pn == NULL) { space->next = pn; *newpn = space; } else { for (sp = pn; sp->next != NULL; TO_NEXT(sp)) ; sp->next = space; space->next = NULL; *newpn = pn; } /* assemble newpn */ sp = *newpn; if (rep == REP_DOM && !back) /* i.e. numerical subdomain: special case */ TO_NEXT(sp); t = name; if (rep == REP_TYPE && delims) /* delimiter at start */ *(t++) = glue; if (rep == REP_BROWSUM && sp != NULL) { /* special case to enable two different delimiters */ l = strlen(sp->name); memcpy(t, sp->name, l); t += l; if (sp->next != NULL) *(t++) = '/'; TO_NEXT(sp); } for ( ; sp != NULL; TO_NEXT(sp)) { l = strlen(sp->name); memcpy(t, sp->name, l); t += l; if (sp->next != NULL) *(t++) = glue; } if (delims && (rep == REP_REFSITE || rep == REP_DIR)) /* delimiter at end */ *(t++) = glue; *t = '\0'; return(name); } logical sublevels(Hashtable *tree) { /* check if a tree has anything below the top level */ Hashindex *p; unsigned long i; if (tree != NULL) { for (i = 0; i < tree->size; i++) { for (p = tree->head[i]; p != NULL; TO_NEXT(p)) { if (p->other != NULL) return(TRUE); } } } return(FALSE); } /* genstreq is like streq but takes double-pointer strings */ logical genstreq(char *a, char *b, char *t) { for ( ; *a == *t && a < b; a++) t++; if (a == b && *t == '\0') return(TRUE); else return(FALSE); } /* Now the various nextname functions, which vary by type of item. There are two patterns, cutfnp and dcutfnp. The former is used in building a tree report, the latter in building a dervrep or dervtreerep. Pattern for cutfnp (up to pnextname) is void ?nextname(char **name, char **nameend, char *whole, logical build). whole is whole name; [*name, *nameend) is last chunk (*name == NULL if none); build is because behaviour of leading/trailing delimiters may be different if building tree; return new [*name, *nameend), or *name = NULL if no more; never return NULL if given NULL. From Bnextname onwards, the functions follow dcutfnp. They look like void ?nextname(char **name, char **nameend, char *whole, Strpair *list). Otherwise they are the same except they can return NULL if given NULL. */ void rnextname(char **name, char **nameend, char *whole, logical build) { if (*name == NULL) { *name = whole; if ((*nameend = strchr(whole + 1, '?')) == NULL) *nameend = strchr(whole, '\0'); } else if (IS_EMPTY_STRING(*nameend)) *name = NULL; else { *name = *nameend + 1; *nameend = strchr(*name, '\0'); } } void inextname(char **name, char **nameend, char *whole, logical build) { static char *s = "/"; logical first = FALSE; if (*name == NULL) { *name = whole; first = TRUE; } else if (IS_EMPTY_STRING(*nameend) || IS_EMPTY_STRING((*nameend) + 1) || **nameend == '?' || *((*nameend) + 1) == '?') { *name = NULL; return; } else *name = *nameend + 1; for (*nameend = *name + (ptrdiff_t)(first && **name != '\0'); **nameend != '/' && **nameend != '\0' && **nameend != '?'; (*nameend)++) ; /* run nameend to next '/', '\0' or '?' */ if (**nameend == '/') { for ( ; *((*nameend) + 1) == '/'; (*nameend)++) ; /* run to last consecutive '/' */ } if ((**nameend == '\0' || **nameend == '?') && !build) { if (first && **name != '/') { *name = s; *nameend = s + 1; } else if (first) *name = *nameend; else *name = NULL; } } void onextname(char **name, char **nameend, char *whole, logical build) { static char *s0 = "\f"; static char *s1 = "\b"; static logical isnum = FALSE; if (*name == NULL) { isnum = FALSE; *nameend = strchr(whole, '\0'); if (!build) { if (strchr(whole, '.') == NULL) { *name = s1; *nameend = s1 + 1; debug('V', "%s", whole); return; } if (ISDIGIT(*(*nameend - 1))) { *name = s0; *nameend = s0 + 1; isnum = TRUE; return; } } else if (ISDIGIT(*whole) && /* test for num. more subtle while building */ (ISDIGIT(*(*nameend - 1)) || *(*nameend - 1) == '*')) { *name = s0; *nameend = s0 + 1; isnum = TRUE; return; } for (*name = *nameend - 1; **name != '.' && *name != whole; (*name)--) ; if (**name == '.') (*name)++; } else if (isnum) { if (*name == s0) *name = whole; else if (IS_EMPTY_STRING(*nameend) || IS_EMPTY_STRING((*nameend) + 1)) { *name = NULL; return; } else *name = *nameend + 1; for (*nameend = *name; **nameend != '.' && **nameend != '\0'; (*nameend)++) ; /* run to first '.' or '\0' */ } else if (*name == s1 || *name - whole < 2) *name = NULL; else { *nameend = *name - 1; for (*name -= 2; **name != '.' && *name != whole; (*name)--) ; if (**name == '.') (*name)++; } } void tnextname(char **name, char **nameend, char *whole, logical build) { logical first = FALSE; if (*name == NULL) { if ((*nameend = strchr(whole, '?')) == NULL) *nameend = strchr(whole, '\0'); first = TRUE; } else if (*name == whole || *name == whole + 1 || **name == '/') { *name = NULL; return; } else *nameend = *name - 1; *name = *nameend - 1; if (**name == '/' && first) return; for ( ; **name != '.' && **name != '/' && *name != whole; (*name)--) ; if (**name == '.') (*name)++; else if (build) return; else if (first) { *name = whole; *nameend = whole; } else *name = NULL; } void snextname(char **name, char **nameend, char *whole, logical build) { if (*name == NULL) { *name = whole; for (*nameend = *name; **nameend != ':' && **nameend != '\0'; (*nameend)++) ; if (*nameend == '\0') { *nameend = *name; return; } (*nameend)++; if (**nameend == '/') (*nameend)++; if (**nameend == '/') (*nameend)++; for ( ; **nameend != '/' && **nameend != '\0'; (*nameend)++) ; } else inextname(name, nameend, whole, build); } void Znextname(char **name, char **nameend, char *whole, logical build) { static char *s0 = "\f"; static char *s1 = "\b"; static char *s2 = "\v"; extern Strpair **domlevels; Strpair *ap; unsigned int c; if (*name == NULL) { *nameend = strchr(whole, '\0'); if (ISDIGIT(*(*nameend - 1))) { *name = s0; *nameend = s0 + 1; return; } for (*name = *nameend; **name != '.' && *name != whole; (*name)--) ; if (*name == whole) { if (!build) { *name = s1; *nameend = s1 + 1; } return; } (*name)++; c = 26 * ((int)(**name - 'a')); if (**name != '\0') c += (int)(*((*name) + 1) - 'a'); if (c >= DOMLEVEL_NUMBER) c = DOMLEVEL_NUMBER - 1; for (ap = domlevels[c]; ap != NULL && !STREQ(*name, ap->name); TO_NEXT(ap)) ; if (ap == NULL) { /* domain not found */ *name = s2; *nameend = s2 + 1; return; } /* otherwise we've now found the right number of levels */ (*name)--; for (c = (unsigned int)(*(ap->data) - '0'); c > 1 && *name != whole; c--) { for((*name)--; **name != '.' && *name != whole; (*name)--) ; } if (*name == whole && (unsigned int)(*(ap->data) - '0') - c >= 2) { /* don't use whole name even if <= levels (but don't just use domain) */ for ( ; **name != '.'; (*name)++) ; } if (**name == '.') (*name)++; } else onextname(name, nameend, whole, build); } void bnextname(char **name, char **nameend, char *whole, logical build) { if (*name == NULL) { *name = whole; for (*nameend = *name; **nameend != '/' && **nameend != '\0'; (*nameend)++) ; } else if (**nameend == '/') { *name = *nameend + 1; for (*nameend = *name; **nameend != '.' && **nameend != ' ' && **nameend != '\0'; (*nameend)++) ; } else if (**nameend == '.') { *name = *nameend + 1; for (*nameend = *name; **nameend != ' ' && **nameend != '\0'; (*nameend)++) ; } else *name = NULL; } void pnextname(char **name, char **nameend, char *whole, logical build) { if (*name == NULL) { *name = whole; for (*nameend = *name; **nameend != ':' && **nameend != '\0'; (*nameend)++) ; } else if (**nameend == ':') { *name = *nameend + 1; for (*nameend = *name; **nameend != '\0'; (*nameend)++) ; } else *name = NULL; } void Bnextname(char **name, char **nameend, char *whole, Strpair *list) { /* NB "list" is ignored */ static char *s1 = "Mozilla (compatible)"; static char *s2 = "Mosaic"; char *temps; if (*name == NULL) { if (strstr(whole, "osaic") != NULL) { *name = s2; *nameend = s2 + 6; } else if (headmatch(whole, "Mozilla")) { /* try & do something nearly sensible for all the Moz. (compatible)'s */ if ((temps = strstr(whole + 8, "WebTV")) != NULL || (temps = strstr(whole + 8, "Opera")) != NULL) { *name = temps; if (*(temps + 5) == '\0') *nameend = temps + 5; else for (*nameend = temps + 6; ISALNUM(**nameend) || **nameend == '.'; (*nameend)++) ; } else if ((temps = strstr(whole + 8, "MSIE")) != NULL) { *name = temps; if (*(temps + 4) == '\0') *nameend = temps + 4; else for (*nameend = temps + 5; ISALNUM(**nameend) || **nameend == '.'; (*nameend)++) ; } else if (strstr(whole + 9, "ompatible")) { *name = s1; *nameend = s1 + 20; } else { /* probably genuine Mozilla */ *name = whole; *nameend = strchr(whole + 7, ' '); if (*nameend == NULL) *nameend = strchr(whole + 7, '\0'); } } else { *name = whole; *nameend = strchr(whole, '\0'); } } else *name = NULL; } void Pnextname(char **name, char **nameend, char *whole, Strpair *list) { /* list is ignored again */ char *c; if (*name == NULL) { if (headmatch(whole, "Mozilla")) whole += 7; /* just to save searching time */ if ((c = strstr(whole, "Windows")) != NULL) { c += 7; if (*c == ' ') c++; } else if ((c = strstr(whole, "Win")) != NULL && (ISDIGIT(*(c + 3)) || *(c + 3) == 'N')) c += 3; if (c != NULL) { if (*c == '9' && *(c + 1) == '5') *name = "Windows:Windows 95"; else if (*c == '9' && *(c + 1) == '8') { if (strstr(c, "Win 9x 4.9")) *name = "Windows:Windows Me"; else *name = "Windows:Windows 98"; } else if (*c == 'N' && *(c + 1) == 'T') { if (*(c + 2) == ' ' && *(c + 3) == '5') *name = "Windows:Windows 2000"; else *name = "Windows:Windows NT"; } else if (*c == 'C' && *(c + 1) == 'E') *name = "Windows:Windows CE"; else if (*c == '3' && *(c + 1) == '.' && *(c + 2) == '1') *name = "Windows:Windows 3.1"; else if ((*c == '1' && *(c + 1) == '6') || strstr(c + 1, "16bit") || strstr(c + 1, "16-bit")) *name = "Windows:Windows 16-bit"; else if ((*c == '3' && *(c + 1) == '2') || strstr(c + 1, "32bit") || strstr(c + 1, "32-bit")) *name = "Windows:Windows 32-bit"; else *name = "Windows:unkwin"; } else if ((c = strstr(whole, "Mac")) != NULL) { if (strstr(c + 3, "68")) *name = "Macintosh:Macintosh 68k"; else if (strstr(c + 3, "PPC") || strstr(c + 3, "PowerPC")) *name = "Macintosh:Macintosh PowerPC"; else *name = "Macintosh:unkmac"; } else if (strstr(whole, "Linux") != NULL || strstr(whole, "linux") != NULL) *name = "Unix:Linux"; else if (strstr(whole, "BSD") != NULL) *name = "Unix:BSD"; else if (strstr(whole, "SunOS") != NULL || strstr(whole, "sunos") != NULL) *name = "Unix:SunOS"; else if (strstr(whole, "HP-UX") != NULL || strstr(whole, "HPUX") != NULL || strstr(whole, "hp-ux") != NULL || strstr(whole, "hpux") != NULL) *name = "Unix:HP-UX"; else if (strstr(whole, "IRIX") != NULL || strstr(whole, "irix") != NULL) *name = "Unix:IRIX"; else if (strstr(whole, "AIX") != NULL || strstr(whole, "aix") != NULL) *name = "Unix:AIX"; else if (strstr(whole, "OSF1") != NULL) *name = "Unix:OSF1"; else if (strstr(whole, "VMS") != NULL) *name = "OpenVMS"; else if (strstr(whole, "X11") != NULL) *name = "Unix:unkux"; else if (strstr(whole, "WebTV") != NULL) *name = "WebTV"; else if (strstr(whole, "OS/2") != NULL) *name = "OS/2"; else if (strstr(whole, "BeOS") != NULL) *name = "BeOS"; else if (strstr(whole, "RISC OS") != NULL) *name = "RISC OS"; else if (strstr(whole, "Amiga") != NULL) *name = "Amiga"; else *name = "unkos"; *nameend = strchr(*name, '\0'); } else *name = NULL; } void Nnextname(char **name, char **nameend, char *whole, Strpair *list) { Strpair *al; char *c, *d; logical done; if (*name == NULL) { if ((c = strchr(whole, '?')) != NULL && *(c + 1) != '\0') { *c = '\0'; d = c; for (al = list, done = FALSE; al != NULL && !done; TO_NEXT(al)) { if (MATCHES(whole, al->name)) { /* find right engine in list */ for (c = d; c != NULL && !done; c = strchr(c, '&')) { c++; /* find right arg */ if (headmatch(c, al->data) && *(c + strlen(al->data)) == '=') { done = TRUE; *name = c + strlen(al->data) + 1; for (*nameend = *name; **nameend != '&' && **nameend != '\0'; (*nameend)++) ; /* run to next & */ if (*name == *nameend) name = NULL; } } } } *d = '?'; } } else *name = NULL; } void nnextname(char **name, char **nameend, char *whole, Strpair *list) { if (*name == NULL) Nnextname(name, nameend, whole, list); /* to find right arg etc. */ else if (**nameend == '&' || **nameend == '\0') *name = NULL; else *name = *nameend + 1; if (*name == NULL) return; for ( ; **name == '+' || **name == ',' || **name == ';' || **name == '"' || **name == '(' || **name == ')' || **name == '.' || ISSPACE(**name) || (**name == '-' && (*(*name + 1) == '+' || *(*name + 1) == ',' || *(*name + 1) == ';' || *(*name + 1) == '"' || *(*name + 1) == '(' || *(*name + 1) == ')' || *(*name + 1) == '.' || *(*name + 1) == '-' || *(*name + 1) == '&' || *(*name + 1) == '\0' || ISSPACE(*(*name + 1)))); (*name)++) ; /* run to first wanted character; cf list in do_aliasN() and below */ /* NB 'good' dots never occur at beginning of word */ if (**name == '&' || **name == '\0') *name = NULL; else for (*nameend = *name; **nameend != '+' && **nameend != '&' && **nameend != '\0' && **nameend != '"' && **nameend != ',' && **nameend != ';' && **nameend != '(' && **nameend != ')' && (**nameend != '.' || (ISALNUM(*(*nameend - 1)) && ISALNUM(*(*nameend + 1)))) && (**nameend != '-' || *nameend == *name) && !ISSPACE(**nameend); (*nameend)++) ; /* run to first unwanted character; see above */ }