1 /* intrp.c
2  */
3 /* This software is copyrighted as detailed in the LICENSE file. */
4 
5 
6 #include "EXTERN.h"
7 #include "common.h"
8 #include "list.h"
9 #include "env.h"
10 #include "util.h"
11 #include "util2.h"
12 #include "search.h"
13 #include "hash.h"
14 #include "cache.h"
15 #include "bits.h"
16 #include "head.h"
17 #include "trn.h"
18 #include "ngdata.h"
19 #include "nntpclient.h"
20 #include "datasrc.h"
21 #include "nntp.h"
22 #include "artsrch.h"
23 #include "ng.h"
24 #include "respond.h"
25 #include "rcstuff.h"
26 #include "artio.h"
27 #include "init.h"
28 #include "term.h"
29 #include "final.h"
30 #include "rthread.h"
31 #include "rt-select.h"
32 #include "rt-util.h"
33 #include "INTERN.h"
34 #include "intrp.h"
35 #include "intrp.ih"
36 #include <netdb.h>
37 
38 static char* regexp_specials = "^$.*[\\/?%";
39 
40 char orgname[] = ORGNAME;
41 
42 #ifdef HAS_UNAME
43 #include <sys/utsname.h>
44 struct utsname utsn;
45 #endif
46 
47 COMPEX cond_compex;
48 
49 void
intrp_init(tcbuf,tcbuf_len)50 intrp_init(tcbuf, tcbuf_len)
51 char* tcbuf;
52 int tcbuf_len;
53 {
54 #if HOSTBITS != 0
55     int i;
56 #endif
57 
58     init_compex(&cond_compex);
59 
60     /* get environmental stuff */
61 
62 #ifdef NEWS_ADMIN
63     {
64 #ifdef HAS_GETPWENT
65 	struct passwd* pwd = getpwnam(NEWS_ADMIN);
66 
67 	if (pwd != NULL)
68 	    newsuid = pwd->pw_uid;
69 #else
70 #ifdef TILDENAME
71 	char tildenews[2+sizeof NEWS_ADMIN];
72 	strcpy(tildenews, "~");
73 	strcat(tildenews, NEWS_ADMIN);
74 	(void) filexp(tildenews);
75 #else
76 	... "Define either HAS_GETPWENT or TILDENAME to get NEWS_ADMIN"
77 #endif  /* TILDENAME */
78 #endif	/* HAS_GETPWENT */
79     }
80 
81     /* if this is the news admin then load his UID into newsuid */
82 
83     if (strEQ(loginName,NEWS_ADMIN))
84 	newsuid = getuid();
85 #endif
86 
87     if (checkflag)			/* that getwd below takes ~1/3 sec. */
88 	return;				/* and we do not need it for -c */
89     trn_getwd(tcbuf, tcbuf_len);	/* find working directory name */
90     origdir = savestr(tcbuf);		/* and remember it */
91 
92     /* name of header file (%h) */
93 
94     headname = savestr(filexp(HEADNAME));
95 
96     /* the hostname to use in local-article comparisons */
97 #if HOSTBITS != 0
98     i = (HOSTBITS < 2? 2 : HOSTBITS);
99     hostname = phostname+strlen(phostname)-1;
100     while (i && hostname != phostname) {
101 	if (*--hostname == '.')
102 	    i--;
103     }
104     if (*hostname == '.')
105 	hostname++;
106 #else
107     hostname = phostname;
108 #endif
109 }
110 
111 /* skip interpolations */
112 
113 static char*
skipinterp(pattern,stoppers)114 skipinterp(pattern,stoppers)
115 register char* pattern;
116 char* stoppers;
117 {
118 #ifdef DEBUG
119     if (debug & DEB_INTRP)
120 	printf("skipinterp %s (till %s)\n",pattern,stoppers?stoppers:"");
121 #endif
122 
123     while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
124 	if (*pattern == '%' && pattern[1]) {
125 	switch_again:
126 	    switch (*++pattern) {
127 	    case '^':
128 	    case '_':
129 	    case '\\':
130 	    case '\'':
131 	    case '>':
132 	    case ')':
133 		goto switch_again;
134 	    case ':':
135 		pattern++;
136 		while (*pattern
137 		 && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) {
138 		    pattern++;
139 		}
140 		pattern--;
141 		goto switch_again;
142 	    case '{':
143 		for (pattern++; *pattern && *pattern != '}'; pattern++)
144 		    if (*pattern == '\\')
145 			pattern++;
146 		break;
147 	    case '[':
148 		for (pattern++; *pattern && *pattern != ']'; pattern++)
149 		    if (*pattern == '\\')
150 			pattern++;
151 		break;
152 	    case '(': {
153 		pattern = skipinterp(pattern+1,"!=");
154 		if (!*pattern)
155 		    goto getout;
156 		for (pattern++; *pattern && *pattern != '?'; pattern++)
157 		    if (*pattern == '\\')
158 			pattern++;
159 		if (!*pattern)
160 		    goto getout;
161 		pattern = skipinterp(pattern+1,":)");
162 		if (*pattern == ':')
163 		    pattern = skipinterp(pattern+1,")");
164 		break;
165 	    }
166 #ifdef BACKTICK
167 	    case '`': {
168 		pattern = skipinterp(pattern+1,"`");
169 		break;
170 	    }
171 #endif
172 #ifdef PROMPTTTY
173 	    case '"':
174 		pattern = skipinterp(pattern+1,"\"");
175 		break;
176 #endif
177 	    default:
178 		break;
179 	    }
180 	    pattern++;
181 	}
182 	else {
183 	    if (*pattern == '^'
184 	     && ((Uchar)pattern[1]>='?' || pattern[1]=='(' || pattern[1]==')'))
185 		pattern += 2;
186 	    else if (*pattern == '\\' && pattern[1])
187 		pattern += 2;
188 	    else
189 		pattern++;
190 	}
191     }
192 getout:
193     return pattern;			/* where we left off */
194 }
195 
196 /* interpret interpolations */
197 
198 char*
dointerp(dest,destsize,pattern,stoppers,cmd)199 dointerp(dest,destsize,pattern,stoppers,cmd)
200 register char* dest;
201 register int destsize;
202 register char* pattern;
203 char* stoppers;
204 char* cmd;
205 {
206     char* subj_buf = NULL;
207     char* ngs_buf = NULL;
208     char* refs_buf = NULL;
209     char* artid_buf = NULL;
210     char* reply_buf = NULL;
211     char* from_buf = NULL;
212     char* path_buf = NULL;
213     char* follow_buf = NULL;
214     char* dist_buf = NULL;
215     char* line_buf = NULL;
216     char* line_split = NULL;
217     char* orig_dest = dest;
218     register char* s;
219     register char* h;
220     register int i;
221     char scrbuf[8192];
222     char spfbuf[512];
223     static char* input_str = NULL;
224     static int input_siz = 0;
225     bool upper = FALSE;
226     bool lastcomp = FALSE;
227     bool re_quote = FALSE;
228     int tick_quote = 0;
229     bool address_parse = FALSE;
230     bool comment_parse = FALSE;
231     bool proc_sprintf = FALSE;
232     int metabit = 0;
233 
234 #ifdef DEBUG
235     if (debug & DEB_INTRP)
236 	printf(">dointerp: %s (till %s)\n",pattern,stoppers?stoppers:"");
237 #endif
238 
239     while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
240 	if (*pattern == '%' && pattern[1]) {
241 	    upper = FALSE;
242 	    lastcomp = FALSE;
243 	    re_quote = FALSE;
244 	    tick_quote = 0;
245 	    address_parse = FALSE;
246 	    comment_parse = FALSE;
247 	    proc_sprintf = FALSE;
248 	    for (s=NULL; !s; ) {
249 		switch (*++pattern) {
250 		case '^':
251 		    upper = TRUE;
252 		    break;
253 		case '_':
254 		    lastcomp = TRUE;
255 		    break;
256 		case '\\':
257 		    re_quote = TRUE;
258 		    break;
259 		case '\'':
260 		    tick_quote++;
261 		    break;
262 		case '>':
263 		    address_parse = TRUE;
264 		    break;
265 		case ')':
266 		    comment_parse = TRUE;
267 		    break;
268 		case ':':
269 		    proc_sprintf = TRUE;
270 		    h = spfbuf;
271 		    *h++ = '%';
272 		    pattern++;	/* Skip over ':' */
273 		    while (*pattern
274 		     && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) {
275 			*h++ = *pattern++;
276 		    }
277 		    *h++ = 's';
278 		    *h++ = '\0';
279 		    pattern--;
280 		    break;
281 		case '/':
282 #ifdef ARTSEARCH
283 		    s = scrbuf;
284 		    if (!cmd || !index("/?g",*cmd))
285 			*s++ = '/';
286 		    strcpy(s,lastpat);
287 		    s += strlen(s);
288 		    if (!cmd || *cmd != 'g') {
289 			if (cmd && index("/?",*cmd))
290 			    *s++ = *cmd;
291 			else
292 			    *s++ = '/';
293 			if (art_doread)
294 			    *s++ = 'r';
295 			if (art_howmuch != ARTSCOPE_SUBJECT) {
296 			    *s++ = scopestr[art_howmuch];
297 			    if (art_howmuch == ARTSCOPE_ONEHDR) {
298 				safecpy(s,htype[art_srchhdr].name,
299 					(sizeof scrbuf) - (s-scrbuf));
300 				if (!(s = index(s,':')))
301 				    s = scrbuf+(sizeof scrbuf)-1;
302 				else
303 				    s++;
304 			    }
305 			}
306 		    }
307 		    *s = '\0';
308 		    s = scrbuf;
309 #else
310 		    s = nullstr;
311 #endif
312 		    break;
313 		case '{':
314 		    pattern = cpytill(scrbuf,pattern+1,'}');
315 		    if ((s = index(scrbuf,'-')) != NULL)
316 			*s++ = '\0';
317 		    else
318 			s = nullstr;
319 		    s = getval(scrbuf,s);
320 		    break;
321 		case '<':
322 		    pattern = cpytill(scrbuf,pattern+1,'>');
323 		    if ((s = index(scrbuf,'-')) != NULL)
324 			*s++ = '\0';
325 		    else
326 			s = nullstr;
327 		    interp(scrbuf, 8192, getval(scrbuf,s));
328 		    s = scrbuf;
329 		    break;
330 		case '[':
331 		    if (in_ng) {
332 			pattern = cpytill(scrbuf,pattern+1,']');
333 			if (*scrbuf
334 			 && (i = get_header_num(scrbuf)) != SOME_LINE) {
335 			    safefree(line_buf);
336 			    s = line_buf = fetchlines(art,i);
337 			}
338 			else
339 			    s = nullstr;
340 		    }
341 		    else
342 			s = nullstr;
343 		    break;
344 		case '(': {
345 		    COMPEX *oldbra_compex = bra_compex;
346 		    char rch;
347 		    bool matched;
348 
349 		    pattern = dointerp(dest,destsize,pattern+1,"!=",cmd);
350 		    rch = *pattern;
351 		    if (rch == '!')
352 			pattern++;
353 		    if (*pattern != '=')
354 			goto getout;
355 		    pattern = cpytill(scrbuf,pattern+1,'?');
356 		    if (!*pattern)
357 			goto getout;
358 		    s = scrbuf;
359 		    h = spfbuf;
360 		    proc_sprintf = FALSE;
361 		    do {
362 			switch (*s) {
363 			case '^':
364 			    *h++ = '\\';
365 			    break;
366 			case '\\':
367 			    *h++ = '\\';
368 			    *h++ = '\\';
369 			    break;
370 			case '%':
371 			    proc_sprintf = TRUE;
372 			    break;
373 			}
374 			*h++ = *s;
375 		    } while (*s++);
376 		    if (proc_sprintf) {
377 			dointerp(scrbuf,sizeof scrbuf,spfbuf,(char*)NULL,cmd);
378 			proc_sprintf = FALSE;
379 		    }
380 		    if ((s = compile(&cond_compex,scrbuf,TRUE,TRUE)) != NULL) {
381 			printf("%s: %s\n",scrbuf,s) FLUSH;
382 			pattern += strlen(pattern);
383 			free_compex(&cond_compex);
384 			goto getout;
385 		    }
386 		    matched = (execute(&cond_compex,dest) != NULL);
387 		    if (getbracket(&cond_compex, 0)) /* were there brackets? */
388 			bra_compex = &cond_compex;
389 		    if (matched==(rch == '=')) {
390 			pattern = dointerp(dest,destsize,pattern+1,":)",cmd);
391 			if (*pattern == ':')
392 			    pattern = skipinterp(pattern+1,")");
393 		    }
394 		    else {
395 			pattern = skipinterp(pattern+1,":)");
396 			if (*pattern == ':')
397 			    pattern++;
398 			pattern = dointerp(dest,destsize,pattern,")",cmd);
399 		    }
400 		    s = dest;
401 		    bra_compex = oldbra_compex;
402 		    free_compex(&cond_compex);
403 		    break;
404 		}
405 #ifdef BACKTICK
406 		case '`': {
407 		    FILE* popen();
408 		    FILE* pipefp;
409 
410 		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`",cmd);
411 		    pipefp = popen(scrbuf,"r");
412 		    if (pipefp != NULL) {
413 			int len;
414 
415 			len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
416 			    pipefp);
417 			scrbuf[len] = '\0';
418 			pclose(pipefp);
419 		    }
420 		    else {
421 			printf("\nCan't run %s\n",scrbuf);
422 			*scrbuf = '\0';
423 		    }
424 		    for (s=scrbuf; *s; s++) {
425 			if (*s == '\n') {
426 			    if (s[1])
427 				*s = ' ';
428 			    else
429 				*s = '\0';
430 			}
431 		    }
432 		    s = scrbuf;
433 		    break;
434 		}
435 #endif
436 #ifdef PROMPTTTY
437 		case '"':
438 		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"",cmd);
439 		    fputs(scrbuf,stdout) FLUSH;
440 		    resetty();
441 		    fgets(scrbuf, sizeof scrbuf, stdin);
442 		    noecho();
443 		    crmode();
444 		    i = strlen(scrbuf);
445 		    if (scrbuf[i-1] == '\n') {
446 			scrbuf[--i] = '\0';
447 		    }
448 		    growstr(&input_str, &input_siz, i+1);
449 		    safecpy(input_str, scrbuf, i+1);
450 		    s = input_str;
451 		    break;
452 #endif
453 		case '~':
454 		    s = homedir;
455 		    break;
456 		case '.':
457 		    s = dotdir;
458 		    break;
459 		case '+':
460 		    s = trndir;
461 		    break;
462 		case '$':
463 		    s = scrbuf;
464 		    sprintf(s,"%ld",our_pid);
465 		    break;
466 		case '#':
467 		    s = scrbuf;
468 		    if (upper) {
469 			static int counter = 0;
470 			sprintf(s,"%d",++counter);
471 		    }
472 		    else
473 			sprintf(s,"%d",perform_cnt);
474 		    break;
475 		case '?':
476 		    s = " ";
477 		    line_split = dest;
478 		    break;
479 		case '0': case '1': case '2': case '3': case '4':
480 		case '5': case '6': case '7': case '8': case '9':
481 		    s = getbracket(bra_compex,*pattern - '0');
482 		    break;
483 		case 'a':
484 		    if (in_ng) {
485 			s = scrbuf;
486 			sprintf(s,"%ld",(long)art);
487 		    }
488 		    else
489 			s = nullstr;
490 		    break;
491 		case 'A':
492 		    if (in_ng) {
493 #ifdef SUPPORT_NNTP
494 			if (datasrc->flags & DF_REMOTE) {
495 			    if (artopen(art,(ART_POS)0)) {
496 				nntp_finishbody(FB_SILENT);
497 				sprintf(s = scrbuf,"%s/%s",datasrc->spool_dir,
498 					nntp_artname(art, FALSE));
499 			    }
500 			    else
501 				s = nullstr;
502 			}
503 			else
504 #endif
505 #ifdef LINKART
506 			    s = linkartname;  /* for Eunice */
507 #else
508 			    sprintf(s = scrbuf,"%s/%s/%ld",datasrc->spool_dir,
509 				    ngdir,(long)art);
510 #endif
511 		    }
512 		    else
513 			s = nullstr;
514 		    break;
515 		case 'b':
516 		    s = savedest? savedest : nullstr;
517 		    break;
518 		case 'B':
519 		    s = scrbuf;
520 		    sprintf(s,"%ld",(long)savefrom);
521 		    break;
522 		case 'c':
523 		    s = ngdir? ngdir : nullstr;
524 		    break;
525 		case 'C':
526 		    s = ngname? ngname : nullstr;
527 		    break;
528 		case 'd':
529 		    if (ngdir) {
530 			s = scrbuf;
531 			sprintf(s,"%s/%s",datasrc->spool_dir,ngdir);
532 		    }
533 		    else
534 			s = nullstr;
535 		    break;
536 		case 'D':
537 		    if (in_ng)
538 			s = dist_buf = fetchlines(art,DIST_LINE);
539 		    else
540 			s = nullstr;
541 		    break;
542 		case 'e':
543 		    s = extractprog? extractprog : "-";
544 		    break;
545 		case 'E':
546 		    s = extractdest? extractdest : nullstr;
547 		    break;
548 		case 'f':			/* from line */
549 		    if (in_ng) {
550 			parseheader(art);
551 			if (htype[REPLY_LINE].minpos >= 0 && !comment_parse) {
552 						/* was there a reply line? */
553 			    if (!(s=reply_buf))
554 				s = reply_buf = fetchlines(art,REPLY_LINE);
555 			}
556 			else if (!(s = from_buf))
557 			    s = from_buf = fetchlines(art,FROM_LINE);
558 		    }
559 		    else
560 			s = nullstr;
561 		    break;
562 		case 'F':
563 		    if (in_ng) {
564 			parseheader(art);
565 			if (htype[FOLLOW_LINE].minpos >= 0)
566 					/* is there a Followup-To line? */
567 			    s = follow_buf = fetchlines(art,FOLLOW_LINE);
568 			else
569 			    s = ngs_buf = fetchlines(art,NGS_LINE);
570 		    }
571 		    else
572 			s = nullstr;
573 		    break;
574 		case 'g':			/* general mode */
575 		    s = scrbuf;
576 		    *s = gmode;
577 		    s[1] = '\0';
578 		    break;
579 		case 'h':			/* header file name */
580 		    s = headname;
581 		    break;
582 		case 'H':			/* host name in postings */
583 		    s = phostname;
584 		    break;
585 		case 'i':
586 		    if (in_ng) {
587 			if (!(s=artid_buf))
588 			    s = artid_buf = fetchlines(art,MSGID_LINE);
589 			if (*s && *s != '<') {
590 			    sprintf(scrbuf,"<%s>",artid_buf);
591 			    s = scrbuf;
592 			}
593 		    }
594 		    else
595 			s = nullstr;
596 		    break;
597 		case 'I':			/* indent string for quoting */
598 		    s = scrbuf;
599 		    sprintf(scrbuf,"'%s'",indstr);
600 		    break;
601 		case 'j':
602 		    s = scrbuf;
603 		    sprintf(scrbuf,"%d",just_a_sec*10);
604 		    break;
605 		case 'l':			/* rn library */
606 #ifdef NEWS_ADMIN
607 		    s = newsadmin;
608 #else
609 		    s = "???";
610 #endif
611 		    break;
612 		case 'L':			/* login id */
613 		    s = loginName;
614 		    break;
615 		case 'm':		/* current mode */
616 		    s = scrbuf;
617 		    *s = mode;
618 		    s[1] = '\0';
619 		    break;
620 		case 'M':
621 		    sprintf(scrbuf,"%ld",(long)dmcount);
622 		    s = scrbuf;
623 		    break;
624 		case 'n':			/* newsgroups */
625 		    if (in_ng)
626 			s = ngs_buf = fetchlines(art,NGS_LINE);
627 		    else
628 			s = nullstr;
629 		    break;
630 		case 'N':			/* full name */
631 		    s = getval("NAME",realName);
632 		    break;
633 		case 'o':			/* organization */
634 #ifdef IGNOREORG
635 		    s = getval("NEWSORG",orgname);
636 #else
637 		    s = getval("NEWSORG",NULL);
638 		    if (s == NULL)
639 			s = getval("ORGANIZATION",orgname);
640 #endif
641 		    s = filexp(s);
642 #ifdef ORGFILE
643 		    if (FILE_REF(s)) {
644 			FILE* ofp = fopen(s,"r");
645 
646 			if (ofp) {
647 			    if (fgets(scrbuf,sizeof scrbuf,ofp) == NULL)
648 			    	*scrbuf = '\0';
649 			    fclose(ofp);
650 			    s = scrbuf+strlen(scrbuf)-1;
651 			    if (*scrbuf && *s == '\n')
652 				*s = '\0';
653 			    s = scrbuf;
654 			}
655 			else
656 			    s = nullstr;
657 		    }
658 #endif
659 		    break;
660 		case 'O':
661 		    s = origdir;
662 		    break;
663 		case 'p':
664 		    s = cwd;
665 		    break;
666 		case 'P':
667 		    s = datasrc? datasrc->spool_dir : nullstr;
668 		    break;
669 		case 'q':
670 		    s = input_str;
671 		    break;
672 		case 'r':
673 		    if (in_ng) {
674 			parseheader(art);
675 			safefree0(refs_buf);
676 			if (htype[REFS_LINE].minpos >= 0) {
677 			    refs_buf = fetchlines(art,REFS_LINE);
678 			    normalize_refs(refs_buf);
679 			    if ((s = rindex(refs_buf,'<')) != NULL)
680 				break;
681 			}
682 		    }
683 		    s = nullstr;
684 		    break;
685 		case 'R': {
686 		    int len, j;
687 
688 		    if (!in_ng) {
689 			s = nullstr;
690 			break;
691 		    }
692 		    parseheader(art);
693 		    safefree0(refs_buf);
694 		    if (htype[REFS_LINE].minpos >= 0) {
695 			refs_buf = fetchlines(art,REFS_LINE);
696 			len = strlen(refs_buf)+1;
697 			normalize_refs(refs_buf);
698 			/* no more than 3 prior references PLUS the
699 			** root article allowed, including the one
700 			** concatenated below */
701 			if ((s = rindex(refs_buf,'<')) != NULL && s > refs_buf) {
702 			    *s = '\0';
703 			    h = rindex(refs_buf,'<');
704 			    *s = '<';
705 			    if (h && h > refs_buf) {
706 				s = index(refs_buf+1,'<');
707 				if (s < h)
708 				    safecpy(s,h,len);
709 			    }
710 			}
711 		    }
712 		    else
713 			len = 0;
714 		    if (!artid_buf)
715 			artid_buf = fetchlines(art,MSGID_LINE);
716 		    i = refs_buf? strlen(refs_buf) : 0;
717 		    j = strlen(artid_buf) + (i? 1 : 0)
718 		      + (artid_buf[0] == '<'? 0 : 2) + 1;
719 		    if (len < i + j)
720 			refs_buf = saferealloc(refs_buf, i + j);
721 		    if (i)
722 			refs_buf[i++] = ' ';
723 		    if (artid_buf[0] == '<')
724 			strcpy(refs_buf+i, artid_buf);
725 		    else if (artid_buf[0])
726 			sprintf(refs_buf+i, "<%s>", artid_buf);
727 		    else
728 			refs_buf[i] = '\0';
729 		    s = refs_buf;
730 		    break;
731 		}
732 		case 's':
733 		case 'S': {
734 		    char* str;
735 		    if (!in_ng || !art || !artp) {
736 			s = nullstr;
737 			break;
738 		    }
739 		    if ((str = subj_buf) == NULL)
740 			str = subj_buf = fetchsubj(art,TRUE);
741 		    subject_has_Re(str,&str);
742 		    if (*pattern == 's'
743 		     && (h = instr(str,"- (nf", TRUE)) != NULL)
744 			*h = '\0';
745 		    s = str;
746 		    break;
747 		}
748 		case 't':
749 		case 'T':
750 		    if (!in_ng) {
751 			s = nullstr;
752 			break;
753 		    }
754 		    parseheader(art);
755 		    if (htype[REPLY_LINE].minpos >= 0) {
756 					/* was there a reply line? */
757 			if (!(s=reply_buf))
758 			    s = reply_buf = fetchlines(art,REPLY_LINE);
759 		    }
760 		    else if (!(s = from_buf))
761 			s = from_buf = fetchlines(art,FROM_LINE);
762 		    else
763 			s = "noname";
764 		    if (*pattern == 'T') {
765 			if (htype[PATH_LINE].minpos >= 0) {
766 					/* should we substitute path? */
767 			    s = path_buf = fetchlines(art,PATH_LINE);
768 			}
769 			i = strlen(phostname);
770 			if (strnEQ(phostname,s,i) && s[i] == '!')
771 			    s += i + 1;
772 		    }
773 		    address_parse = TRUE;	/* just the good part */
774 		    break;
775 		case 'u':
776 		    if (in_ng) {
777 			sprintf(scrbuf,"%ld",(long)ngptr->toread);
778 			s = scrbuf;
779 		    }
780 		    else
781 			s = nullstr;
782 		    break;
783 		case 'U': {
784 		    int unseen;
785 
786 		    if (!in_ng) {
787 			s = nullstr;
788 			break;
789 		    }
790 		    unseen = (art <= lastart) && !was_read(art);
791 		    if (selected_only) {
792 			int selected;
793 
794 			selected = curr_artp && (curr_artp->flags & AF_SEL);
795 			sprintf(scrbuf,"%ld",
796 				(long)selected_count - (selected && unseen));
797 		    }
798 		    else
799 			sprintf(scrbuf,"%ld",(long)ngptr->toread - unseen);
800 		    s = scrbuf;
801 		    break;
802 		}
803 		case 'v': {
804 		    int selected, unseen;
805 
806 		    if (in_ng) {
807 			selected = curr_artp && (curr_artp->flags & AF_SEL);
808 			unseen = (art <= lastart) && !was_read(art);
809 			sprintf(scrbuf,"%ld",(long)ngptr->toread-selected_count
810 						- (!selected && unseen));
811 			s = scrbuf;
812 		    }
813 		    else
814 			s = nullstr;
815 		    break;
816 		}
817 		case 'V':
818 		    s = patchlevel + 1;
819 		    break;
820 		case 'W':
821 		    s = datasrc? datasrc->thread_dir : nullstr;
822 		    break;
823 		case 'x':			/* news library */
824 		    s = lib;
825 		    break;
826 		case 'X':			/* rn library */
827 		    s = rnlib;
828 		    break;
829 #ifdef SCORE
830 		case 'y':	/* from line with *-shortening */
831 		    if (!in_ng) {
832 			s = nullstr;
833 			break;
834 		    }
835 #ifdef ASYNC_PARSE
836 /*		    parse_maybe(art); */
837 #endif
838 		    /* XXX Rewrite this! */
839 		    {	/* sick, but I don't want to hunt down a buf... */
840 			static char tmpbuf[1024];
841 			char* s2;
842 			char* s3;
843 			int i = 0;
844 
845 			s2 = fetchlines(art,FROM_LINE);
846 			strcpy(tmpbuf,s2);
847 			free(s2);
848 			for (s2=tmpbuf;
849 			     (*s2 && (*s2 != '@') && (*s2 !=' '));s2++)
850 				; /* EMPTY */
851 			if (*s2 == '@') {	/* we have normal form... */
852 			    for (s3=s2+1;(*s3 && (*s3 != ' '));s3++)
853 				if (*s3 == '.')
854 				    i++;
855 			}
856 			if (i>1) { /* more than one dot */
857 			    s3 = s2;	/* will be incremented before use */
858 			    while (i>=2) {
859 				s3++;
860 				if (*s3 == '.')
861 				    i--;
862 			    }
863 			    s2++;
864 			    *s2 = '*';
865 			    s2++;
866 			    *s2 = '\0';
867 			    from_buf = (char*)safemalloc(
868 				(strlen(tmpbuf)+strlen(s3)+1)*sizeof(char));
869 			    strcpy(from_buf,tmpbuf);
870 			    strcat(from_buf,s3);
871 			} else {
872 			    from_buf = savestr(tmpbuf);
873 			}
874 			s = from_buf;
875 		    }
876 		    break;
877 #endif /* SCORE */
878 		case 'Y':
879 		    s = tmpdir;
880 		    break;
881 		case 'z':
882 		    if (!in_ng) {
883 			s = nullstr;
884 			break;
885 		    }
886 #ifdef LINKART
887 		    s = linkartname;	/* so Eunice people get right file */
888 #else
889 		    s = scrbuf;
890 		    sprintf(s,"%ld",(long)art);
891 #endif
892 		    if (stat(s,&filestat) < 0)
893 			filestat.st_size = 0L;
894 		    sprintf(scrbuf,"%5ld",(long)filestat.st_size);
895 		    s = scrbuf;
896 		    break;
897 		case 'Z':
898 		    sprintf(scrbuf,"%ld",(long)selected_count);
899 		    s = scrbuf;
900 		    break;
901 		case '\0':
902 		    s = nullstr;
903 		    break;
904 		default:
905 		    if (--destsize <= 0)
906 			abort_interp();
907 		    *dest++ = *pattern | metabit;
908 		    s = nullstr;
909 		    break;
910 		}
911 	    }
912 	    if (proc_sprintf) {
913 		sprintf(scrbuf,spfbuf,s);
914 		s = scrbuf;
915 	    }
916 	    if (*pattern)
917 		pattern++;
918 	    if (upper || lastcomp) {
919 		char* t;
920 
921 		if (s != scrbuf) {
922 		    safecpy(scrbuf,s,sizeof scrbuf);
923 		    s = scrbuf;
924 		}
925 		if (upper || !(t = rindex(s,'/')))
926 		    t = s;
927 		while (*t && !isalpha(*t))
928 		    t++;
929 		if (islower(*t))
930 		    *t = toupper(*t);
931 	    }
932 	    /* Do we have room left? */
933 	    i = strlen(s);
934 	    if (destsize <= i)
935 		abort_interp();
936 	    destsize -= i;	/* adjust the size now. */
937 
938 #ifdef DEBUG
939 	    if (debug & DEB_INTRP)
940 		printf("%% = %s\n",s);
941 #endif
942 
943 	    /* A maze of twisty little conditions, all alike... */
944 	    if (address_parse || comment_parse) {
945 		if (s != scrbuf) {
946 		    safecpy(scrbuf,s,sizeof scrbuf);
947 		    s = scrbuf;
948 		}
949 		decode_header(s, s, strlen(s));
950 		if (address_parse) {
951 		    if ((h=index(s,'<')) != NULL) { /* grab the good part */
952 			s = h+1;
953 			if ((h=index(s,'>')) != NULL)
954 			    *h = '\0';
955 		    } else if ((h=index(s,'(')) != NULL) {
956 			while (h-- != s && *h == ' ')
957 			    ;
958 			h[1] = '\0';		/* or strip the comment */
959 		    }
960 		} else {
961 		    if (!(s = extract_name(s)))
962 			s = nullstr;
963 		}
964 	    }
965 	    if (metabit) {
966 		/* set meta bit while copying. */
967 		i = metabit;		/* maybe get into register */
968 		if (s == dest) {
969 		    while (*dest)
970 			*dest++ |= i;
971 		} else {
972 		    while (*s)
973 			*dest++ = *s++ | i;
974 		}
975 	    } else if (re_quote || tick_quote) {
976 		/* put a backslash before regexp specials while copying. */
977 		if (s == dest) {
978 		    /* copy out so we can copy in. */
979 		    safecpy(scrbuf, s, sizeof scrbuf);
980 		    s = scrbuf;
981 		    if (i > sizeof scrbuf)	/* we truncated, ack! */
982 			abort_interp();
983 		}
984 		while (*s) {
985 		    if ((re_quote && index(regexp_specials, *s))
986 		     || (tick_quote == 2 && *s == '"')) {
987 			if (--destsize <= 0)
988 			    abort_interp();
989 			*dest++ = '\\';
990 		    }
991 		    else if (re_quote && *s == ' ' && s[1] == ' ') {
992 			*dest++ = ' ';
993 			*dest++ = '*';
994 			while (*++s == ' ') ;
995 			continue;
996 		    }
997 		    else if (tick_quote && *s == '\'') {
998 			if ((destsize -= 3) <= 0)
999 			    abort_interp();
1000 			*dest++ = '\'';
1001 			*dest++ = '\\';
1002 			*dest++ = '\'';
1003 		    }
1004 		    *dest++ = *s++;
1005 		}
1006 	    } else {
1007 		/* straight copy. */
1008 		if (s == dest) {
1009 		    dest += i;
1010 		} else {
1011 		    while (*s)
1012 			*dest++ = *s++;
1013 		}
1014 	    }
1015 	}
1016 	else {
1017 	    if (--destsize <= 0)
1018 		abort_interp();
1019 	    if (*pattern == '^' && pattern[1]) {
1020 		pattern++;
1021 		i = *(Uchar*)pattern;	/* get char after arrow into a register */
1022 		if (i == '?')
1023 		    *dest++ = '\177' | metabit;
1024 		else if (i == '(') {
1025 		    metabit = 0200;
1026 		    destsize++;
1027 		}
1028 		else if (i == ')') {
1029 		    metabit = 0;
1030 		    destsize++;
1031 		}
1032 		else if (i >= '@')
1033 		    *dest++ = (i & 037) | metabit;
1034 		else
1035 		    *dest++ = *--pattern | metabit;
1036 		pattern++;
1037 	    }
1038 	    else if (*pattern == '\\' && pattern[1]) {
1039 		++pattern;		/* skip backslash */
1040 		pattern = interp_backslash(dest, pattern) + 1;
1041 		*dest++ |= metabit;
1042 	    }
1043 	    else if (*pattern)
1044 		*dest++ = *pattern++ | metabit;
1045 	}
1046     }
1047     *dest = '\0';
1048     if (line_split != NULL)
1049 	if ((int)strlen(orig_dest) > 79)
1050 	    *line_split = '\n';
1051 getout:
1052     safefree(subj_buf);		/* return any checked out storage */
1053     safefree(ngs_buf);
1054     safefree(refs_buf);
1055     safefree(artid_buf);
1056     safefree(reply_buf);
1057     safefree(from_buf);
1058     safefree(path_buf);
1059     safefree(follow_buf);
1060     safefree(dist_buf);
1061     safefree(line_buf);
1062 #ifdef DEBUG
1063     if (debug & DEB_INTRP)
1064 	printf("<dointerp: %s\n",orig_dest);
1065 #endif
1066     return pattern;			/* where we left off */
1067 }
1068 
1069 char*
interp_backslash(dest,pattern)1070 interp_backslash(dest,pattern)
1071 char* dest;
1072 register char* pattern;
1073 {
1074     register int i = *pattern;
1075 
1076     if (i >= '0' && i <= '7') {
1077 	i = 0;
1078 	while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
1079 	    i <<= 3;
1080 	    i += *pattern++ - '0';
1081 	}
1082 	*dest = (char)(i & 0377);
1083 	return pattern - 1;
1084     }
1085     switch (i) {
1086     case 'b':
1087 	*dest = '\b';
1088 	break;
1089     case 'f':
1090 	*dest = '\f';
1091 	break;
1092     case 'n':
1093 	*dest = '\n';
1094 	break;
1095     case 'r':
1096 	*dest = '\r';
1097 	break;
1098     case 't':
1099 	*dest = '\t';
1100 	break;
1101     case '\0':
1102 	*dest = '\\';
1103 	return pattern - 1;
1104     default:
1105 	*dest = (char)i;
1106 	break;
1107     }
1108     return pattern;
1109 }
1110 
1111 /* helper functions */
1112 
1113 char*
interp(dest,destsize,pattern)1114 interp(dest,destsize,pattern)
1115 register char* dest;
1116 register int destsize;
1117 register char* pattern;
1118 {
1119     return dointerp(dest,destsize,pattern,(char*)NULL,(char*)NULL);
1120 }
1121 
1122 char*
interpsearch(dest,destsize,pattern,cmd)1123 interpsearch(dest,destsize,pattern,cmd)
1124 register char* dest;
1125 register int destsize;
1126 register char* pattern;
1127 char* cmd;
1128 {
1129     return dointerp(dest,destsize,pattern,(char*)NULL,cmd);
1130 }
1131 
1132 /* normalize a references line in place */
1133 
1134 void
normalize_refs(refs)1135 normalize_refs(refs)
1136 char* refs;
1137 {
1138     register char* f;
1139     register char* t;
1140 
1141     for (f = t = refs; *f; ) {
1142 	if (*f == '<') {
1143 	    while (*f && (*t++ = *f++) != '>') ;
1144 	    while (*f == ' ' || *f == '\t' || *f == '\n' || *f == ',') f++;
1145 	    if (f != t)
1146 		*t++ = ' ';
1147 	}
1148 	else
1149 	    f++;
1150     }
1151     if (t != refs && t[-1] == ' ')
1152 	t--;
1153     *t = '\0';
1154 }
1155 
1156 static void
abort_interp()1157 abort_interp()
1158 {
1159     fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
1160     sig_catcher(0);
1161 }
1162