14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*7783Seric SCCSID(@(#)headers.c 3.24 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(); 36*7783Seric 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 977365Seric /* count Mail-From: lines to avoid loops (simulate hop counts) */ 987365Seric if (strcmp(fname, "mail-from") == 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); 119*7783Seric if (!def && strcmp(fname, "from") == 0) 120*7783Seric { 121*7783Seric /* turn it into a macro -- will be expanded later */ 122*7783Seric h->h_value = newstr(crackfrom(fvalue)); 123*7783Seric h->h_flags |= H_DEFAULT; 124*7783Seric } 125*7783Seric else 126*7783Seric 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 } 349*7783Seric /* 350*7783Seric ** EATHEADER -- run through the stored header and extract info. 351*7783Seric ** 352*7783Seric ** Parameters: 353*7783Seric ** none. 354*7783Seric ** 355*7783Seric ** Returns: 356*7783Seric ** none. 357*7783Seric ** 358*7783Seric ** Side Effects: 359*7783Seric ** Sets a bunch of global variables from information 360*7783Seric ** in the collected header. 361*7783Seric */ 362*7783Seric 363*7783Seric eatheader() 364*7783Seric { 365*7783Seric register HDR *h; 366*7783Seric register char *p; 367*7783Seric char buf[MAXLINE]; 368*7783Seric 369*7783Seric # ifdef DEBUG 370*7783Seric if (tTd(32, 2)) 371*7783Seric { 372*7783Seric extern char *capitalize(); 373*7783Seric 374*7783Seric printf("----- collected header -----\n"); 375*7783Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 376*7783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 377*7783Seric printf("----------------------------\n"); 378*7783Seric } 379*7783Seric # endif DEBUG 380*7783Seric 381*7783Seric /* message id */ 382*7783Seric h = hrvalue("message-id"); 383*7783Seric if (h == NULL) 384*7783Seric syserr("No Message-Id spec"); 385*7783Seric else if (bitset(H_DEFAULT, h->h_flags)) 386*7783Seric { 387*7783Seric (void) expand(h->h_value, buf, &buf[sizeof buf - 1], CurEnv); 388*7783Seric MsgId = newstr(buf); 389*7783Seric } 390*7783Seric else 391*7783Seric MsgId = h->h_value; 392*7783Seric # ifdef DEBUG 393*7783Seric if (tTd(32, 1)) 394*7783Seric printf("Message-Id: %s\n", MsgId); 395*7783Seric # endif DEBUG 396*7783Seric 397*7783Seric /* message priority */ 398*7783Seric if (!QueueRun) 399*7783Seric { 400*7783Seric /* adjust total priority by message priority */ 401*7783Seric CurEnv->e_msgpriority = CurEnv->e_msgsize; 402*7783Seric p = hvalue("priority"); 403*7783Seric if (p != NULL) 404*7783Seric CurEnv->e_class = priencode(p); 405*7783Seric else 406*7783Seric CurEnv->e_class = PRI_NORMAL; 407*7783Seric CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 408*7783Seric } 409*7783Seric 410*7783Seric /* special handling */ 411*7783Seric p = hvalue("special-handling"); 412*7783Seric if (p != NULL) 413*7783Seric spechandling(p); 414*7783Seric 415*7783Seric /* from person */ 416*7783Seric p = hvalue("sender"); 417*7783Seric if (p == NULL) 418*7783Seric p = CurEnv->e_origfrom; 419*7783Seric if (ArpaMode) 420*7783Seric setfrom(p, (char *) NULL); 421*7783Seric 422*7783Seric /* full name of from person */ 423*7783Seric p = hvalue("full-name"); 424*7783Seric if (p != NULL) 425*7783Seric define('x', p); 426*7783Seric 427*7783Seric /* date message originated */ 428*7783Seric p = hvalue("posted-date"); 429*7783Seric if (p == NULL) 430*7783Seric p = hvalue("date"); 431*7783Seric if (p != NULL) 432*7783Seric { 433*7783Seric define('a', p); 434*7783Seric /* we don't have a good way to do canonical conversion .... 435*7783Seric define('d', newstr(arpatounix(p))); 436*7783Seric .... so we will ignore the problem for the time being */ 437*7783Seric } 438*7783Seric } 439*7783Seric /* 440*7783Seric ** PRIENCODE -- encode external priority names into internal values. 441*7783Seric ** 442*7783Seric ** Parameters: 443*7783Seric ** p -- priority in ascii. 444*7783Seric ** 445*7783Seric ** Returns: 446*7783Seric ** priority as a numeric level. 447*7783Seric ** 448*7783Seric ** Side Effects: 449*7783Seric ** none. 450*7783Seric */ 451*7783Seric 452*7783Seric struct prio 453*7783Seric { 454*7783Seric char *pri_name; /* external name of priority */ 455*7783Seric int pri_val; /* internal value for same */ 456*7783Seric }; 457*7783Seric 458*7783Seric static struct prio Prio[] = 459*7783Seric { 460*7783Seric "alert", PRI_ALERT, 461*7783Seric "quick", PRI_QUICK, 462*7783Seric "first-class", PRI_FIRSTCL, 463*7783Seric "normal", PRI_NORMAL, 464*7783Seric "second-class", PRI_SECONDCL, 465*7783Seric "third-class", PRI_THIRDCL, 466*7783Seric "junk", PRI_JUNK, 467*7783Seric NULL, PRI_NORMAL, 468*7783Seric }; 469*7783Seric 470*7783Seric priencode(p) 471*7783Seric char *p; 472*7783Seric { 473*7783Seric register struct prio *pl; 474*7783Seric extern bool sameword(); 475*7783Seric 476*7783Seric for (pl = Prio; pl->pri_name != NULL; pl++) 477*7783Seric { 478*7783Seric if (sameword(p, pl->pri_name)) 479*7783Seric break; 480*7783Seric } 481*7783Seric return (pl->pri_val); 482*7783Seric } 483*7783Seric /* 484*7783Seric ** SPECHANDLE -- do special handling 485*7783Seric ** 486*7783Seric ** Parameters: 487*7783Seric ** p -- pointer to list of special handling words. 488*7783Seric ** 489*7783Seric ** Returns: 490*7783Seric ** none. 491*7783Seric ** 492*7783Seric ** Side Effects: 493*7783Seric ** Sets flags as indicated by p. 494*7783Seric */ 495*7783Seric 496*7783Seric struct handling 497*7783Seric { 498*7783Seric char *han_name; /* word to get this magic */ 499*7783Seric int han_what; /* what to do, see below */ 500*7783Seric }; 501*7783Seric 502*7783Seric /* modes for han_what */ 503*7783Seric # define HAN_NONE 0 /* nothing special */ 504*7783Seric # define HAN_RRECEIPT 1 /* give return receipt */ 505*7783Seric 506*7783Seric struct handling Handling[] = 507*7783Seric { 508*7783Seric "return-receipt-requested", HAN_RRECEIPT, 509*7783Seric NULL, HAN_NONE 510*7783Seric }; 511*7783Seric 512*7783Seric spechandling(p) 513*7783Seric register char *p; 514*7783Seric { 515*7783Seric register char *w; 516*7783Seric register struct handling *h; 517*7783Seric extern bool sameword(); 518*7783Seric 519*7783Seric while (*p != '\0') 520*7783Seric { 521*7783Seric /* collect a word to compare to */ 522*7783Seric while (*p != '\0' && (*p == ',' || isspace(*p))) 523*7783Seric p++; 524*7783Seric if (*p == '\0') 525*7783Seric break; 526*7783Seric w = p; 527*7783Seric while (*p != '\0' && *p != ',' && !isspace(*p)) 528*7783Seric p++; 529*7783Seric if (*p != '\0') 530*7783Seric *p++ = '\0'; 531*7783Seric 532*7783Seric /* scan the special handling table */ 533*7783Seric for (h = Handling; h->han_name != NULL; h++) 534*7783Seric if (sameword(h->han_name, w)) 535*7783Seric break; 536*7783Seric 537*7783Seric /* see if we can do anything interesting */ 538*7783Seric switch (h->han_what) 539*7783Seric { 540*7783Seric case HAN_NONE: /* nothing to be done */ 541*7783Seric break; 542*7783Seric 543*7783Seric case HAN_RRECEIPT: /* give return receipt */ 544*7783Seric CurEnv->e_retreceipt = TRUE; 545*7783Seric # ifdef DEBUG 546*7783Seric if (tTd(30, 3)) 547*7783Seric printf(">>> Return receipt requested\n"); 548*7783Seric # endif DEBUG 549*7783Seric break; 550*7783Seric 551*7783Seric default: 552*7783Seric syserr("spechandling: handling %d (%s)", h->han_what, w); 553*7783Seric } 554*7783Seric } 555*7783Seric } 556*7783Seric /* 557*7783Seric ** CRACKFROM -- parse the from line and turn it into a macro 558*7783Seric ** 559*7783Seric ** This doesn't actually parse the address -- it just extracts 560*7783Seric ** it and replaces it with "$g". The parse is totally ad hoc 561*7783Seric ** and isn't even guaranteed to leave something syntactically 562*7783Seric ** identical to what it started with. However, it does leave 563*7783Seric ** something semantically identical. 564*7783Seric ** 565*7783Seric ** The process is kind of strange. There are a number of 566*7783Seric ** interesting cases: 567*7783Seric ** 1. comment <address> comment ==> comment <$g> comment 568*7783Seric ** 2. address ==> address 569*7783Seric ** 3. address (comment) ==> $g (comment) 570*7783Seric ** 4. (comment) address ==> (comment) $g 571*7783Seric ** And then there are the hard cases.... 572*7783Seric ** 5. add (comment) ress ==> $g (comment) 573*7783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 574*7783Seric ** 7. .... etc .... 575*7783Seric ** 576*7783Seric ** Parameters: 577*7783Seric ** from -- the value part of the from line. 578*7783Seric ** 579*7783Seric ** Returns: 580*7783Seric ** a pointer to the new version. 581*7783Seric ** 582*7783Seric ** Side Effects: 583*7783Seric ** The $f and $x macros may be defined. 584*7783Seric ** 585*7783Seric ** Warning: 586*7783Seric ** The return value is saved in local storage and should 587*7783Seric ** be copied if it is to be reused. 588*7783Seric */ 589*7783Seric 590*7783Seric char * 591*7783Seric crackfrom(from) 592*7783Seric register char *from; 593*7783Seric { 594*7783Seric register char *p; 595*7783Seric register int i; 596*7783Seric static char buf[MAXNAME]; 597*7783Seric char *rhs; 598*7783Seric bool gotaddr; 599*7783Seric register char *bp; 600*7783Seric 601*7783Seric # ifdef DEBUG 602*7783Seric if (tTd(33, 1)) 603*7783Seric printf("crackfrom(%s)\n", from); 604*7783Seric # endif DEBUG 605*7783Seric 606*7783Seric strcpy(buf, ""); 607*7783Seric rhs = NULL; 608*7783Seric 609*7783Seric /* 610*7783Seric ** See if we have anything in angle brackets. If so, that is 611*7783Seric ** the address part, and the rest is the comment. 612*7783Seric */ 613*7783Seric 614*7783Seric p = index(from, '<'); 615*7783Seric if (p != NULL) 616*7783Seric { 617*7783Seric /* copy the beginning of the from field to the buffer */ 618*7783Seric *p = '\0'; 619*7783Seric strcpy(buf, from); 620*7783Seric strcat(buf, "<"); 621*7783Seric *p = '<'; 622*7783Seric 623*7783Seric /* find the matching right angle bracket */ 624*7783Seric from = ++p; 625*7783Seric for (i = 0; *p != '\0'; p++) 626*7783Seric { 627*7783Seric switch (*p) 628*7783Seric { 629*7783Seric case '<': 630*7783Seric i++; 631*7783Seric break; 632*7783Seric 633*7783Seric case '>': 634*7783Seric i--; 635*7783Seric break; 636*7783Seric } 637*7783Seric if (i < 0) 638*7783Seric break; 639*7783Seric } 640*7783Seric 641*7783Seric /* p now points to the closing quote (or a null byte) */ 642*7783Seric if (*p != '\0') 643*7783Seric { 644*7783Seric /* make rhs point to the extra stuff at the end */ 645*7783Seric rhs = p; 646*7783Seric *p++ = '\0'; 647*7783Seric } 648*7783Seric } 649*7783Seric 650*7783Seric /* 651*7783Seric ** Now parse the real address part. from points to the (null 652*7783Seric ** terminated) version of what we are inerested in; rhs points 653*7783Seric ** to the extra stuff at the end of the line, if any. 654*7783Seric */ 655*7783Seric 656*7783Seric p = from; 657*7783Seric 658*7783Seric /* now strip out comments */ 659*7783Seric bp = &buf[strlen(buf)]; 660*7783Seric gotaddr = FALSE; 661*7783Seric for (; *p != '\0'; p++) 662*7783Seric { 663*7783Seric if (*p == '(') 664*7783Seric { 665*7783Seric /* copy to matching close paren */ 666*7783Seric *bp++ = *p++; 667*7783Seric for (i = 0; *p != '\0'; p++) 668*7783Seric { 669*7783Seric *bp++ = *p; 670*7783Seric switch (*p) 671*7783Seric { 672*7783Seric case '(': 673*7783Seric i++; 674*7783Seric break; 675*7783Seric 676*7783Seric case ')': 677*7783Seric i--; 678*7783Seric break; 679*7783Seric } 680*7783Seric if (i < 0) 681*7783Seric break; 682*7783Seric } 683*7783Seric continue; 684*7783Seric } 685*7783Seric 686*7783Seric /* 687*7783Seric ** If this is the first "real" character we have seen, 688*7783Seric ** then we put the "$g" in the buffer now. 689*7783Seric */ 690*7783Seric 691*7783Seric if (isspace(*p)) 692*7783Seric *bp++ = *p; 693*7783Seric else if (!gotaddr) 694*7783Seric { 695*7783Seric strcpy(bp, "$g"); 696*7783Seric bp += 2; 697*7783Seric gotaddr = TRUE; 698*7783Seric } 699*7783Seric } 700*7783Seric 701*7783Seric /* 702*7783Seric ** If there is a tag at the end, insert it. 703*7783Seric */ 704*7783Seric 705*7783Seric *bp = '\0'; 706*7783Seric if (rhs != NULL) 707*7783Seric { 708*7783Seric *rhs = '>'; 709*7783Seric strcpy(bp, rhs); 710*7783Seric } 711*7783Seric 712*7783Seric # ifdef DEBUG 713*7783Seric if (tTd(33, 1)) 714*7783Seric printf("crackfrom=>%s\n", buf); 715*7783Seric # endif DEBUG 716*7783Seric 717*7783Seric return (buf); 718*7783Seric } 719