Mercurial > illumos > onarm
diff usr/src/cmd/mailx/optim.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/mailx/optim.c Tue Jun 02 18:56:50 2009 +0900 @@ -0,0 +1,1008 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1998 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + + +#pragma ident "@(#)optim.c 1.26 05/06/13 SMI" + +/* + * mailx -- a modified version of a University of California at Berkeley + * mail program + * + * Network name modification routines. + */ + +#include "rcv.h" +#include "configdefs.h" +#include <locale.h> + +static char *arpafix(char name[], char from[]); +static char *lasthost(char *addr); +static char *makeremote(char name[], char from[]); +static int mstash(char name[], int attnet); +static int mtype(int mid); +static int netlook(char machine[], int attnet); +static int nettype(int mid); +static int ntype(register int nc); +static void stradd(register char *str, int n, register int c); +static char *tackon(char *sys, char *rest); +static struct xtrahash *xlocate(char name[]); +#ifdef OPTIM +static char best(int src, int dest); +static char *mlook(int mid); +static int netkind(register int nt); +static void optiboth(char net[]); +static void optim(char net[], char name[]); +static void optim1(char netstr[], char name[]); +static int optimex(char net[], char name[]); +static int optimimp(char net[], char name[]); +static void prefer(char name[]); +static char *rpair(char str[], int mach); +#endif + +/* + * Map a name into the correct network "view" of the + * name. This is done by prepending the name with the + * network address of the sender, then optimizing away + * nonsense. + */ + +char * +netmap(char name[], char from[]) +{ + char nbuf[BUFSIZ], ret[BUFSIZ]; + register char *cp, *oname; + + if (debug) fprintf(stderr, "netmap(name '%s', from '%s')\n", name, from); + if (strlen(from) == 0) + return(name); /* "from" is empty - can't do anything */ + + if (strcmp(from, name) == 0) + return(name); /* "from" and "name" are the same, do nothing */ + + /* + * If the name contains an "@" or a "%", remove it and the host + * following it if that host is "known". + */ + if (any('@', name) || any('%', name)) + return(arpafix(name, from)); + + /* + * If the sender contains a "@" or a "%", make "name" into an + * address on that host, on the presumption that it should + * really have read "name@from" when we received the message + * rather than just "name". + */ + if (any('@', from) || any('%', from)) + return(unuucp(makeremote(name, from))); + if (value("onehop") && (cp = strchr(name, '!')) && cp > name) { + /* + * "onehop" is set, meaning all machines are one UUCP + * hop away (fat chance, in this day and age), and "name" + * is a UUCP path rather than just a name. Leave it alone. + */ + nstrcpy(nbuf, sizeof (nbuf), name); + } else { + from = tackon(host, from); + *strrchr(from, '!') = 0; + name = tackon(lasthost(from), name); + while (((cp = lasthost(from)) != 0) && ishost(cp, name)) { + oname = name; + name = strchr(name, '!') + 1; + if (cp == from) { + from[strlen(from)] = '!'; + if (value("mustbang") && !strchr(name, '!')) + name = oname; + return(unuucp(name)); + } + *--cp = 0; + } + from[strlen(from)] = '!'; + from = strchr(from, '!') + 1; + snprintf(nbuf, sizeof (nbuf), "%s!%s", from, name); + } + if (debug) fprintf(stderr, "before optim, nbuf '%s'\n", name); +#ifdef OPTIM + if ((cp = value("conv"))==NOSTR || strcmp(cp, "optimize") != 0) + nstrcpy(ret, sizeof (ret), nbuf); + else + optim(nbuf, ret); +#else + nstrcpy(ret, sizeof (ret), nbuf); +#endif /* OPTIM */ + if (debug) fprintf(stderr, "after optim, nbuf '%s', ret '%s'\n", nbuf, ret); + cp = ret; + if (debug) fprintf(stderr, "wind up with '%s'\n", name); + if (!icequal(name, cp)) + return(unuucp((char *) savestr(cp))); + return(unuucp(name)); +} + +/* + * Stick a host on the beginning of a uucp + * address if it isn't there already. + */ +static char * +tackon(char *sys, char *rest) +{ + while (*rest == '!') + rest++; + if (!ishost(sys, rest)) { + char *r = (char *)salloc(strlen(sys) + strlen(rest) + 2); + sprintf(r, "%s!%s", sys, rest); + rest = r; + } + return rest; +} + +/* + * Check equality of the first host in a uucp address. + */ +int +ishost(char *sys, char *rest) +{ + while (*sys && *sys == *rest) + sys++, rest++; + return(*sys == 0 && *rest == '!'); +} + +/* + * Return last host in a uucp address. + */ +static char * +lasthost(char *addr) +{ + char *r = strrchr(addr, '!'); + return r ? ++r : addr; +} + +/* + * Optionally translate an old format uucp name into a new one, e.g. + * "mach1!mach2!user" becomes "user@mach2.UUCP". This optional because + * some information is necessarily lost (e.g. the route it got here + * via) and if we don't have the host in our routing tables, we lose. + * XXX THIS IS NO LONGER VALID WITH THE NEW UUCP PROJECT PLANS TO + * REGISTER UUCP HOSTS IN THE STANDARD INTERNET NAMESPACE, E.G. + * ihnp4 BECOMES "ihnp4.att.com". + */ +char * +unuucp(char *name) +{ + register char *np, *hp, *cp; + char result[100]; + char tname[300]; + + if (UnUUCP==0 && + ((cp = value("conv"))==NOSTR || strcmp(cp, "internet"))) + return name; + if (debug) fprintf(stderr, "unuucp(%s)\n", name); + nstrcpy(tname, sizeof (tname), name); + np = strrchr(tname, '!'); + if (np == NOSTR) + return name; + *np++ = 0; + hp = strrchr(tname, '!'); + if (hp == NOSTR) + hp = tname; + else + *hp++ = 0; + cp = strchr(np, '@'); + if (cp == NOSTR) + cp = strchr(np, '%'); + if (cp) + *cp = 0; + if (debug) fprintf(stderr, "host %s, name %s\n", hp, np); + snprintf(result, sizeof (result), "%s@%s.UUCP", np, hp); + if (debug) fprintf(stderr, "unuucp returns %s\n", result); + return savestr(result); +} + +/* + * Turn a network machine name into a unique character + */ +static int +netlook(char machine[], int attnet) +{ + register struct netmach *np; + register char *cp, *cp2; + char nbuf[BUFSIZ]; + + /* + * Make into lower case. + */ + for (cp = machine, cp2 = nbuf; + *cp && cp2 < &nbuf[BUFSIZ-1]; + *cp2++ = tolower(*cp++)) + /*nothing*/; + *cp2 = 0; + + /* + * If a single letter machine, look through those first. + */ + + if (strlen(nbuf) == 1) + for (np = netmach; np->nt_mid != 0; np++) + if (np->nt_mid == nbuf[0]) + return(nbuf[0]); + + /* + * Look for usual name + */ + + for (np = netmach; np->nt_mid != 0; np++) + if (strcmp(np->nt_machine, nbuf) == 0) + return(np->nt_mid); + + /* + * Look in side hash table. + */ + + return(mstash(nbuf, attnet)); +} + +#ifdef OPTIM +/* + * Turn a network unique character identifier into a network name. + */ + +static char * +netname(int mid) +{ + register struct netmach *np; + + if (mid & 0200) + return(mlook(mid)); + for (np = netmach; np->nt_mid != 0; np++) + if (np->nt_mid == mid) + return(np->nt_machine); + return(NOSTR); +} +#endif + +/* + * Deal with arpa net addresses. The way this is done is strange. + * name contains an "@" or "%". Look up the machine after it in + * the hash table. If it isn't found, return name unmolested. + * If ???, return name unmolested. + * Otherwise, delete the "@" or "%" and the machine after it from + * name, and return the new string. + */ +static char * +arpafix(char name[], char from[]) +{ + register char *cp; + register int arpamach; + char newname[BUFSIZ]; + + if (debug) { + fprintf(stderr, "arpafix(%s, %s)\n", name, from); + } + cp = strrchr(name, '@'); + if (cp == NOSTR) + cp = strrchr(name, '%'); + if (cp == NOSTR) { + fprintf(stderr, + gettext("Something's amiss -- no @ or %% in arpafix\n")); + return(name); + } + cp++; + arpamach = netlook(cp, '@'); + if (debug) + fprintf(stderr, + "cp '%s', arpamach %o, nettypes arpamach %o LOCAL %o\n", + cp, arpamach, nettype(arpamach), nettype(LOCAL)); + if (arpamach == 0) { + if (debug) + fprintf(stderr, "machine %s unknown, uses: %s\n", + cp, name); + return(name); + } + if (((nettype(arpamach) & nettype(LOCAL)) & ~AN) == 0) { + if (debug) + fprintf(stderr, "machine %s known but remote, uses: %s\n", + cp, name); + return(name); + } + nstrcpy(newname, sizeof (newname), name); + cp = strrchr(newname, '@'); + if (cp == NOSTR) + cp = strrchr(newname, '%'); + *cp = 0; + if (debug) fprintf(stderr, "local address, return '%s'\n", newname); + return(savestr(newname)); +} + +/* + * We have name with no @'s in it, and from with @'s. + * Assume that name is meaningful only on the site in from, + * and return "name@site_in_from". + */ +static char * +makeremote(char name[], char from[]) +{ + register char *cp; + char rbuf[BUFSIZ]; + + if (!value("makeremote")) + return(name); + if (debug) fprintf(stderr, "makeremote(%s, %s) returns ", name, from); + cp = strrchr(from, '@'); + if (cp == NOSTR) + cp = strrchr(from, '%'); + snprintf(rbuf, sizeof (rbuf), "%s%s", name, cp); + if (debug) fprintf(stderr, "%s\n", rbuf); + return(savestr(rbuf)); +} + +/* + * Take a network machine descriptor and find the types of connected + * nets and return it. + */ +static int +nettype(int mid) +{ + register struct netmach *np; + + if (mid & 0200) + return(mtype(mid)); + for (np = netmach; np->nt_mid != 0; np++) + if (np->nt_mid == mid) + return(np->nt_type); + return(0); +} + +/* + * Hashing routines to salt away machines seen scanning + * networks paths that we don't know about. + */ + +#define XHSIZE 97 /* Size of extra hash table */ +#define NXMID (XHSIZE*3/4) /* Max extra machines */ + +struct xtrahash { + char *xh_name; /* Name of machine */ + short xh_mid; /* Machine ID */ + short xh_attnet; /* Attached networks */ +} xtrahash[XHSIZE]; + +static struct xtrahash *xtab[XHSIZE]; /* F: mid-->machine name */ + +static short midfree; /* Next free machine id */ + +/* + * Initialize the extra host hash table. + * Called by sreset. + */ +void +minit(void) +{ + register struct xtrahash *xp, **tp; + + midfree = 0; + tp = &xtab[0]; + for (xp = &xtrahash[0]; xp < &xtrahash[XHSIZE]; xp++) { + xp->xh_name = NOSTR; + xp->xh_mid = 0; + xp->xh_attnet = 0; + *tp++ = (struct xtrahash *) 0; + } +} + +/* + * Stash a net name in the extra host hash table. + * If a new entry is put in the hash table, deduce what + * net the machine is attached to from the net character. + * + * If the machine is already known, add the given attached + * net to those already known. + */ +static int +mstash(char name[], int attnet) +{ + register struct xtrahash *xp; + int x; + + xp = xlocate(name); + if (xp == (struct xtrahash *) 0) { + printf(gettext("Ran out of machine id spots\n")); + return(0); + } + if (xp->xh_name == NOSTR) { + if (midfree >= XHSIZE) { + printf(gettext("Out of machine ids\n")); + return(0); + } + xtab[midfree] = xp; + xp->xh_name = savestr(name); + xp->xh_mid = 0200 + midfree++; + } + x = ntype(attnet); + if (x == 0) + xp->xh_attnet |= AN; + else + xp->xh_attnet |= x; + return(xp->xh_mid); +} + +/* + * Search for the given name in the hash table + * and return the pointer to it if found, or to the first + * empty slot if not found. + * + * If no free slots can be found, return 0. + */ + +static struct xtrahash * +xlocate(char name[]) +{ + register int h, q, i; + register char *cp; + register struct xtrahash *xp; + + for (h = 0, cp = name; *cp; h = (h << 2) + *cp++) + ; + if (h < 0 && (h = -h) < 0) + h = 0; + h = h % XHSIZE; + cp = name; + for (i = 0, q = 0; q < XHSIZE; i++, q = i * i) { + xp = &xtrahash[(h + q) % XHSIZE]; + if (xp->xh_name == NOSTR) + return(xp); + if (strcmp(cp, xp->xh_name) == 0) + return(xp); + if (h - q < 0) + h += XHSIZE; + xp = &xtrahash[(h - q) % XHSIZE]; + if (xp->xh_name == NOSTR) + return(xp); + if (strcmp(cp, xp->xh_name) == 0) + return(xp); + } + return((struct xtrahash *) 0); +} + +#ifdef OPTIM +/* + * Return the name from the extra host hash table corresponding + * to the passed machine id. + */ + +static char * +mlook(int mid) +{ + register int m; + + if ((mid & 0200) == 0) + return(NOSTR); + m = mid & 0177; + if (m >= midfree) { + printf(gettext("Use made of undefined machine id\n")); + return(NOSTR); + } + return(xtab[m]->xh_name); +} +#endif + +/* + * Return the bit mask of net's that the given extra host machine + * id has so far. + */ +static int +mtype(int mid) +{ + register int m; + + if ((mid & 0200) == 0) + return(0); + m = mid & 0177; + if (m >= midfree) { + printf(gettext("Use made of undefined machine id\n")); + return(0); + } + return(xtab[m]->xh_attnet); +} + +#ifdef OPTIM +/* + * Take a network name and optimize it. This gloriously messy + * operation takes place as follows: the name with machine names + * in it is tokenized by mapping each machine name into a single + * character machine id (netlook). The separator characters (network + * metacharacters) are left intact. The last component of the network + * name is stripped off and assumed to be the destination user name -- + * it does not participate in the optimization. As an example, the + * name "res!vax!res!uvax!bill" becomes, tokenized, + * "r!x!r!v!" and "bill" A low level routine, optim1, fixes up the + * network part (eg, "r!x!r!v!"), then we convert back to network + * machine names and tack the user name on the end. + * + * The result of this is copied into the parameter "name" + */ + +static void +optim(char net[], char name[]) +{ + char netcomp[BUFSIZ], netstr[STSIZ], xfstr[STSIZ]; + register char *cp, *cp2; + register int c; + + if (debug) fprintf(stderr, "optim(%s, %s) called\n", net, name); + *netstr = '\0'; + cp = net; + for (;;) { + /* + * Rip off next path component into netcomp + */ + cp2 = netcomp; + while (*cp && !any(*cp, metanet)) + *cp2++ = *cp++; + *cp2 = 0; + /* + * If we hit null byte, then we just scanned + * the destination user name. Go off and optimize + * if its so. + */ + if (*cp == 0) + break; + if ((c = netlook(netcomp, *cp)) == 0) { + printf(gettext("No host named \"%s\"\n"), netcomp); +err: + nstrcpy(name, BUFSIZ, net); + return; + } + stradd(name, BUFSIZ, c); + stradd(name, BUFSIZ, *cp++); + /* + * If multiple network separators given, + * throw away the extras. + */ + while (any(*cp, metanet)) + cp++; + } + if (strlen(netcomp) == 0) { + printf(gettext("net name syntax\n")); + goto err; + } + if (debug) fprintf(stderr, "optim1(%s,%s) called\n", netstr, xfstr); + optim1(netstr, xfstr); + if (debug) fprintf(stderr, "optim1(%s,%s) returns\n", netstr, xfstr); + + /* + * Convert back to machine names. + */ + + cp = xfstr; + *name = '\0'; + while (*cp) { + if ((cp2 = netname(*cp++)) == NOSTR) { + printf(gettext("Made up bad net name\n")); + printf(gettext("Machine code %c (0%o)\n"), cp[-1], +cp[-1]); + printf(gettext("Sorry.\n")); + goto err; + } + nstrcat(name, BUFSIZ, cp2); + stradd(name, BUFSIZ, *cp++); + } + nstrcat(name, BUFSIZ, netcomp); + if (debug) fprintf(stderr, "optim returns %s in name\n", name); +} + +/* + * Take a string of network machine id's and separators and + * optimize them. We process these by pulling off maximal + * leading strings of the same type, passing these to the appropriate + * optimizer and concatenating the results. + */ + +static void +optim1(char netstr[], char name[]) +{ + char path[STSIZ], rpath[STSIZ]; + register char *cp, *cp2; + register int tp, nc; + + cp = netstr; + prefer(cp); + *name = '\0'; + /* + * If the address ultimately points back to us, + * just return a null network path. + */ + if ((int)strlen(cp) > 1 && cp[strlen(cp) - 2] == LOCAL) + return; + while (*cp != 0) { + *path = '\0'; + + tp = ntype(cp[1]); + nc = cp[1]; + while (*cp && tp == ntype(cp[1])) { + stradd(path, sizeof (path), *cp++); + cp++; + } + switch (netkind(tp)) { + default: + nstrcpy(rpath, sizeof (rpath), path); + break; + + case IMPLICIT: + optimimp(path, rpath); + break; + + case EXPLICIT: + optimex(path, rpath); + break; + } + for (cp2 = rpath; *cp2 != 0; cp2++) { + stradd(name, BUFSIZ, *cp2); + stradd(name, BUFSIZ, nc); + } + } + optiboth(name); + prefer(name); +} +#endif /* OPTIM */ + +/* + * Return the network of the separator -- + * AN for arpa net + * BN for Bell labs net (e.g. UUCP, NOT Berknet) + * SN for Schmidt net (Berknet) + * 0 if we don't know. + */ +static int +ntype(register int nc) +{ + register struct ntypetab *np; + + for (np = ntypetab; np->nt_char != 0; np++) + if (np->nt_char == nc) + return(np->nt_bcode); + return(0); +} + +#ifdef OPTIM +/* + * Return the kind of routing used for the particular net + * EXPLICIT means explicitly routed + * IMPLICIT means implicitly routed + * 0 means don't know + */ + +static int +netkind(register int nt) +{ + register struct nkindtab *np; + + for (np = nkindtab; np->nk_type != 0; np++) + if (np->nk_type == nt) + return(np->nk_kind); + return(0); +} + +/* + * Do name optimization for an explicitly routed network (eg uucp). + */ + +static int +optimex(char net[], char name[]) +{ + register char *cp, *rp; + register int m; + + nstrcpy(name, STSIZ, net); + cp = name; + if (strlen(cp) == 0) + return(-1); + if (cp[strlen(cp)-1] == LOCAL) { + name[0] = 0; + return(0); + } + for (cp = name; *cp; cp++) { + m = *cp; + rp = strrchr(cp+1, m); + if (rp != NOSTR) + strcpy(cp, rp); + } + return(0); +} + +/* + * Do name optimization for implicitly routed network (eg, arpanet). + */ + +static int +optimimp(char net[], char name[]) +{ + register char *cp; + register char m; + + cp = net; + if (strlen(cp) == 0) + return(-1); + m = cp[strlen(cp) - 1]; + if (m == LOCAL) { + *name = '\0'; + return(0); + } + name[0] = m; + name[1] = 0; + return(0); +} + +/* + * Perform global optimization on the given network path. + * The trick here is to look ahead to see if there are any loops + * in the path and remove them. The interpretation of loops is + * more strict here than in optimex since both the machine and net + * type must match. + */ + +static void +optiboth(char net[]) +{ + register char *cp, *cp2; + + cp = net; + if (strlen(cp) == 0) + return; + if (((int)strlen(cp) % 2) != 0) { + printf(gettext("Strange arg to optiboth\n")); + return; + } + while (*cp) { + cp2 = rpair(cp+2, *cp); + if (cp2 != NOSTR) + strcpy(cp, cp2); + cp += 2; + } +} + +/* + * Find the rightmost instance of the given (machine, type) pair. + */ + +static char * +rpair(char str[], int mach) +{ + register char *cp, *last; + + cp = str; + last = NOSTR; + while (*cp) { + if (*cp == mach) + last = cp; + cp += 2; + } + return(last); +} + +/* + * Change the network separators in the given network path + * to the preferred network transmission means. + */ + +static void +prefer(char name[]) +{ + register char *cp, n; + register int state; + + state = LOCAL; + for (cp = name; *cp; cp += 2) { + n = best(state, *cp); + if (n) + cp[1] = n; + state = *cp; + } +} + +/* + * Return the best network separator for the given machine pair. + */ + +static char +best(int src, int dest) +{ + register int dtype, stype; + register struct netorder *np; + + stype = nettype(src); + dtype = nettype(dest); + fflush(stdout); + if (stype == 0 || dtype == 0) { + printf(gettext("ERROR: unknown internal machine id\n")); + return(0); + } + if ((stype & dtype) == 0) + return(0); + np = &netorder[0]; + while ((np->no_stat & stype & dtype) == 0) + np++; + return(np->no_char); +} +#endif /* OPTIM */ + +#ifdef notdef +/* + * Code to twist around arpa net names. + */ + +#define WORD 257 /* Token for a string */ + +static char netbuf[256]; +static char *yylval; + +/* + * Reverse all of the arpa net addresses in the given name to + * be of the form "host @ user" instead of "user @ host" + * This function is its own inverse. + */ + +char * +revarpa(char str[]) +{ + + if (yyinit(str) < 0) + return(NOSTR); + if (name()) + return(NOSTR); + if (strcmp(str, netbuf) == 0) + return(str); + return(savestr(netbuf)); +} + +/* + * Parse (by recursive descent) network names, using the following grammar: + * name: + * term {':' term} + * term {'^' term} + * term {'!' term} + * term '@' name + * term '%' name + * + * term: + * string of characters. + */ + +static int +name(void) +{ + register int t; + register char *cp; + + for (;;) { + t = yylex(); + if (t != WORD) + return(-1); + cp = yylval; + t = yylex(); + switch (t) { + case 0: + nstrcat(netbuf, sizeof (netbuf), cp); + return(0); + + case '@': + case '%': + if (name()) + return(-1); + stradd(netbuf, sizeof (netbuf), '@'); + nstrcat(netbuf, sizeof (netbuf), cp); + return(0); + case WORD: + return(-1); + + default: + nstrcat(netbuf, sizeof (netbuf), cp); + stradd(netbuf, sizeof (netbuf), t); + } + } +} + +/* + * Scanner for network names. + */ + +static char *charp; /* Current input pointer */ +static int nexttok; /* Salted away next token */ + +/* + * Initialize the network name scanner. + */ + +int +yyinit(char str[]) +{ + static char lexbuf[BUFSIZ]; + + netbuf[0] = 0; + if (strlen(str) >= sizeof lexbuf - 1) + return(-1); + nexttok = 0; + nstrcpy(lexbuf, sizeof (lexbuf), str); + charp = lexbuf; + return(0); +} + +/* + * Scan and return a single token. + * yylval is set to point to a scanned string. + */ + +int +yylex(void) +{ + register char *cp, *dotp; + register int s; + + if (nexttok) { + s = nexttok; + nexttok = 0; + return(s); + } + cp = charp; + while (*cp && isspace(*cp)) + cp++; + if (*cp == 0) + return(0); + if (any(*cp, metanet)) { + charp = cp+1; + return(*cp); + } + dotp = cp; + while (*cp && !any(*cp, metanet) && !any(*cp, " \t")) + cp++; + if (any(*cp, metanet)) + nexttok = *cp; + if (*cp == 0) + charp = cp; + else + charp = cp+1; + *cp = 0; + yylval = dotp; + return(WORD); +} +#endif + +/* + * Add a single character onto a string. Here dstsize is the size of the + * destnation buffer. + */ + +static void +stradd(register char *dst, int dstsize, register int c) +{ + while (*dst != '\0') { + dst++; + dstsize--; + } + if (--dstsize > 0) + *dst++ = (char)c; + *dst = '\0'; +}