14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*7890Seric SCCSID(@(#)headers.c 3.29 08/25/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 char *fname; 314091Seric char *fvalue; 324091Seric struct hdrinfo *hi; 334627Seric u_long mopts; 344627Seric extern u_long mfencode(); 35*7890Seric extern char *crackaddr(); 364091Seric 377677Seric # ifdef DEBUG 387677Seric if (tTd(31, 6)) 397677Seric printf("chompheader: %s\n", line); 407677Seric # endif DEBUG 417677Seric 424627Seric /* strip off options */ 434627Seric mopts = 0; 444627Seric p = line; 454627Seric if (*p == '?') 464627Seric { 474627Seric /* have some */ 484627Seric register char *q = index(p + 1, *p); 494627Seric 504627Seric if (q != NULL) 514627Seric { 524627Seric *q++ = '\0'; 534627Seric mopts = mfencode(p + 1); 544627Seric p = q; 554627Seric } 564627Seric else 574627Seric syserr("chompheader: syntax error, line \"%s\"", line); 584627Seric } 594627Seric 604091Seric /* find canonical name */ 614627Seric fname = p; 624627Seric p = index(p, ':'); 634091Seric fvalue = &p[1]; 644091Seric while (isspace(*--p)) 654091Seric continue; 664091Seric *++p = '\0'; 674091Seric makelower(fname); 684091Seric 694091Seric /* strip field value on front */ 704091Seric if (*fvalue == ' ') 714091Seric fvalue++; 724091Seric 734091Seric /* search header list for this header */ 746908Seric for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; hp = &h->h_link, h = h->h_link) 754091Seric { 765187Seric if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 774091Seric break; 784091Seric } 794091Seric 804091Seric /* see if it is a known type */ 814091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 824091Seric { 834091Seric if (strcmp(hi->hi_field, fname) == 0) 844091Seric break; 854091Seric } 864091Seric 874091Seric /* if this means "end of header" quit now */ 884091Seric if (bitset(H_EOH, hi->hi_flags)) 894091Seric return (hi->hi_flags); 904091Seric 917789Seric /* count Received: lines to avoid loops (simulate hop counts) */ 927789Seric if (strcmp(fname, "received") == 0) 937365Seric HopCount++; 947365Seric 954091Seric /* create/fill in a new node */ 965187Seric if (h == NULL || bitset(H_FORCE, h->h_flags)) 974091Seric { 984091Seric /* create a new node */ 995187Seric h = (HDR *) xalloc(sizeof *h); 1004091Seric h->h_field = newstr(fname); 1014091Seric h->h_value = NULL; 1025187Seric h->h_link = *hp; 1034091Seric h->h_flags = hi->hi_flags; 1044627Seric h->h_mflags = mopts | hi->hi_mflags; 1055187Seric *hp = h; 1064091Seric } 1074091Seric if (def) 1084091Seric h->h_flags |= H_DEFAULT; 1094627Seric else if (mopts == 0) 1104091Seric h->h_flags &= ~H_CHECK; 1114091Seric if (h->h_value != NULL) 1124091Seric free(h->h_value); 1137783Seric if (!def && strcmp(fname, "from") == 0) 1147783Seric { 1157783Seric /* turn it into a macro -- will be expanded later */ 116*7890Seric h->h_value = newstr(crackaddr(fvalue)); 1177783Seric h->h_flags |= H_DEFAULT; 1187783Seric } 1197783Seric else 1207783Seric h->h_value = newstr(fvalue); 1215919Seric if (!def && GrabTo && bitset(H_RCPT, h->h_flags)) 1226908Seric sendto(h->h_value, 0, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1234091Seric 1245937Seric /* hack to see if this is a new format message */ 1255937Seric if (bitset(H_RCPT, h->h_flags) && 1265937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1275937Seric index(fvalue, '<') != NULL)) 1286908Seric CurEnv->e_oldstyle = FALSE; 1295937Seric 1304091Seric return (h->h_flags); 1314091Seric } 1324091Seric /* 1336980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1346980Seric ** 1356980Seric ** This bypasses the special checking of chompheader. 1366980Seric ** 1376980Seric ** Parameters: 1386980Seric ** field -- the name of the header field. 1396980Seric ** value -- the value of the field. It must be lower-cased. 1406980Seric ** e -- the envelope to add them to. 1416980Seric ** 1426980Seric ** Returns: 1436980Seric ** none. 1446980Seric ** 1456980Seric ** Side Effects: 1466980Seric ** adds the field on the list of headers for this envelope. 1476980Seric */ 1486980Seric 1496980Seric addheader(field, value, e) 1506980Seric char *field; 1516980Seric char *value; 1526980Seric ENVELOPE *e; 1536980Seric { 1546980Seric register HDR *h; 1556980Seric register struct hdrinfo *hi; 1566980Seric HDR **hp; 1576980Seric 1586980Seric /* find info struct */ 1596980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1606980Seric { 1616980Seric if (strcmp(field, hi->hi_field) == 0) 1626980Seric break; 1636980Seric } 1646980Seric 1656980Seric /* find current place in list -- keep back pointer? */ 1666980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1676980Seric { 1686980Seric if (strcmp(field, h->h_field) == 0) 1696980Seric break; 1706980Seric } 1716980Seric 1726980Seric /* allocate space for new header */ 1736980Seric h = (HDR *) xalloc(sizeof *h); 1746980Seric h->h_field = field; 1756980Seric h->h_value = newstr(value); 1767368Seric h->h_link = *hp; 1776980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 1786980Seric h->h_mflags = hi->hi_mflags; 1796980Seric *hp = h; 1806980Seric } 1816980Seric /* 1824091Seric ** HVALUE -- return value of a header. 1834091Seric ** 1844091Seric ** Only "real" fields (i.e., ones that have not been supplied 1854091Seric ** as a default) are used. 1864091Seric ** 1874091Seric ** Parameters: 1884091Seric ** field -- the field name. 1894091Seric ** 1904091Seric ** Returns: 1914091Seric ** pointer to the value part. 1924091Seric ** NULL if not found. 1934091Seric ** 1944091Seric ** Side Effects: 1954091Seric ** sets the H_USED bit in the header if found. 1964091Seric */ 1974091Seric 1984091Seric char * 1994091Seric hvalue(field) 2004091Seric char *field; 2014091Seric { 2024091Seric register HDR *h; 2034091Seric 2046908Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2054091Seric { 2064091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2074091Seric { 2084091Seric h->h_flags |= H_USED; 2094091Seric return (h->h_value); 2104091Seric } 2114091Seric } 2124091Seric return (NULL); 2134091Seric } 2144091Seric /* 2157677Seric ** HRVALUE -- return pointer to header descriptor. 2167677Seric ** 2177677Seric ** Like hvalue except returns header descriptor block and isn't 2187677Seric ** picky about "real" headers. 2197677Seric ** 2207677Seric ** Parameters: 2217677Seric ** field -- name of field we are interested in. 2227677Seric ** 2237677Seric ** Returns: 2247677Seric ** pointer to header descriptor. 2257677Seric ** 2267677Seric ** Side Effects: 2277677Seric ** none. 2287677Seric */ 2297677Seric 2307677Seric HDR * 2317677Seric hrvalue(field) 2327677Seric char *field; 2337677Seric { 2347677Seric register HDR *h; 2357677Seric 2367677Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2377677Seric { 2387677Seric if (strcmp(h->h_field, field) == 0) 2397677Seric return (h); 2407677Seric } 2417677Seric return (NULL); 2427677Seric } 2437677Seric /* 2444091Seric ** ISHEADER -- predicate telling if argument is a header. 2454091Seric ** 2464319Seric ** A line is a header if it has a single word followed by 2474319Seric ** optional white space followed by a colon. 2484319Seric ** 2494091Seric ** Parameters: 2504091Seric ** s -- string to check for possible headerness. 2514091Seric ** 2524091Seric ** Returns: 2534091Seric ** TRUE if s is a header. 2544091Seric ** FALSE otherwise. 2554091Seric ** 2564091Seric ** Side Effects: 2574091Seric ** none. 2584091Seric */ 2594091Seric 2604091Seric bool 2614091Seric isheader(s) 2624091Seric register char *s; 2634091Seric { 2644091Seric if (!isalnum(*s)) 2654091Seric return (FALSE); 2667855Seric while (!isspace(*s) && *s != ':' && *s != '\0') 2674091Seric s++; 2684091Seric while (isspace(*s)) 2694091Seric s++; 2704091Seric return (*s == ':'); 2714091Seric } 2725919Seric /* 2735919Seric ** GETXPART -- extract the "signature" part of an address line. 2745919Seric ** 2755919Seric ** Try to extract the full name from a general address 2765919Seric ** field. We take anything which is a comment as a 2775919Seric ** first choice. Failing in that, we see if there is 2785919Seric ** a "machine readable" name (in <angle brackets>); if 2795919Seric ** so we take anything preceeding that clause. 2805919Seric ** 2815919Seric ** If we blow it here it's not all that serious. 2825919Seric ** 2835919Seric ** Parameters: 2845919Seric ** p -- line to crack. 2855919Seric ** 2865919Seric ** Returns: 2875919Seric ** signature part. 2885919Seric ** NULL if no signature part. 2895919Seric ** 2905919Seric ** Side Effects: 2915919Seric ** none. 2925919Seric */ 2935919Seric 2945919Seric char * 2955919Seric getxpart(p) 2965919Seric register char *p; 2975919Seric { 2985919Seric register char *q; 2995919Seric register char *rval = NULL; 3005919Seric 3015919Seric q = index(p, '('); 3025919Seric if (q != NULL) 3035919Seric { 3045919Seric int parenlev = 0; 3055919Seric 3065919Seric for (p = q; *p != '\0'; p++) 3075919Seric { 3085919Seric if (*p == '(') 3095919Seric parenlev++; 3105919Seric else if (*p == ')' && --parenlev <= 0) 3115919Seric break; 3125919Seric } 3135919Seric if (*p == ')') 3145919Seric { 3155919Seric *p = '\0'; 3165919Seric if (*++q != '\0') 3175919Seric rval = newstr(q); 3185919Seric *p = ')'; 3195919Seric } 3205919Seric } 3215919Seric else if ((q = index(p, '<')) != NULL) 3225919Seric { 3235919Seric char savec; 3245919Seric 3255919Seric while (*--q == ' ') 3265919Seric continue; 3275919Seric while (isspace(*p)) 3285919Seric p++; 3295919Seric savec = *++q; 3305919Seric *q = '\0'; 3315919Seric if (*p != '\0') 3325919Seric rval = newstr(p); 3335919Seric *q = savec; 3345919Seric } 3355919Seric 3365919Seric return (rval); 3375919Seric } 3387783Seric /* 3397783Seric ** EATHEADER -- run through the stored header and extract info. 3407783Seric ** 3417783Seric ** Parameters: 3427783Seric ** none. 3437783Seric ** 3447783Seric ** Returns: 3457783Seric ** none. 3467783Seric ** 3477783Seric ** Side Effects: 3487783Seric ** Sets a bunch of global variables from information 3497783Seric ** in the collected header. 3507783Seric */ 3517783Seric 3527783Seric eatheader() 3537783Seric { 3547783Seric register HDR *h; 3557783Seric register char *p; 3567783Seric char buf[MAXLINE]; 3577783Seric 3587783Seric # ifdef DEBUG 3597783Seric if (tTd(32, 2)) 3607783Seric { 3617783Seric extern char *capitalize(); 3627783Seric 3637783Seric printf("----- collected header -----\n"); 3647783Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 3657783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 3667783Seric printf("----------------------------\n"); 3677783Seric } 3687783Seric # endif DEBUG 3697783Seric 3707783Seric /* message priority */ 3717783Seric if (!QueueRun) 3727783Seric { 3737783Seric /* adjust total priority by message priority */ 3747783Seric CurEnv->e_msgpriority = CurEnv->e_msgsize; 3757783Seric p = hvalue("priority"); 3767783Seric if (p != NULL) 3777783Seric CurEnv->e_class = priencode(p); 3787783Seric else 3797783Seric CurEnv->e_class = PRI_NORMAL; 3807783Seric CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 3817783Seric } 3827783Seric 3837783Seric /* special handling */ 3847783Seric p = hvalue("special-handling"); 3857783Seric if (p != NULL) 3867783Seric spechandling(p); 3877783Seric 3887783Seric /* from person */ 3897783Seric p = hvalue("sender"); 3907783Seric if (p == NULL) 3917783Seric p = CurEnv->e_origfrom; 3927783Seric if (ArpaMode) 3937783Seric setfrom(p, (char *) NULL); 3947783Seric 3957783Seric /* full name of from person */ 3967783Seric p = hvalue("full-name"); 3977783Seric if (p != NULL) 3987783Seric define('x', p); 3997783Seric 4007783Seric /* date message originated */ 4017783Seric p = hvalue("posted-date"); 4027783Seric if (p == NULL) 4037783Seric p = hvalue("date"); 4047783Seric if (p != NULL) 4057783Seric { 4067783Seric define('a', p); 4077783Seric /* we don't have a good way to do canonical conversion .... 4087783Seric define('d', newstr(arpatounix(p))); 4097783Seric .... so we will ignore the problem for the time being */ 4107783Seric } 4117783Seric } 4127783Seric /* 4137783Seric ** PRIENCODE -- encode external priority names into internal values. 4147783Seric ** 4157783Seric ** Parameters: 4167783Seric ** p -- priority in ascii. 4177783Seric ** 4187783Seric ** Returns: 4197783Seric ** priority as a numeric level. 4207783Seric ** 4217783Seric ** Side Effects: 4227783Seric ** none. 4237783Seric */ 4247783Seric 4257783Seric struct prio 4267783Seric { 4277783Seric char *pri_name; /* external name of priority */ 4287783Seric int pri_val; /* internal value for same */ 4297783Seric }; 4307783Seric 4317783Seric static struct prio Prio[] = 4327783Seric { 4337783Seric "alert", PRI_ALERT, 4347783Seric "quick", PRI_QUICK, 4357783Seric "first-class", PRI_FIRSTCL, 4367783Seric "normal", PRI_NORMAL, 4377783Seric "second-class", PRI_SECONDCL, 4387783Seric "third-class", PRI_THIRDCL, 4397783Seric "junk", PRI_JUNK, 4407783Seric NULL, PRI_NORMAL, 4417783Seric }; 4427783Seric 4437783Seric priencode(p) 4447783Seric char *p; 4457783Seric { 4467783Seric register struct prio *pl; 4477783Seric extern bool sameword(); 4487783Seric 4497783Seric for (pl = Prio; pl->pri_name != NULL; pl++) 4507783Seric { 4517783Seric if (sameword(p, pl->pri_name)) 4527783Seric break; 4537783Seric } 4547783Seric return (pl->pri_val); 4557783Seric } 4567783Seric /* 4577783Seric ** SPECHANDLE -- do special handling 4587783Seric ** 4597783Seric ** Parameters: 4607783Seric ** p -- pointer to list of special handling words. 4617783Seric ** 4627783Seric ** Returns: 4637783Seric ** none. 4647783Seric ** 4657783Seric ** Side Effects: 4667783Seric ** Sets flags as indicated by p. 4677783Seric */ 4687783Seric 4697783Seric struct handling 4707783Seric { 4717783Seric char *han_name; /* word to get this magic */ 4727783Seric int han_what; /* what to do, see below */ 4737783Seric }; 4747783Seric 4757783Seric /* modes for han_what */ 4767783Seric # define HAN_NONE 0 /* nothing special */ 4777783Seric # define HAN_RRECEIPT 1 /* give return receipt */ 4787783Seric 4797783Seric struct handling Handling[] = 4807783Seric { 4817783Seric "return-receipt-requested", HAN_RRECEIPT, 4827783Seric NULL, HAN_NONE 4837783Seric }; 4847783Seric 4857783Seric spechandling(p) 4867783Seric register char *p; 4877783Seric { 4887783Seric register char *w; 4897783Seric register struct handling *h; 4907783Seric extern bool sameword(); 4917783Seric 4927783Seric while (*p != '\0') 4937783Seric { 4947783Seric /* collect a word to compare to */ 4957783Seric while (*p != '\0' && (*p == ',' || isspace(*p))) 4967783Seric p++; 4977783Seric if (*p == '\0') 4987783Seric break; 4997783Seric w = p; 5007783Seric while (*p != '\0' && *p != ',' && !isspace(*p)) 5017783Seric p++; 5027783Seric if (*p != '\0') 5037783Seric *p++ = '\0'; 5047783Seric 5057783Seric /* scan the special handling table */ 5067783Seric for (h = Handling; h->han_name != NULL; h++) 5077783Seric if (sameword(h->han_name, w)) 5087783Seric break; 5097783Seric 5107783Seric /* see if we can do anything interesting */ 5117783Seric switch (h->han_what) 5127783Seric { 5137783Seric case HAN_NONE: /* nothing to be done */ 5147783Seric break; 5157783Seric 5167783Seric case HAN_RRECEIPT: /* give return receipt */ 5177783Seric CurEnv->e_retreceipt = TRUE; 5187783Seric # ifdef DEBUG 5197783Seric if (tTd(30, 3)) 5207783Seric printf(">>> Return receipt requested\n"); 5217783Seric # endif DEBUG 5227783Seric break; 5237783Seric 5247783Seric default: 5257783Seric syserr("spechandling: handling %d (%s)", h->han_what, w); 5267783Seric } 5277783Seric } 5287783Seric } 5297783Seric /* 530*7890Seric ** CRACKADDR -- parse an address and turn it into a macro 5317783Seric ** 5327783Seric ** This doesn't actually parse the address -- it just extracts 5337783Seric ** it and replaces it with "$g". The parse is totally ad hoc 5347783Seric ** and isn't even guaranteed to leave something syntactically 5357783Seric ** identical to what it started with. However, it does leave 5367783Seric ** something semantically identical. 5377783Seric ** 5387783Seric ** The process is kind of strange. There are a number of 5397783Seric ** interesting cases: 5407783Seric ** 1. comment <address> comment ==> comment <$g> comment 5417783Seric ** 2. address ==> address 5427783Seric ** 3. address (comment) ==> $g (comment) 5437783Seric ** 4. (comment) address ==> (comment) $g 5447783Seric ** And then there are the hard cases.... 5457783Seric ** 5. add (comment) ress ==> $g (comment) 5467783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 5477783Seric ** 7. .... etc .... 5487783Seric ** 5497783Seric ** Parameters: 550*7890Seric ** addr -- the address to be cracked. 5517783Seric ** 5527783Seric ** Returns: 5537783Seric ** a pointer to the new version. 5547783Seric ** 5557783Seric ** Side Effects: 556*7890Seric ** none. 5577783Seric ** 5587783Seric ** Warning: 5597783Seric ** The return value is saved in local storage and should 5607783Seric ** be copied if it is to be reused. 5617783Seric */ 5627783Seric 5637783Seric char * 564*7890Seric crackaddr(addr) 565*7890Seric register char *addr; 5667783Seric { 5677783Seric register char *p; 5687783Seric register int i; 5697783Seric static char buf[MAXNAME]; 5707783Seric char *rhs; 5717783Seric bool gotaddr; 5727783Seric register char *bp; 5737783Seric 5747783Seric # ifdef DEBUG 5757783Seric if (tTd(33, 1)) 576*7890Seric printf("crackaddr(%s)\n", addr); 5777783Seric # endif DEBUG 5787783Seric 5797783Seric strcpy(buf, ""); 5807783Seric rhs = NULL; 5817783Seric 5827783Seric /* 5837783Seric ** See if we have anything in angle brackets. If so, that is 5847783Seric ** the address part, and the rest is the comment. 5857783Seric */ 5867783Seric 587*7890Seric p = index(addr, '<'); 5887783Seric if (p != NULL) 5897783Seric { 590*7890Seric /* copy the beginning of the addr field to the buffer */ 5917783Seric *p = '\0'; 592*7890Seric strcpy(buf, addr); 5937783Seric strcat(buf, "<"); 5947783Seric *p = '<'; 5957783Seric 5967783Seric /* find the matching right angle bracket */ 597*7890Seric addr = ++p; 5987783Seric for (i = 0; *p != '\0'; p++) 5997783Seric { 6007783Seric switch (*p) 6017783Seric { 6027783Seric case '<': 6037783Seric i++; 6047783Seric break; 6057783Seric 6067783Seric case '>': 6077783Seric i--; 6087783Seric break; 6097783Seric } 6107783Seric if (i < 0) 6117783Seric break; 6127783Seric } 6137783Seric 6147783Seric /* p now points to the closing quote (or a null byte) */ 6157783Seric if (*p != '\0') 6167783Seric { 6177783Seric /* make rhs point to the extra stuff at the end */ 6187783Seric rhs = p; 6197783Seric *p++ = '\0'; 6207783Seric } 6217783Seric } 6227783Seric 6237783Seric /* 6247783Seric ** Now parse the real address part. from points to the (null 6257783Seric ** terminated) version of what we are inerested in; rhs points 6267783Seric ** to the extra stuff at the end of the line, if any. 6277783Seric */ 6287783Seric 629*7890Seric p = addr; 6307783Seric 6317783Seric /* now strip out comments */ 6327783Seric bp = &buf[strlen(buf)]; 6337783Seric gotaddr = FALSE; 6347783Seric for (; *p != '\0'; p++) 6357783Seric { 6367783Seric if (*p == '(') 6377783Seric { 6387783Seric /* copy to matching close paren */ 6397783Seric *bp++ = *p++; 6407783Seric for (i = 0; *p != '\0'; p++) 6417783Seric { 6427783Seric *bp++ = *p; 6437783Seric switch (*p) 6447783Seric { 6457783Seric case '(': 6467783Seric i++; 6477783Seric break; 6487783Seric 6497783Seric case ')': 6507783Seric i--; 6517783Seric break; 6527783Seric } 6537783Seric if (i < 0) 6547783Seric break; 6557783Seric } 6567783Seric continue; 6577783Seric } 6587783Seric 6597783Seric /* 6607783Seric ** If this is the first "real" character we have seen, 6617783Seric ** then we put the "$g" in the buffer now. 6627783Seric */ 6637783Seric 6647783Seric if (isspace(*p)) 6657783Seric *bp++ = *p; 6667783Seric else if (!gotaddr) 6677783Seric { 6687783Seric strcpy(bp, "$g"); 6697783Seric bp += 2; 6707783Seric gotaddr = TRUE; 6717783Seric } 6727783Seric } 6737783Seric 6747783Seric /* 6757783Seric ** If there is a tag at the end, insert it. 6767783Seric */ 6777783Seric 6787783Seric *bp = '\0'; 6797783Seric if (rhs != NULL) 6807783Seric { 6817783Seric *rhs = '>'; 6827783Seric strcpy(bp, rhs); 6837783Seric } 6847783Seric 6857783Seric # ifdef DEBUG 6867783Seric if (tTd(33, 1)) 687*7890Seric printf("crackaddr=>%s\n", buf); 6887783Seric # endif DEBUG 6897783Seric 6907783Seric return (buf); 6917783Seric } 692