14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*7789Seric SCCSID(@(#)headers.c 3.25 08/17/82); 54091Seric 64091Seric /* 74091Seric ** CHOMPHEADER -- process and save a header line. 84091Seric ** 94091Seric ** Called by collect and by readcf to deal with header lines. 104091Seric ** 114091Seric ** Parameters: 124091Seric ** line -- header as a text line. 134091Seric ** def -- if set, this is a default value. 144091Seric ** 154091Seric ** Returns: 164091Seric ** flags for this header. 174091Seric ** 184091Seric ** Side Effects: 194091Seric ** The header is saved on the header list. 204319Seric ** Contents of 'line' are destroyed. 214091Seric */ 224091Seric 234091Seric chompheader(line, def) 244091Seric char *line; 254091Seric bool def; 264091Seric { 274091Seric register char *p; 284091Seric register HDR *h; 294091Seric HDR **hp; 304091Seric extern bool isheader(); 314091Seric char *fname; 324091Seric char *fvalue; 334091Seric struct hdrinfo *hi; 344627Seric u_long mopts; 354627Seric extern u_long mfencode(); 367783Seric extern char *crackfrom(); 374091Seric 387677Seric # ifdef DEBUG 397677Seric if (tTd(31, 6)) 407677Seric printf("chompheader: %s\n", line); 417677Seric # endif DEBUG 427677Seric 434091Seric /* strip off trailing newline */ 444091Seric p = rindex(line, '\n'); 454091Seric if (p != NULL) 464091Seric *p = '\0'; 474091Seric 484627Seric /* strip off options */ 494627Seric mopts = 0; 504627Seric p = line; 514627Seric if (*p == '?') 524627Seric { 534627Seric /* have some */ 544627Seric register char *q = index(p + 1, *p); 554627Seric 564627Seric if (q != NULL) 574627Seric { 584627Seric *q++ = '\0'; 594627Seric mopts = mfencode(p + 1); 604627Seric p = q; 614627Seric } 624627Seric else 634627Seric syserr("chompheader: syntax error, line \"%s\"", line); 644627Seric } 654627Seric 664091Seric /* find canonical name */ 674627Seric fname = p; 684627Seric p = index(p, ':'); 694091Seric fvalue = &p[1]; 704091Seric while (isspace(*--p)) 714091Seric continue; 724091Seric *++p = '\0'; 734091Seric makelower(fname); 744091Seric 754091Seric /* strip field value on front */ 764091Seric if (*fvalue == ' ') 774091Seric fvalue++; 784091Seric 794091Seric /* search header list for this header */ 806908Seric for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; hp = &h->h_link, h = h->h_link) 814091Seric { 825187Seric if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 834091Seric break; 844091Seric } 854091Seric 864091Seric /* see if it is a known type */ 874091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 884091Seric { 894091Seric if (strcmp(hi->hi_field, fname) == 0) 904091Seric break; 914091Seric } 924091Seric 934091Seric /* if this means "end of header" quit now */ 944091Seric if (bitset(H_EOH, hi->hi_flags)) 954091Seric return (hi->hi_flags); 964091Seric 97*7789Seric /* count Received: lines to avoid loops (simulate hop counts) */ 98*7789Seric if (strcmp(fname, "received") == 0) 997365Seric HopCount++; 1007365Seric 1014091Seric /* create/fill in a new node */ 1025187Seric if (h == NULL || bitset(H_FORCE, h->h_flags)) 1034091Seric { 1044091Seric /* create a new node */ 1055187Seric h = (HDR *) xalloc(sizeof *h); 1064091Seric h->h_field = newstr(fname); 1074091Seric h->h_value = NULL; 1085187Seric h->h_link = *hp; 1094091Seric h->h_flags = hi->hi_flags; 1104627Seric h->h_mflags = mopts | hi->hi_mflags; 1115187Seric *hp = h; 1124091Seric } 1134091Seric if (def) 1144091Seric h->h_flags |= H_DEFAULT; 1154627Seric else if (mopts == 0) 1164091Seric h->h_flags &= ~H_CHECK; 1174091Seric if (h->h_value != NULL) 1184091Seric free(h->h_value); 1197783Seric if (!def && strcmp(fname, "from") == 0) 1207783Seric { 1217783Seric /* turn it into a macro -- will be expanded later */ 1227783Seric h->h_value = newstr(crackfrom(fvalue)); 1237783Seric h->h_flags |= H_DEFAULT; 1247783Seric } 1257783Seric else 1267783Seric h->h_value = newstr(fvalue); 1275919Seric if (!def && GrabTo && bitset(H_RCPT, h->h_flags)) 1286908Seric sendto(h->h_value, 0, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1294091Seric 1305937Seric /* hack to see if this is a new format message */ 1315937Seric if (bitset(H_RCPT, h->h_flags) && 1325937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1335937Seric index(fvalue, '<') != NULL)) 1346908Seric CurEnv->e_oldstyle = FALSE; 1355937Seric 1364091Seric return (h->h_flags); 1374091Seric } 1384091Seric /* 1396980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1406980Seric ** 1416980Seric ** This bypasses the special checking of chompheader. 1426980Seric ** 1436980Seric ** Parameters: 1446980Seric ** field -- the name of the header field. 1456980Seric ** value -- the value of the field. It must be lower-cased. 1466980Seric ** e -- the envelope to add them to. 1476980Seric ** 1486980Seric ** Returns: 1496980Seric ** none. 1506980Seric ** 1516980Seric ** Side Effects: 1526980Seric ** adds the field on the list of headers for this envelope. 1536980Seric */ 1546980Seric 1556980Seric addheader(field, value, e) 1566980Seric char *field; 1576980Seric char *value; 1586980Seric ENVELOPE *e; 1596980Seric { 1606980Seric register HDR *h; 1616980Seric register struct hdrinfo *hi; 1626980Seric HDR **hp; 1636980Seric 1646980Seric /* find info struct */ 1656980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1666980Seric { 1676980Seric if (strcmp(field, hi->hi_field) == 0) 1686980Seric break; 1696980Seric } 1706980Seric 1716980Seric /* find current place in list -- keep back pointer? */ 1726980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1736980Seric { 1746980Seric if (strcmp(field, h->h_field) == 0) 1756980Seric break; 1766980Seric } 1776980Seric 1786980Seric /* allocate space for new header */ 1796980Seric h = (HDR *) xalloc(sizeof *h); 1806980Seric h->h_field = field; 1816980Seric h->h_value = newstr(value); 1827368Seric h->h_link = *hp; 1836980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 1846980Seric h->h_mflags = hi->hi_mflags; 1856980Seric *hp = h; 1866980Seric } 1876980Seric /* 1884091Seric ** HVALUE -- return value of a header. 1894091Seric ** 1904091Seric ** Only "real" fields (i.e., ones that have not been supplied 1914091Seric ** as a default) are used. 1924091Seric ** 1934091Seric ** Parameters: 1944091Seric ** field -- the field name. 1954091Seric ** 1964091Seric ** Returns: 1974091Seric ** pointer to the value part. 1984091Seric ** NULL if not found. 1994091Seric ** 2004091Seric ** Side Effects: 2014091Seric ** sets the H_USED bit in the header if found. 2024091Seric */ 2034091Seric 2044091Seric char * 2054091Seric hvalue(field) 2064091Seric char *field; 2074091Seric { 2084091Seric register HDR *h; 2094091Seric 2106908Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2114091Seric { 2124091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2134091Seric { 2144091Seric h->h_flags |= H_USED; 2154091Seric return (h->h_value); 2164091Seric } 2174091Seric } 2184091Seric return (NULL); 2194091Seric } 2204091Seric /* 2217677Seric ** HRVALUE -- return pointer to header descriptor. 2227677Seric ** 2237677Seric ** Like hvalue except returns header descriptor block and isn't 2247677Seric ** picky about "real" headers. 2257677Seric ** 2267677Seric ** Parameters: 2277677Seric ** field -- name of field we are interested in. 2287677Seric ** 2297677Seric ** Returns: 2307677Seric ** pointer to header descriptor. 2317677Seric ** 2327677Seric ** Side Effects: 2337677Seric ** none. 2347677Seric */ 2357677Seric 2367677Seric HDR * 2377677Seric hrvalue(field) 2387677Seric char *field; 2397677Seric { 2407677Seric register HDR *h; 2417677Seric 2427677Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2437677Seric { 2447677Seric if (strcmp(h->h_field, field) == 0) 2457677Seric return (h); 2467677Seric } 2477677Seric return (NULL); 2487677Seric } 2497677Seric /* 2504091Seric ** ISHEADER -- predicate telling if argument is a header. 2514091Seric ** 2524319Seric ** A line is a header if it has a single word followed by 2534319Seric ** optional white space followed by a colon. 2544319Seric ** 2554091Seric ** Parameters: 2564091Seric ** s -- string to check for possible headerness. 2574091Seric ** 2584091Seric ** Returns: 2594091Seric ** TRUE if s is a header. 2604091Seric ** FALSE otherwise. 2614091Seric ** 2624091Seric ** Side Effects: 2634091Seric ** none. 2644319Seric ** 2654319Seric ** Bugs: 2664319Seric ** According to RFC733, there should be a newline 2674319Seric ** permitted after the word but before the colon. 2684319Seric ** We don't seem to support that..... 2694091Seric */ 2704091Seric 2714091Seric bool 2724091Seric isheader(s) 2734091Seric register char *s; 2744091Seric { 2754091Seric if (!isalnum(*s)) 2764091Seric return (FALSE); 2774091Seric while (!isspace(*s) && *s != ':') 2784091Seric s++; 2794091Seric while (isspace(*s)) 2804091Seric s++; 2814091Seric return (*s == ':'); 2824091Seric } 2835919Seric /* 2845919Seric ** GETXPART -- extract the "signature" part of an address line. 2855919Seric ** 2865919Seric ** Try to extract the full name from a general address 2875919Seric ** field. We take anything which is a comment as a 2885919Seric ** first choice. Failing in that, we see if there is 2895919Seric ** a "machine readable" name (in <angle brackets>); if 2905919Seric ** so we take anything preceeding that clause. 2915919Seric ** 2925919Seric ** If we blow it here it's not all that serious. 2935919Seric ** 2945919Seric ** Parameters: 2955919Seric ** p -- line to crack. 2965919Seric ** 2975919Seric ** Returns: 2985919Seric ** signature part. 2995919Seric ** NULL if no signature part. 3005919Seric ** 3015919Seric ** Side Effects: 3025919Seric ** none. 3035919Seric */ 3045919Seric 3055919Seric char * 3065919Seric getxpart(p) 3075919Seric register char *p; 3085919Seric { 3095919Seric register char *q; 3105919Seric register char *rval = NULL; 3115919Seric 3125919Seric q = index(p, '('); 3135919Seric if (q != NULL) 3145919Seric { 3155919Seric int parenlev = 0; 3165919Seric 3175919Seric for (p = q; *p != '\0'; p++) 3185919Seric { 3195919Seric if (*p == '(') 3205919Seric parenlev++; 3215919Seric else if (*p == ')' && --parenlev <= 0) 3225919Seric break; 3235919Seric } 3245919Seric if (*p == ')') 3255919Seric { 3265919Seric *p = '\0'; 3275919Seric if (*++q != '\0') 3285919Seric rval = newstr(q); 3295919Seric *p = ')'; 3305919Seric } 3315919Seric } 3325919Seric else if ((q = index(p, '<')) != NULL) 3335919Seric { 3345919Seric char savec; 3355919Seric 3365919Seric while (*--q == ' ') 3375919Seric continue; 3385919Seric while (isspace(*p)) 3395919Seric p++; 3405919Seric savec = *++q; 3415919Seric *q = '\0'; 3425919Seric if (*p != '\0') 3435919Seric rval = newstr(p); 3445919Seric *q = savec; 3455919Seric } 3465919Seric 3475919Seric return (rval); 3485919Seric } 3497783Seric /* 3507783Seric ** EATHEADER -- run through the stored header and extract info. 3517783Seric ** 3527783Seric ** Parameters: 3537783Seric ** none. 3547783Seric ** 3557783Seric ** Returns: 3567783Seric ** none. 3577783Seric ** 3587783Seric ** Side Effects: 3597783Seric ** Sets a bunch of global variables from information 3607783Seric ** in the collected header. 3617783Seric */ 3627783Seric 3637783Seric eatheader() 3647783Seric { 3657783Seric register HDR *h; 3667783Seric register char *p; 3677783Seric char buf[MAXLINE]; 3687783Seric 3697783Seric # ifdef DEBUG 3707783Seric if (tTd(32, 2)) 3717783Seric { 3727783Seric extern char *capitalize(); 3737783Seric 3747783Seric printf("----- collected header -----\n"); 3757783Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 3767783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 3777783Seric printf("----------------------------\n"); 3787783Seric } 3797783Seric # endif DEBUG 3807783Seric 3817783Seric /* message id */ 3827783Seric h = hrvalue("message-id"); 3837783Seric if (h == NULL) 3847783Seric syserr("No Message-Id spec"); 3857783Seric else if (bitset(H_DEFAULT, h->h_flags)) 3867783Seric { 3877783Seric (void) expand(h->h_value, buf, &buf[sizeof buf - 1], CurEnv); 3887783Seric MsgId = newstr(buf); 3897783Seric } 3907783Seric else 3917783Seric MsgId = h->h_value; 3927783Seric # ifdef DEBUG 3937783Seric if (tTd(32, 1)) 3947783Seric printf("Message-Id: %s\n", MsgId); 3957783Seric # endif DEBUG 3967783Seric 3977783Seric /* message priority */ 3987783Seric if (!QueueRun) 3997783Seric { 4007783Seric /* adjust total priority by message priority */ 4017783Seric CurEnv->e_msgpriority = CurEnv->e_msgsize; 4027783Seric p = hvalue("priority"); 4037783Seric if (p != NULL) 4047783Seric CurEnv->e_class = priencode(p); 4057783Seric else 4067783Seric CurEnv->e_class = PRI_NORMAL; 4077783Seric CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 4087783Seric } 4097783Seric 4107783Seric /* special handling */ 4117783Seric p = hvalue("special-handling"); 4127783Seric if (p != NULL) 4137783Seric spechandling(p); 4147783Seric 4157783Seric /* from person */ 4167783Seric p = hvalue("sender"); 4177783Seric if (p == NULL) 4187783Seric p = CurEnv->e_origfrom; 4197783Seric if (ArpaMode) 4207783Seric setfrom(p, (char *) NULL); 4217783Seric 4227783Seric /* full name of from person */ 4237783Seric p = hvalue("full-name"); 4247783Seric if (p != NULL) 4257783Seric define('x', p); 4267783Seric 4277783Seric /* date message originated */ 4287783Seric p = hvalue("posted-date"); 4297783Seric if (p == NULL) 4307783Seric p = hvalue("date"); 4317783Seric if (p != NULL) 4327783Seric { 4337783Seric define('a', p); 4347783Seric /* we don't have a good way to do canonical conversion .... 4357783Seric define('d', newstr(arpatounix(p))); 4367783Seric .... so we will ignore the problem for the time being */ 4377783Seric } 4387783Seric } 4397783Seric /* 4407783Seric ** PRIENCODE -- encode external priority names into internal values. 4417783Seric ** 4427783Seric ** Parameters: 4437783Seric ** p -- priority in ascii. 4447783Seric ** 4457783Seric ** Returns: 4467783Seric ** priority as a numeric level. 4477783Seric ** 4487783Seric ** Side Effects: 4497783Seric ** none. 4507783Seric */ 4517783Seric 4527783Seric struct prio 4537783Seric { 4547783Seric char *pri_name; /* external name of priority */ 4557783Seric int pri_val; /* internal value for same */ 4567783Seric }; 4577783Seric 4587783Seric static struct prio Prio[] = 4597783Seric { 4607783Seric "alert", PRI_ALERT, 4617783Seric "quick", PRI_QUICK, 4627783Seric "first-class", PRI_FIRSTCL, 4637783Seric "normal", PRI_NORMAL, 4647783Seric "second-class", PRI_SECONDCL, 4657783Seric "third-class", PRI_THIRDCL, 4667783Seric "junk", PRI_JUNK, 4677783Seric NULL, PRI_NORMAL, 4687783Seric }; 4697783Seric 4707783Seric priencode(p) 4717783Seric char *p; 4727783Seric { 4737783Seric register struct prio *pl; 4747783Seric extern bool sameword(); 4757783Seric 4767783Seric for (pl = Prio; pl->pri_name != NULL; pl++) 4777783Seric { 4787783Seric if (sameword(p, pl->pri_name)) 4797783Seric break; 4807783Seric } 4817783Seric return (pl->pri_val); 4827783Seric } 4837783Seric /* 4847783Seric ** SPECHANDLE -- do special handling 4857783Seric ** 4867783Seric ** Parameters: 4877783Seric ** p -- pointer to list of special handling words. 4887783Seric ** 4897783Seric ** Returns: 4907783Seric ** none. 4917783Seric ** 4927783Seric ** Side Effects: 4937783Seric ** Sets flags as indicated by p. 4947783Seric */ 4957783Seric 4967783Seric struct handling 4977783Seric { 4987783Seric char *han_name; /* word to get this magic */ 4997783Seric int han_what; /* what to do, see below */ 5007783Seric }; 5017783Seric 5027783Seric /* modes for han_what */ 5037783Seric # define HAN_NONE 0 /* nothing special */ 5047783Seric # define HAN_RRECEIPT 1 /* give return receipt */ 5057783Seric 5067783Seric struct handling Handling[] = 5077783Seric { 5087783Seric "return-receipt-requested", HAN_RRECEIPT, 5097783Seric NULL, HAN_NONE 5107783Seric }; 5117783Seric 5127783Seric spechandling(p) 5137783Seric register char *p; 5147783Seric { 5157783Seric register char *w; 5167783Seric register struct handling *h; 5177783Seric extern bool sameword(); 5187783Seric 5197783Seric while (*p != '\0') 5207783Seric { 5217783Seric /* collect a word to compare to */ 5227783Seric while (*p != '\0' && (*p == ',' || isspace(*p))) 5237783Seric p++; 5247783Seric if (*p == '\0') 5257783Seric break; 5267783Seric w = p; 5277783Seric while (*p != '\0' && *p != ',' && !isspace(*p)) 5287783Seric p++; 5297783Seric if (*p != '\0') 5307783Seric *p++ = '\0'; 5317783Seric 5327783Seric /* scan the special handling table */ 5337783Seric for (h = Handling; h->han_name != NULL; h++) 5347783Seric if (sameword(h->han_name, w)) 5357783Seric break; 5367783Seric 5377783Seric /* see if we can do anything interesting */ 5387783Seric switch (h->han_what) 5397783Seric { 5407783Seric case HAN_NONE: /* nothing to be done */ 5417783Seric break; 5427783Seric 5437783Seric case HAN_RRECEIPT: /* give return receipt */ 5447783Seric CurEnv->e_retreceipt = TRUE; 5457783Seric # ifdef DEBUG 5467783Seric if (tTd(30, 3)) 5477783Seric printf(">>> Return receipt requested\n"); 5487783Seric # endif DEBUG 5497783Seric break; 5507783Seric 5517783Seric default: 5527783Seric syserr("spechandling: handling %d (%s)", h->han_what, w); 5537783Seric } 5547783Seric } 5557783Seric } 5567783Seric /* 5577783Seric ** CRACKFROM -- parse the from line and turn it into a macro 5587783Seric ** 5597783Seric ** This doesn't actually parse the address -- it just extracts 5607783Seric ** it and replaces it with "$g". The parse is totally ad hoc 5617783Seric ** and isn't even guaranteed to leave something syntactically 5627783Seric ** identical to what it started with. However, it does leave 5637783Seric ** something semantically identical. 5647783Seric ** 5657783Seric ** The process is kind of strange. There are a number of 5667783Seric ** interesting cases: 5677783Seric ** 1. comment <address> comment ==> comment <$g> comment 5687783Seric ** 2. address ==> address 5697783Seric ** 3. address (comment) ==> $g (comment) 5707783Seric ** 4. (comment) address ==> (comment) $g 5717783Seric ** And then there are the hard cases.... 5727783Seric ** 5. add (comment) ress ==> $g (comment) 5737783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 5747783Seric ** 7. .... etc .... 5757783Seric ** 5767783Seric ** Parameters: 5777783Seric ** from -- the value part of the from line. 5787783Seric ** 5797783Seric ** Returns: 5807783Seric ** a pointer to the new version. 5817783Seric ** 5827783Seric ** Side Effects: 5837783Seric ** The $f and $x macros may be defined. 5847783Seric ** 5857783Seric ** Warning: 5867783Seric ** The return value is saved in local storage and should 5877783Seric ** be copied if it is to be reused. 5887783Seric */ 5897783Seric 5907783Seric char * 5917783Seric crackfrom(from) 5927783Seric register char *from; 5937783Seric { 5947783Seric register char *p; 5957783Seric register int i; 5967783Seric static char buf[MAXNAME]; 5977783Seric char *rhs; 5987783Seric bool gotaddr; 5997783Seric register char *bp; 6007783Seric 6017783Seric # ifdef DEBUG 6027783Seric if (tTd(33, 1)) 6037783Seric printf("crackfrom(%s)\n", from); 6047783Seric # endif DEBUG 6057783Seric 6067783Seric strcpy(buf, ""); 6077783Seric rhs = NULL; 6087783Seric 6097783Seric /* 6107783Seric ** See if we have anything in angle brackets. If so, that is 6117783Seric ** the address part, and the rest is the comment. 6127783Seric */ 6137783Seric 6147783Seric p = index(from, '<'); 6157783Seric if (p != NULL) 6167783Seric { 6177783Seric /* copy the beginning of the from field to the buffer */ 6187783Seric *p = '\0'; 6197783Seric strcpy(buf, from); 6207783Seric strcat(buf, "<"); 6217783Seric *p = '<'; 6227783Seric 6237783Seric /* find the matching right angle bracket */ 6247783Seric from = ++p; 6257783Seric for (i = 0; *p != '\0'; p++) 6267783Seric { 6277783Seric switch (*p) 6287783Seric { 6297783Seric case '<': 6307783Seric i++; 6317783Seric break; 6327783Seric 6337783Seric case '>': 6347783Seric i--; 6357783Seric break; 6367783Seric } 6377783Seric if (i < 0) 6387783Seric break; 6397783Seric } 6407783Seric 6417783Seric /* p now points to the closing quote (or a null byte) */ 6427783Seric if (*p != '\0') 6437783Seric { 6447783Seric /* make rhs point to the extra stuff at the end */ 6457783Seric rhs = p; 6467783Seric *p++ = '\0'; 6477783Seric } 6487783Seric } 6497783Seric 6507783Seric /* 6517783Seric ** Now parse the real address part. from points to the (null 6527783Seric ** terminated) version of what we are inerested in; rhs points 6537783Seric ** to the extra stuff at the end of the line, if any. 6547783Seric */ 6557783Seric 6567783Seric p = from; 6577783Seric 6587783Seric /* now strip out comments */ 6597783Seric bp = &buf[strlen(buf)]; 6607783Seric gotaddr = FALSE; 6617783Seric for (; *p != '\0'; p++) 6627783Seric { 6637783Seric if (*p == '(') 6647783Seric { 6657783Seric /* copy to matching close paren */ 6667783Seric *bp++ = *p++; 6677783Seric for (i = 0; *p != '\0'; p++) 6687783Seric { 6697783Seric *bp++ = *p; 6707783Seric switch (*p) 6717783Seric { 6727783Seric case '(': 6737783Seric i++; 6747783Seric break; 6757783Seric 6767783Seric case ')': 6777783Seric i--; 6787783Seric break; 6797783Seric } 6807783Seric if (i < 0) 6817783Seric break; 6827783Seric } 6837783Seric continue; 6847783Seric } 6857783Seric 6867783Seric /* 6877783Seric ** If this is the first "real" character we have seen, 6887783Seric ** then we put the "$g" in the buffer now. 6897783Seric */ 6907783Seric 6917783Seric if (isspace(*p)) 6927783Seric *bp++ = *p; 6937783Seric else if (!gotaddr) 6947783Seric { 6957783Seric strcpy(bp, "$g"); 6967783Seric bp += 2; 6977783Seric gotaddr = TRUE; 6987783Seric } 6997783Seric } 7007783Seric 7017783Seric /* 7027783Seric ** If there is a tag at the end, insert it. 7037783Seric */ 7047783Seric 7057783Seric *bp = '\0'; 7067783Seric if (rhs != NULL) 7077783Seric { 7087783Seric *rhs = '>'; 7097783Seric strcpy(bp, rhs); 7107783Seric } 7117783Seric 7127783Seric # ifdef DEBUG 7137783Seric if (tTd(33, 1)) 7147783Seric printf("crackfrom=>%s\n", buf); 7157783Seric # endif DEBUG 7167783Seric 7177783Seric return (buf); 7187783Seric } 719