home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume23 / trn / part05 / intrp.c next >
C/C++ Source or Header  |  1991-08-22  |  27KB  |  1,199 lines

  1. /* $Header: intrp.c,v 4.3.3.3 91/01/16 02:43:42 davison Trn $
  2.  *
  3.  * $Log:    intrp.c,v $
  4.  * Revision 4.3.3.3  91/01/16  02:43:42  davison
  5.  * Integrated rn patches 48-54.
  6.  * 
  7.  * Revision 4.3.3.2  90/08/20  16:29:08  davison
  8.  * Added HOSTFILE handling.  Add OURDOMAIN if site has no '.'
  9.  * 
  10.  * Revision 4.3.3.1  90/07/21  20:20:13  davison
  11.  * Initial Trn Release
  12.  * 
  13.  * Revision 4.3.2.11  90/12/31  11:47:44  sob
  14.  * NEWSADMIN could not cancel articles because it was not getting set.
  15.  *
  16.  * Revision 4.3.2.10  90/12/31  00:02:55  sob
  17.  * Moved HIDDENET to remove unneeded cruft.
  18.  * 
  19.  * Revision 4.3.2.9  90/12/30  03:48:11  sob
  20.  * Changed "hidden" to "hiddennet" to be like nntp and bnews.
  21.  * Made it possible to cancel articles if hiddennet is defined.
  22.  * 
  23.  * Revision 4.3.2.8  90/11/22  13:52:27  sob
  24.  * Made changes to keep preprocessors from complaining.
  25.  * 
  26.  * Revision 4.3.2.7  90/11/05  23:59:33  sob
  27.  * moved the definition of tmpbuf such that it get defined before it is used.
  28.  * 
  29.  * Revision 4.3.2.6  90/11/03  18:52:31  sob
  30.  * Fixed bug in the definition of the nodename using the uname() system call.
  31.  * 
  32.  * Revision 4.3.2.5  90/10/01  01:31:18  sob
  33.  * Fixed problem with struct utsname reported by jrallen@devildog.att.com
  34.  * when rn is compiled on the Amdahl 5890 UTS 2.0 system.
  35.  * 
  36.  * Revision 4.3.2.4  90/04/23  00:31:20  sob
  37.  * Removed unneeded atoi call.
  38.  * 
  39.  * Revision 4.3.2.3  90/03/22  23:04:35  sob
  40.  * Fixes provided by Wayne Davison <drivax!davison>
  41.  * 
  42.  * Revision 4.3.2.2  90/03/17  17:03:12  sob
  43.  * Fixed determination of the news superuser's id. Fix provided by Chip
  44.  * Rosenthal <chip@chinacat.lonestar.org>.
  45.  * 
  46.  * Revision 4.3.2.1  89/12/17  02:54:55  sob
  47.  * Removed redundant include directive.
  48.  * 
  49.  * Revision 4.3.1.5  85/05/23  17:21:24  lwall
  50.  * Now allows 'r' and 'f' on null articles.
  51.  * 
  52.  * Revision 4.3.1.4  85/05/21  13:35:21  lwall
  53.  * Sped up "rn -c" by not doing unnecessary initialization.
  54.  * 
  55.  * Revision 4.3.1.3  85/05/17  10:37:11  lwall
  56.  * Fixed & substitution to capitalize last name too.
  57.  * 
  58.  * Revision 4.3.1.2  85/05/15  14:39:45  lwall
  59.  * Spelled gecos right.
  60.  * 
  61.  * Revision 4.3.1.1  85/05/10  11:33:51  lwall
  62.  * Branch for patches.
  63.  * 
  64.  * Revision 4.3  85/05/01  11:40:54  lwall
  65.  * Baseline for release with 4.3bsd.
  66.  * 
  67.  */
  68.  
  69. #include "EXTERN.h"
  70. #include "common.h"
  71. #include "util.h"
  72. #include "search.h"
  73. #include "head.h"
  74. #include "rn.h"
  75. #include "artsrch.h"
  76. #include "ng.h"
  77. #include "respond.h"
  78. #include "rcstuff.h"
  79. #include "bits.h"
  80. #include "artio.h"
  81. #include "term.h"
  82. #include "final.h"
  83. #ifdef USETHREADS
  84. #include "rthreads.h"
  85. #endif
  86. #include "INTERN.h"
  87. #include "intrp.h"
  88.  
  89. char orgname[] = ORGNAME;
  90.  
  91. /* name of this site */
  92. #ifndef HIDDENNET
  93. #ifdef GETHOSTNAME
  94.     char *hostname;
  95. #   undef SITENAME
  96. #   define SITENAME hostname
  97. #else /* !GETHOSTNAME */
  98. #   ifdef DOUNAME
  99. #    include <sys/utsname.h>
  100.     struct utsname utsn;
  101. #    undef SITENAME
  102. #    define SITENAME utsn.nodename
  103. #   else /* !DOUNAME */
  104. #    ifdef PHOSTNAME
  105.         char *hostname;
  106. #        undef SITENAME
  107. #        define SITENAME hostname
  108. #    else /* !PHOSTNAME */
  109. #        ifdef WHOAMI
  110. #        undef SITENAME
  111. #        define SITENAME sysname
  112. #        endif /* WHOAMI */
  113. #    endif /* PHOSTNAME */
  114. #   endif /* DOUNAME */
  115. #endif /* GETHOSTNAME */
  116. #endif /*HIDDENNET */
  117.  
  118. #ifdef TILDENAME
  119. static char *tildename = Nullch;
  120. static char *tildedir = Nullch;
  121. #endif
  122.  
  123. char *realname INIT(Nullch);    /* real name of sender from /etc/passwd */
  124.  
  125. #ifdef CONDSUB
  126. char *skipinterp ANSI((char *,char *));
  127. #endif
  128.  
  129. static void abort_interp ANSI((void));
  130.  
  131. void
  132. intrp_init(tcbuf)
  133. char *tcbuf;
  134. {
  135.     char *getlogin();
  136.  
  137.     spool = savestr(filexp(SPOOL));    /* usually /usr/spool/news */
  138.     
  139.     /* get environmental stuff */
  140.  
  141. #ifdef NEWSADMIN
  142. #ifdef GETPWENT
  143.     {
  144.     struct passwd *getpwnam();
  145.     struct passwd *pwd = getpwnam(NEWSADMIN);
  146.  
  147.     if (pwd != NULL)
  148.         newsuid = pwd->pw_uid;
  149.     }
  150. #endif    /* GETPWENT */
  151. #endif    /* NEWSADMIN */
  152.     /* get home directory */
  153.  
  154.     homedir = getenv("HOME");
  155.     if (homedir == Nullch)
  156.     homedir = getenv("LOGDIR");
  157.  
  158.     dotdir = getval("DOTDIR",homedir);
  159.  
  160.     /* get login name */
  161.  
  162.     logname = getenv("USER");
  163.     if (logname == Nullch)
  164.     logname = getenv("LOGNAME");
  165. #ifdef GETLOGIN
  166.     if (logname == Nullch)
  167.     logname = savestr(getlogin());
  168. #endif
  169.  
  170. #ifdef NEWSADMIN
  171.     /* if this is the news admin than load his UID into newsuid */
  172.  
  173.     if ( strEQ(logname,NEWSADMIN) )
  174.     newsuid = getuid();
  175. #endif
  176.  
  177.     if (checkflag)            /* that getwd below takes ~1/3 sec. */
  178.     return;                /* and we do not need it for -c */
  179.     getwd(tcbuf);            /* find working directory name */
  180.     origdir = savestr(tcbuf);        /* and remember it */
  181.  
  182.     /* get the real name of the person (%N) */
  183.     /* Must be done after logname is read in because BERKNAMES uses that */
  184.  
  185.     strcpy(tcbuf,getrealname(getuid()));
  186.     realname = savestr(tcbuf);
  187.  
  188.     /* name of header file (%h) */
  189.  
  190.     headname = savestr(filexp(HEADNAME));
  191.  
  192.     /* name of this site (%H) */
  193.  
  194. #ifndef HIDDENNET
  195. #ifdef HOSTFILE
  196.     if ((tmpfp = fopen(HOSTFILE,"r")) == NULL) {
  197.     hostname = "unknown";
  198.     printf("Warning: Couldn't open %s to determine hostname!\n", HOSTFILE); 
  199.     } else {
  200.     fgets(buf, sizeof(buf), tmpfp);
  201.     buf[strlen(buf)-1] = 0;
  202.     hostname = savestr(buf);
  203.     fclose(tmpfp);
  204.     }
  205. #else
  206. #ifdef GETHOSTNAME
  207.     gethostname(buf,sizeof buf);
  208.     hostname = savestr(buf);
  209. #else
  210. #ifdef DOUNAME
  211.     /* get sysname */
  212.     uname(&utsn);
  213. #else
  214. #ifdef PHOSTNAME
  215.     {
  216.     FILE *popen();
  217.     FILE *pipefp = popen(PHOSTNAME,"r");
  218.     
  219.     if (pipefp == Nullfp) {
  220.         printf("Can't find hostname\n");
  221.         sig_catcher(0);
  222.     }
  223.     fgets(buf,sizeof buf,pipefp);
  224.     buf[strlen(buf)-1] = '\0';    /* wipe out newline */
  225.     hostname = savestr(buf);
  226.     pclose(pipefp);
  227.     }
  228. #endif    /* PHOSTNAME */
  229. #endif    /* DOUNAME */
  230. #endif    /* GETHOSTNAME */
  231. #endif    /* HOSTFILE */
  232.     if (index(SITENAME,'.') == NULL) {
  233.     sprintf(buf, "%s.%s", SITENAME, OURDOMAIN);
  234.     sitename = savestr(buf);
  235.     } else
  236.     sitename = savestr(SITENAME);
  237. #else
  238.     sitename = savestr(OURDOMAIN);
  239. #endif
  240. }
  241.  
  242. /* expand filename via %, ~, and $ interpretation */
  243. /* returns pointer to static area */
  244. /* Note that there is a 1-deep cache of ~name interpretation */
  245.  
  246. char *
  247. filexp(s)
  248. register char *s;
  249. {
  250.     static char filename[CBUFLEN];
  251.     char scrbuf[CBUFLEN];
  252.     register char *d;
  253.  
  254. #ifdef DEBUGGING
  255.     if (debug & DEB_FILEXP)
  256.     printf("< %s\n",s) FLUSH;
  257. #endif
  258.     interp(filename, (sizeof filename), s);            /* interpret any % escapes */
  259. #ifdef DEBUGGING
  260.     if (debug & DEB_FILEXP)
  261.     printf("%% %s\n",filename) FLUSH;
  262. #endif
  263.     s = filename;
  264.     if (*s == '~') {    /* does destination start with ~? */
  265.     if (!*(++s) || *s == '/') {
  266.         sprintf(scrbuf,"%s%s",homedir,s);
  267.                 /* swap $HOME for it */
  268. #ifdef DEBUGGING
  269.     if (debug & DEB_FILEXP)
  270.     printf("~ %s\n",scrbuf) FLUSH;
  271. #endif
  272.         strcpy(filename,scrbuf);
  273.     }
  274.     else {
  275. #ifdef TILDENAME
  276.         for (d=scrbuf; isalnum(*s); s++,d++)
  277.         *d = *s;
  278.         *d = '\0';
  279.         if (tildedir && strEQ(tildename,scrbuf)) {
  280.         strcpy(scrbuf,tildedir);
  281.         strcat(scrbuf, s);
  282.         strcpy(filename, scrbuf);
  283. #ifdef DEBUGGING
  284.         if (debug & DEB_FILEXP)
  285.             printf("r %s %s\n",tildename,tildedir) FLUSH;
  286. #endif
  287.         }
  288.         else {
  289.         if (tildename) {
  290.             free(tildename);
  291.             free(tildedir);
  292.         }
  293.         tildedir = Nullch;
  294.         tildename = savestr(scrbuf);
  295. #ifdef GETPWENT        /* getpwnam() is not the paragon of efficiency */
  296.         {
  297.             struct passwd *getpwnam();
  298.             struct passwd *pwd = getpwnam(tildename);
  299.  
  300.             sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
  301.             tildedir = savestr(pwd->pw_dir);
  302.             strcpy(filename,scrbuf);
  303. #ifdef GETPWENT
  304.             endpwent();
  305. #endif
  306.         }
  307. #else            /* this will run faster, and is less D space */
  308.         {    /* just be sure LOGDIRFIELD is correct */
  309.             FILE *pfp = fopen("/etc/passwd","r");
  310.             char tmpbuf[512];
  311.             int i;
  312.             
  313.             if (pfp == Nullfp) {
  314.             printf(cantopen,"passwd") FLUSH;
  315.             sig_catcher(0);
  316.             }
  317.             while (fgets(tmpbuf,512,pfp) != Nullch) {
  318.             d = cpytill(scrbuf,tmpbuf,':');
  319. #ifdef DEBUGGING
  320.             if (debug & DEB_FILEXP)
  321.                 printf("p %s\n",tmpbuf) FLUSH;
  322. #endif
  323.             if (strEQ(scrbuf,tildename)) {
  324.                 for (i=LOGDIRFIELD-2; i; i--) {
  325.                 if (d)
  326.                     d = index(d+1,':');
  327.                 }
  328.                 if (d) {
  329.                 cpytill(scrbuf,d+1,':');
  330.                 tildedir = savestr(scrbuf);
  331.                 strcat(scrbuf,s);
  332.                 strcpy(filename,scrbuf);
  333.                 }
  334.                 break;
  335.             }
  336.             }
  337.             fclose(pfp);
  338.         }
  339. #endif
  340.         }
  341. #else /* !TILDENAME */
  342. #ifdef VERBOSE
  343.         IF(verbose)
  344.         fputs("~loginname not implemented.\n",stdout) FLUSH;
  345.         ELSE
  346. #endif
  347. #ifdef TERSE
  348.         fputs("~login not impl.\n",stdout) FLUSH;
  349. #endif
  350. #endif
  351.     }
  352.     }
  353.     else if (*s == '$') {    /* starts with some env variable? */
  354.     d = scrbuf;
  355.     *d++ = '%';
  356.     if (s[1] == '{')
  357.         strcpy(d,s+2);
  358.     else {
  359.         *d++ = '{';
  360.         for (s++; isalnum(*s); s++) *d++ = *s;
  361.                 /* skip over token */
  362.         *d++ = '}';
  363.         strcpy(d,s);
  364.     }
  365. #ifdef DEBUGGING
  366.     if (debug & DEB_FILEXP)
  367.         printf("$ %s\n",scrbuf) FLUSH;
  368. #endif
  369.     interp(filename, (sizeof filename), scrbuf);
  370.                     /* this might do some extra '%'s but */
  371.                     /* that is how the Mercedes Benz */
  372.     }
  373. #ifdef DEBUGGING
  374.     if (debug & DEB_FILEXP)
  375.     printf("> %s\n",filename) FLUSH;
  376. #endif
  377.     return filename;
  378. }
  379.  
  380. #ifdef CONDSUB
  381. /* skip interpolations */
  382.  
  383. char *
  384. skipinterp(pattern,stoppers)
  385. register char *pattern;
  386. char *stoppers;
  387. {
  388.  
  389.     while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
  390. #ifdef DEBUGGING
  391.     if (debug & 8)
  392.         printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern);
  393. #endif
  394.     if (*pattern == '%' && pattern[1]) {
  395.         switch (*++pattern) {
  396.         case '{':
  397.         for (pattern++; *pattern && *pattern != '}'; pattern++)
  398.             if (*pattern == '\\')
  399.             pattern++;
  400.         break;
  401.         case '[':
  402.         for (pattern++; *pattern && *pattern != ']'; pattern++)
  403.             if (*pattern == '\\')
  404.             pattern++;
  405.         break;
  406. #ifdef CONDSUB
  407.         case '(': {
  408.         pattern = skipinterp(pattern+1,"!=");
  409.         if (!*pattern)
  410.             goto getout;
  411.         for (pattern++; *pattern && *pattern != '?'; pattern++)
  412.             if (*pattern == '\\')
  413.             pattern++;
  414.         if (!*pattern)
  415.             goto getout;
  416.         pattern = skipinterp(pattern+1,":)");
  417.         if (*pattern == ':')
  418.             pattern = skipinterp(pattern+1,")");
  419.         break;
  420.         }
  421. #endif
  422. #ifdef BACKTICK
  423.         case '`': {
  424.         pattern = skipinterp(pattern+1,"`");
  425.         break;
  426.         }
  427. #endif
  428. #ifdef PROMPTTTY
  429.         case '"':
  430.         pattern = skipinterp(pattern+1,"\"");
  431.         break;
  432. #endif
  433.         default:
  434.         break;
  435.         }
  436.         pattern++;
  437.     }
  438.     else {
  439.         if (*pattern == '^' && pattern[1])
  440.         pattern += 2;
  441.         else if (*pattern == '\\' && pattern[1])
  442.         pattern += 2;
  443.         else
  444.         pattern++;
  445.     }
  446.     }
  447. getout:
  448.     return pattern;            /* where we left off */
  449. }
  450. #endif
  451.  
  452. /* interpret interpolations */
  453.  
  454. char *
  455. dointerp(dest,destsize,pattern,stoppers)
  456. register char *dest;
  457. register int destsize;
  458. register char *pattern;
  459. char *stoppers;
  460. {
  461.     char *subj_buf = Nullch;
  462.     char *ngs_buf = Nullch;
  463.     char *refs_buf = Nullch;
  464.     char *artid_buf = Nullch;
  465.     char *reply_buf = Nullch;
  466.     char *from_buf = Nullch;
  467.     char *path_buf = Nullch;
  468.     char *follow_buf = Nullch;
  469.     char *dist_buf = Nullch;
  470.     char *line_buf = Nullch;
  471.     register char *s, *h;
  472.     register int i;
  473.     char scrbuf[512];
  474.     bool upper = FALSE;
  475.     bool lastcomp = FALSE;
  476.     int metabit = 0;
  477.  
  478.     while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
  479. #ifdef DEBUGGING
  480.     if (debug & 8)
  481.         printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
  482. #endif
  483.     if (*pattern == '%' && pattern[1]) {
  484.         upper = FALSE;
  485.         lastcomp = FALSE;
  486.         for (s=Nullch; !s; ) {
  487.         switch (*++pattern) {
  488.         case '^':
  489.             upper = TRUE;
  490.             break;
  491.         case '_':
  492.             lastcomp = TRUE;
  493.             break;
  494.         case '/':
  495. #ifdef ARTSRCH
  496.             s = scrbuf;
  497.             if (!index("/?g",pattern[-2]))
  498.             *s++ = '/';
  499.             strcpy(s,lastpat);
  500.             s += strlen(s);
  501.             if (pattern[-2] != 'g') {
  502.             if (index("/?",pattern[-2]))
  503.                 *s++ = pattern[-2];
  504.             else
  505.                 *s++ = '/';
  506.             if (art_howmuch == 1)
  507.                 *s++ = 'h';
  508.             else if (art_howmuch == 2)
  509.                 *s++ = 'a';
  510.             if (art_doread)
  511.                 *s++ = 'r';
  512.             }
  513.             *s = '\0';
  514.             s = scrbuf;
  515. #else
  516.             s = nullstr;
  517. #endif
  518.             break;
  519.         case '{':
  520.             pattern = cpytill(scrbuf,pattern+1,'}');
  521.             if (s = index(scrbuf,'-'))
  522.             *s++ = '\0';
  523.             else
  524.             s = nullstr;
  525.             s = getval(scrbuf,s);
  526.             break;
  527.         case '[':
  528.             pattern = cpytill(scrbuf,pattern+1,']');
  529.             i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
  530.             if (line_buf)
  531.             free(line_buf);
  532.             s = line_buf = fetchlines(art,i);
  533.             break;
  534. #ifdef CONDSUB
  535.         case '(': {
  536.             COMPEX *oldbra_compex = bra_compex;
  537.             COMPEX cond_compex;
  538.             char rch;
  539.             bool matched;
  540.             
  541.             init_compex(&cond_compex);
  542.             pattern = dointerp(dest,destsize,pattern+1,"!=");
  543.             rch = *pattern;
  544.             if (rch == '!')
  545.             pattern++;
  546.             if (*pattern != '=')
  547.             goto getout;
  548.             pattern = cpytill(scrbuf,pattern+1,'?');
  549.             if (!*pattern)
  550.             goto getout;
  551.             if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
  552.             printf("%s: %s\n",scrbuf,s) FLUSH;
  553.             pattern += strlen(pattern);
  554.             goto getout;
  555.             }
  556.             matched = (execute(&cond_compex,dest) != Nullch);
  557.             if (cond_compex.nbra)    /* were there brackets? */
  558.             bra_compex = &cond_compex;
  559.             if (matched==(rch == '=')) {
  560.             pattern = dointerp(dest,destsize,pattern+1,":)");
  561.             if (*pattern == ':')
  562.                 pattern = skipinterp(pattern+1,")");
  563.             }
  564.             else {
  565.             pattern = skipinterp(pattern+1,":)");
  566.             if (*pattern == ':')
  567.                 pattern++;
  568.             pattern = dointerp(dest,destsize,pattern,")");
  569.             }
  570.             s = dest;
  571.             bra_compex = oldbra_compex;
  572.             free_compex(&cond_compex);
  573.             break;
  574.         }
  575. #endif
  576. #ifdef BACKTICK
  577.         case '`': {
  578.             FILE *pipefp, *popen();
  579.  
  580.             pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
  581.             pipefp = popen(scrbuf,"r");
  582.             if (pipefp != Nullfp) {
  583.             int len;
  584.  
  585.             len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
  586.                 pipefp);
  587.             scrbuf[len] = '\0';
  588.             pclose(pipefp);
  589.             }
  590.             else {
  591.             printf("\nCan't run %s\n",scrbuf);
  592.             *scrbuf = '\0';
  593.             }
  594.             for (s=scrbuf; *s; s++) {
  595.             if (*s == '\n') {
  596.                 if (s[1])
  597.                 *s = ' ';
  598.                 else
  599.                 *s = '\0';
  600.             }
  601.             }
  602.             s = scrbuf;
  603.             break;
  604.         }
  605. #endif
  606. #ifdef PROMPTTTY
  607.         case '"':
  608.             pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
  609.             fputs(scrbuf,stdout) FLUSH;
  610.             resetty();
  611.             gets(scrbuf);
  612.             noecho();
  613.             crmode();
  614.             s = scrbuf;
  615.             break;
  616. #endif
  617.         case '~':
  618.             s = homedir;
  619.             break;
  620.         case '.':
  621.             s = dotdir;
  622.             break;
  623.         case '$':
  624.             s = scrbuf;
  625.             sprintf(s,"%d",getpid());
  626.             break;
  627.         case '#':
  628.             s = scrbuf;
  629.             sprintf(s,"%d",perform_cnt);
  630.             break;
  631.         case '0': case '1': case '2': case '3': case '4':
  632.         case '5': case '6': case '7': case '8': case '9':
  633. #ifdef CONDSUB
  634.             s = getbracket(bra_compex,*pattern - '0');
  635. #else
  636.             s = nullstr;
  637. #endif
  638.             break;
  639.         case 'a':
  640.             s = scrbuf;
  641.             sprintf(s,"%ld",(long)art);
  642.             break;
  643.         case 'A':
  644. #ifdef LINKART
  645.             s = linkartname;    /* so Eunice people get right file */
  646. #else
  647.             s = scrbuf;
  648.             sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
  649. #endif
  650.             break;
  651.         case 'b':
  652.             s = savedest;
  653.             break;
  654.         case 'B':
  655.             s = scrbuf;
  656.             sprintf(s,"%ld",(long)savefrom);
  657.             break;
  658.         case 'c':
  659.             s = ngdir;
  660.             break;
  661.         case 'C':
  662.             s = ngname;
  663.             break;
  664.         case 'd':
  665.             s = scrbuf;
  666.             sprintf(s,"%s/%s",spool,ngdir);
  667.             break;
  668.         case 'D':
  669.             s = dist_buf = fetchlines(art,DIST_LINE);
  670.             break;
  671.         case 'e':
  672.             s = extractprog;
  673.             break;
  674. #ifdef USETHREADS
  675.         case 'E': {
  676.             int sel, unseen;
  677.  
  678.             sel = curr_p_art && (selected_roots[curr_p_art->root] & 1);
  679.             unseen = (art <= lastart) && !was_read(art);
  680.             sprintf(scrbuf,"%ld",(long)toread[ng]-selected_count
  681.                         -unthreaded-(!sel && unseen));
  682.             s = scrbuf;
  683.             break;
  684.         }
  685. #endif
  686.         case 'f':            /* from line */
  687. #ifdef ASYNC_PARSE
  688.             parse_maybe(art);
  689. #endif
  690.             if (htype[REPLY_LINE].ht_minpos >= 0) {
  691.                         /* was there a reply line? */
  692.             if (!(s=reply_buf))
  693.                 s = reply_buf = fetchlines(art,REPLY_LINE);
  694.             }
  695.             else if (!(s = from_buf))
  696.             s = from_buf = fetchlines(art,FROM_LINE);
  697.             break;
  698.         case 'F':
  699. #ifdef ASYNC_PARSE
  700.             parse_maybe(art);
  701. #endif
  702.             if (htype[FOLLOW_LINE].ht_minpos >= 0)
  703.                     /* is there a Followup-To line? */
  704.             s = follow_buf = fetchlines(art,FOLLOW_LINE);
  705.             else {
  706.             int off;
  707.         
  708.             s = ngs_buf = fetchlines(art,NGS_LINE);
  709.             if (h = instr(s,"net.general")) {
  710.                 off = h-s;
  711.                 strncpy(scrbuf,s,off+4);
  712.                 strcpy(scrbuf+off+4,"followup");
  713.                 safecpy(scrbuf+off+12,h+11,sizeof(scrbuf));
  714.                 s = scrbuf;
  715.             }
  716.             }
  717.             break;
  718.         case 'h':            /* header file name */
  719.             s = headname;
  720.             break;
  721.         case 'H':            /* host name */
  722.             s = sitename;
  723.             break;
  724.         case 'i':
  725.             if (!(s=artid_buf))
  726.             s = artid_buf = fetchlines(art,MESSID_LINE);
  727.             if (*s && *s != '<') {
  728.             sprintf(scrbuf,"<%s>",artid_buf);
  729.             s = scrbuf;
  730.             }
  731.             break;
  732.         case 'I':            /* ref article indicator */
  733.             s = scrbuf;
  734.             sprintf(scrbuf,"'%s'",indstr);
  735.             break;
  736.         case 'l':            /* rn library */
  737. #ifdef NEWSADMIN
  738.             s = newsadmin;
  739. #else
  740.             s = "???";
  741. #endif
  742.             break;
  743.         case 'L':            /* login id */
  744.             s = logname;
  745.             break;
  746.         case 'm':        /* current mode */
  747.             s = scrbuf;
  748.             *s = mode;
  749.             s[1] = '\0';
  750.             break;
  751.         case 'M':
  752. #ifdef DELAYMARK
  753.             sprintf(scrbuf,"%ld",(long)dmcount);
  754.             s = scrbuf;
  755. #else
  756.             s = nullstr;
  757. #endif
  758.             break;
  759.         case 'n':            /* newsgroups */
  760.             s = ngs_buf = fetchlines(art,NGS_LINE);
  761.             break;
  762.         case 'N':            /* full name */
  763.             s = getval("NAME",realname);
  764.             break;
  765.         case 'o':            /* organization */
  766.             s = getval("ORGANIZATION",orgname);
  767. #ifdef ORGFILE
  768.             if (*s == '/') {
  769.             FILE *ofp = fopen(s,"r");
  770.  
  771.             if (ofp) {
  772.                 fgets(scrbuf,sizeof scrbuf,ofp);
  773.                 fclose(ofp);
  774.                 s = scrbuf;
  775.                 s[strlen(s)-1] = '\0';
  776.             }
  777.             }
  778. #endif
  779.             break;
  780.         case 'O':
  781.             s = origdir;
  782.             break;
  783.         case 'p':
  784.             s = cwd;
  785.             break;
  786.         case 'P':
  787.             s = spool;
  788.             break;
  789.         case 'r':
  790. #ifdef ASYNC_PARSE
  791.             parse_maybe(art);
  792. #endif
  793.             if (htype[REFS_LINE].ht_minpos >= 0) {
  794.             refs_buf = fetchlines(art,REFS_LINE);
  795.             refscpy(scrbuf,(sizeof scrbuf),refs_buf);
  796.             }
  797.             else
  798.             *scrbuf = '\0';
  799.             s = rindex(scrbuf,'<');
  800.             break;
  801.         case 'R':
  802. #ifdef ASYNC_PARSE
  803.             parse_maybe(art);
  804. #endif
  805.             if (htype[REFS_LINE].ht_minpos >= 0) {
  806.             refs_buf = fetchlines(art,REFS_LINE);
  807.             refscpy(scrbuf,(sizeof scrbuf),refs_buf);
  808.             /* no more than 3 prior references allowed,
  809.             ** including the one concatenated below */
  810.             if ((s = rindex(scrbuf,'<')) > scrbuf) {
  811.                 *s = '\0';
  812.                 h = rindex(scrbuf,'<');
  813.                 *s = '<';
  814.                 if (h > scrbuf)
  815.                 strcpy(scrbuf,h);
  816.             }
  817.             }
  818.             else
  819.             *scrbuf = '\0';
  820.             if (!artid_buf)
  821.             artid_buf = fetchlines(art,MESSID_LINE);
  822.             if (artid_buf[0] == '<')
  823.             safecat(scrbuf,artid_buf,sizeof(scrbuf));
  824.             else if (artid_buf[0]) {
  825.             char tmpbuf[64];
  826.     
  827.             sprintf(tmpbuf,"<%s>",artid_buf);
  828.             safecat(scrbuf,tmpbuf,sizeof(scrbuf));
  829.             }
  830.             s = scrbuf;
  831.             break;
  832.         case 's':
  833.             if (!(s=subj_buf))
  834.             s = subj_buf = fetchsubj(art,TRUE,TRUE);
  835.                         /* get subject handy */
  836.             while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
  837.                         /* skip extra Re: */
  838.             s += 3;
  839.             if (*s == ' ')
  840.                 s++;
  841.             }
  842.             if (h = instr(s,"- (nf"))
  843.             *h = '\0';
  844.             break;
  845.         case 'S':
  846.             if (!(s=subj_buf))
  847.             s = subj_buf = fetchsubj(art,TRUE,TRUE);
  848.                         /* get subject handy */
  849.             if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
  850.                         /* skip extra Re: */
  851.             s += 3;
  852.             if (*s == ' ')
  853.                 s++;
  854.             }
  855.             break;
  856.         case 't':
  857.         case 'T':
  858. #ifdef ASYNC_PARSE
  859.             parse_maybe(art);
  860. #endif
  861.             if (htype[REPLY_LINE].ht_minpos >= 0) {
  862.                     /* was there a reply line? */
  863.             if (!(s=reply_buf))
  864.                 s = reply_buf = fetchlines(art,REPLY_LINE);
  865.             }
  866.             else if (!(s = from_buf))
  867.             s = from_buf = fetchlines(art,FROM_LINE);
  868.             if (*pattern == 'T') {
  869.             if (htype[PATH_LINE].ht_minpos >= 0) {
  870.                     /* should we substitute path? */
  871.                 s = path_buf = fetchlines(art,PATH_LINE);
  872.             }
  873.             i = strlen(sitename);
  874.             if (strnEQ(sitename,s,i) && s[i] == '!')
  875.                 s += i + 1;
  876.             }
  877.             if ((h=index(s,'(')) != Nullch)
  878.                         /* strip garbage from end */
  879.             *(h-1) = '\0';
  880.             else if ((h=index(s,'<')) != Nullch) {
  881.                         /* or perhaps from beginning */
  882.             s = h+1;
  883.             if ((h=index(s,'>')) != Nullch)
  884.                 *h = '\0';
  885.             }
  886.             break;
  887.         case 'u':
  888.             sprintf(scrbuf,"%ld",(long)toread[ng]);
  889.             s = scrbuf;
  890.             break;
  891.         case 'U': {
  892.             int unseen;
  893.  
  894.             unseen = (art <= lastart) && !was_read(art);
  895. #ifdef USETHREADS
  896.             if (selected_root_cnt) {
  897.             int sel;
  898.  
  899.             sel = curr_p_art
  900.                 && (selected_roots[curr_p_art->root] & 1);
  901.             sprintf(scrbuf,"%ld",
  902.                 (long)selected_count-(sel && unseen));
  903.             }
  904.             else
  905.             sprintf(scrbuf,"%ld",(long)toread[ng]-unthreaded
  906.                         -unseen);
  907. #else
  908.             sprintf(scrbuf,"%ld",(long)toread[ng]-unseen);
  909. #endif
  910.             s = scrbuf;
  911.             break;
  912.         }
  913.         case 'x':            /* news library */
  914.             s = lib;
  915.             break;
  916.         case 'X':            /* rn library */
  917.             s = rnlib;
  918.             break;
  919.         case 'z':
  920. #ifdef LINKART
  921.             s = linkartname;    /* so Eunice people get right file */
  922. #else
  923.             s = scrbuf;
  924.             sprintf(s,"%ld",(long)art);
  925. #endif
  926.             if (stat(s,&filestat) < 0)
  927.             filestat.st_size = 0L;
  928.             sprintf(scrbuf,"%5ld",(long)filestat.st_size);
  929.             s = scrbuf;
  930.             break;
  931. #ifdef USETHREADS
  932.         case 'Z':
  933.             sprintf(scrbuf,"%ld",(long)selected_count);
  934.             s = scrbuf;
  935.             break;
  936. #endif
  937.         default:
  938.             if (--destsize <= 0)
  939.             abort_interp();
  940.             *dest++ = *pattern | metabit;
  941.             s = nullstr;
  942.             break;
  943.         }
  944.         }
  945.         if (!s)
  946.         s = nullstr;
  947.         pattern++;
  948.         if (upper || lastcomp) {
  949.         char *t;
  950.  
  951.         if (s != scrbuf) {
  952.             safecpy(scrbuf,s,(sizeof scrbuf));
  953.             s = scrbuf;
  954.         }
  955.         if (upper || !(t=rindex(s,'/')))
  956.             t = s;
  957.         while (*t && !isalpha(*t))
  958.             t++;
  959.         if (islower(*t))
  960.             *t = toupper(*t);
  961.         }
  962.         i = metabit;        /* maybe get into register */
  963.         if (s == dest) {
  964.         while (*dest) {
  965.             if (--destsize <= 0)
  966.             abort_interp();
  967.             *dest++ |= i;
  968.         }
  969.         }
  970.         else {
  971.         while (*s) {
  972.             if (--destsize <= 0)
  973.             abort_interp();
  974.             *dest++ = *s++ | i;
  975.         }
  976.         }
  977.     }
  978.     else {
  979.         if (--destsize <= 0)
  980.         abort_interp();
  981.         if (*pattern == '^' && pattern[1]) {
  982.         ++pattern;            /* skip uparrow */
  983.         i = *pattern;        /* get char into a register */
  984.         if (i == '?')
  985.             *dest++ = '\177' | metabit;
  986.         else if (i == '(') {
  987.             metabit = 0200;
  988.             destsize++;
  989.         }
  990.         else if (i == ')') {
  991.             metabit = 0;
  992.             destsize++;
  993.         }
  994.         else
  995.             *dest++ = i & 037 | metabit;
  996.         pattern++;
  997.         }
  998.         else if (*pattern == '\\' && pattern[1]) {
  999.         ++pattern;            /* skip backslash */
  1000.         i = *pattern;        /* get char into a register */
  1001.     
  1002.         /* this used to be a switch but the if may save space */
  1003.         
  1004.         if (i >= '0' && i <= '7') {
  1005.             i = 1;
  1006.             while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
  1007.             i <<= 3;
  1008.             i += *pattern++ - '0';
  1009.             }
  1010.             *dest++ = i & 0377 | metabit;
  1011.             --pattern;
  1012.         }
  1013.         else if (i == 'b')
  1014.             *dest++ = '\b' | metabit;
  1015.         else if (i == 'f')
  1016.             *dest++ = '\f' | metabit;
  1017.         else if (i == 'n')
  1018.             *dest++ = '\n' | metabit;
  1019.         else if (i == 'r')
  1020.             *dest++ = '\r' | metabit;
  1021.         else if (i == 't')
  1022.             *dest++ = '\t' | metabit;
  1023.         else
  1024.             *dest++ = i | metabit;
  1025.         pattern++;
  1026.         }
  1027.         else
  1028.         *dest++ = *pattern++ | metabit;
  1029.     }
  1030.     }
  1031.     *dest = '\0';
  1032. getout:
  1033.     if (subj_buf != Nullch)    /* return any checked out storage */
  1034.     free(subj_buf);
  1035.     if (ngs_buf != Nullch)
  1036.     free(ngs_buf);
  1037.     if (refs_buf != Nullch)
  1038.     free(refs_buf);
  1039.     if (artid_buf != Nullch)
  1040.     free(artid_buf);
  1041.     if (reply_buf != Nullch)
  1042.     free(reply_buf);
  1043.     if (from_buf != Nullch)
  1044.     free(from_buf);
  1045.     if (path_buf != Nullch)
  1046.     free(path_buf);
  1047.     if (follow_buf != Nullch)
  1048.     free(follow_buf);
  1049.     if (dist_buf != Nullch)
  1050.     free(dist_buf);
  1051.     if (line_buf != Nullch)
  1052.     free(line_buf);
  1053.     return pattern;            /* where we left off */
  1054. }
  1055.  
  1056. void
  1057. interp(dest,destsize,pattern)
  1058. char *dest;
  1059. int destsize;
  1060. char *pattern;
  1061. {
  1062.     dointerp(dest,destsize,pattern,Nullch);
  1063. #ifdef DEBUGGING
  1064.     if (debug & DEB_FILEXP)
  1065.     fputs(dest,stdout);
  1066. #endif
  1067. }
  1068.  
  1069. /* copy a references line, normalizing as we go */
  1070.  
  1071. void
  1072. refscpy(dest,destsize,src)
  1073. register char *dest, *src;
  1074. register int destsize;
  1075. {
  1076.     register char *dot, *at, *beg;
  1077.     char tmpbuf[64];
  1078.     
  1079.     while (*src) {
  1080.     if (*src != '<') {
  1081.         if (--destsize <= 0)
  1082.         break;
  1083.         *dest++ = '<';
  1084.         at = dot = Nullch;
  1085.         beg = src;
  1086.         while (*src && *src != ' ' && *src != ',') {
  1087.         if (*src == '.')
  1088.             dot = src;
  1089.         else if (*src == '@')
  1090.             at = src;
  1091.         if (--destsize <= 0)
  1092.             break;
  1093.         *dest++ = *src++;
  1094.         }
  1095.         if (destsize <= 0)
  1096.         break;
  1097.         if (dot && !at) {
  1098.         int len;
  1099.  
  1100.         *dest = *dot++ = '\0';
  1101.         sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
  1102.         len = strlen(tmpbuf);
  1103.         if (destsize > len) {
  1104.             strcpy(dest,tmpbuf);
  1105.             dest = dest + len;
  1106.             destsize -= len;
  1107.         }
  1108.         }
  1109.         if (--destsize <= 0)
  1110.         break;
  1111.         *dest++ = '>';
  1112.     }
  1113.     else {
  1114.         while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
  1115.         if (destsize <= 0)
  1116.         break;
  1117.     }
  1118.     while (*src == ' ' || *src == ',') src++;
  1119.     if (*src && --destsize > 0)
  1120.         *dest++ = ' ';
  1121.     }
  1122.     *dest = '\0';
  1123.  
  1124. /* get the person's real name from /etc/passwd */
  1125. /* (string is overwritten, so it must be copied) */
  1126.  
  1127. char *
  1128. getrealname(uid)
  1129. int uid;
  1130. {
  1131.     char *s, *c;
  1132.     char tmpbuf[512];
  1133.  
  1134. #ifdef PASSNAMES
  1135. #ifdef GETPWENT
  1136.     struct passwd *pwd = getpwuid(uid);
  1137.     
  1138.     s = pwd->pw_gecos;
  1139. #else
  1140.     int i;
  1141.  
  1142.     getpw(uid, tmpbuf);
  1143.     for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
  1144.     if (s)
  1145.         s = index(s,':')+1;
  1146.     }
  1147.     if (!s)
  1148.     return nullstr;
  1149.     cpytill(tmpbuf,s,':');
  1150.     s = tmpbuf;
  1151. #endif
  1152. #ifdef BERKNAMES
  1153. #ifdef BERKJUNK
  1154.     while (*s && !isalnum(*s) && *s != '&') s++;
  1155. #endif
  1156.     if ((c = index(s, ',')) != Nullch)
  1157.     *c = '\0';
  1158.     if ((c = index(s, ';')) != Nullch)
  1159.     *c = '\0';
  1160.     s = cpytill(buf,s,'&');
  1161.     if (*s == '&') {            /* whoever thought this one up was */
  1162.     c = buf + strlen(buf);        /* in the middle of the night */
  1163.     strcat(c,logname);        /* before the morning after */
  1164.     strcat(c,s+1);
  1165.     if (islower(*c))
  1166.         *c = toupper(*c);        /* gack and double gack */
  1167.     }
  1168. #else
  1169.     if ((c = index(s, '(')) != Nullch)
  1170.     *c = '\0';
  1171.     if ((c = index(s, '-')) != Nullch)
  1172.     s = c;
  1173.     strcpy(buf,tmpbuf);
  1174. #endif
  1175. #ifdef GETPWENT
  1176.     endpwent();
  1177. #endif
  1178.     return buf;                /* return something static */
  1179. #else
  1180.     if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
  1181.     fgets(buf,sizeof buf,tmpfp);
  1182.     fclose(tmpfp);
  1183.     buf[strlen(buf)-1] = '\0';
  1184.     return buf;
  1185.     }
  1186.     return "PUT YOUR NAME HERE";
  1187. #endif
  1188. }
  1189.  
  1190. static void
  1191. abort_interp()
  1192. {
  1193.     fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
  1194.     sig_catcher(0);
  1195. }
  1196.  
  1197.  
  1198.