1: /*
   2:  * visual - visual news interface.
   3:  * Kenneth Almquist
   4:  */
   5: 
   6: #ifdef SCCSID
   7: static char *SccsId = "@(#)visual.c	1.28	3/19/86";
   8: #endif /* SCCSID */
   9: 
  10: #include "rparams.h"
  11: #ifdef USG
  12: #include <sys/ioctl.h>
  13: #include <termio.h>
  14: #include <fcntl.h>
  15: #else /* !USG */
  16: #include <sgtty.h>
  17: #endif /* !USG */
  18: 
  19: #include <errno.h>
  20: #if defined(BSD4_2) || defined(BSD4_1C)
  21: #include <sys/dir.h>
  22: #else
  23: #include "ndir.h"
  24: #endif
  25: #ifdef BSD4_2
  26: #ifndef sigmask
  27: #define sigmask(m) (1<<((m)-1))
  28: #endif /* !sigmask */
  29: #endif /* BSD4_2 */
  30: #ifdef MYDB
  31: #include "db.h"
  32: #endif /* MYDB */
  33: 
  34: extern int errno;
  35: 
  36: #ifdef SIGTSTP
  37: #include <setjmp.h>
  38: #endif /* SIGTSTP */
  39: 
  40: #define ARTWLEN (ROWS-2)/* number of lines used to display article */
  41: #define even(cols) ((cols&1) ? cols + 1 : cols)
  42: #ifdef STATTOP
  43: #define PRLINE  0   /* prompter line */
  44: #define SPLINE  1   /* secondary prompt line */
  45: #define ARTWIN  2   /* first line of article window */
  46: #define SECPRLEN 81 /* length of secondary prompter */
  47: #else
  48: #define PRLINE  (ROWS-1)/* prompter line */
  49: #define SPLINE  (ROWS-2)/* secondary prompt line */
  50: #define ARTWIN  0   /* first line of article window */
  51: #define SECPRLEN 100    /* length of secondary prompter */
  52: #endif
  53: 
  54: #define PIPECHAR '|'    /* indicate save command should pipe to program */
  55: #define META    0200    /* meta character bit (as in emacs) */
  56: /* print (display) flags */
  57: #define HDRONLY 0001    /* print header only */
  58: #define NOPRT   0002    /* don't print at all */
  59: #define NEWART  0004    /* force article display to be regenerated */
  60: #define HELPMSG 0010    /* display currently contains help message */
  61: /* prun flags */
  62: #define CWAIT   0001    /* type "continue?" and wait for return */
  63: #define BKGRND  0002    /* run process in the background */
  64: /* values of curflag */
  65: #define CURP1   1   /* cursor after prompt */
  66: #define CURP2   2   /* cursor after secondary prompt */
  67: #define CURHOME 3   /* cursor at home position */
  68: /* flags for vsave routine */
  69: #define SVHEAD  01  /* write out article header */
  70: #define OVWRITE 02  /* overwrite the file if it already exists */
  71: /* other files */
  72: 
  73: #define saveart oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hptr = h;h = hold;hold = hptr;ongsize = pngsize
  74: #define NLINES(h, fp) (h->numlines[0] ? h->intnumlines : (h->intnumlines=linecnt(fp),sprintf(h->numlines, "%d", h->intnumlines), h->intnumlines))
  75: 
  76: /* terminal handler stuff */
  77: extern int _junked;
  78: #define clearok(xxx, flag) _junked = flag
  79: extern int COLS;
  80: extern int ROWS;
  81: extern int hasscroll;
  82: 
  83: FILE *tmpfile();
  84: char *getmailname();
  85: #ifdef MYDB
  86: char *findparent();
  87: #endif /* MYDB */
  88: int onint();
  89: int onstop();
  90: int xxit();
  91: 
  92: char *Progname = "vnews";       /* for xerror */
  93: 
  94: /* variables shared between vnews routines */
  95: static char linebuf[LBUFLEN];       /* temporary workspace */
  96: static FILE *tfp;           /* temporary file */
  97: static char tfname[] = "/tmp/vnXXXXXX"; /* name of temp file */
  98: static long artbody;            /* offset of body into article */
  99: static int quitflg;         /* if set, then quit */
 100: static int erased;          /* current article has been erased */
 101: static int artlines;            /* # lines in article body */
 102: static int artread;         /* entire article has been read */
 103: static int hdrstart;            /* beginning of header */
 104: static int hdrend;          /* end of header */
 105: static int lastlin;         /* number of lines in tempfile */
 106: static int tflinno = 0;         /* next line in tempfile */
 107: static int maxlinno;            /* number of lines in file + folded */
 108: static char secpr[SECPRLEN];        /* secondary prompt */
 109: static char prompt[30];         /* prompter */
 110: static short prflags;           /* print flags (controls updscr) */
 111: static short curflag;           /* where to locate cursor */
 112: static int dlinno;          /* top line on screen */
 113: static char timestr[20];        /* current time */
 114: static int ismail;          /* true if user has mail */
 115: static char *mailf;         /* user's mail file */
 116: static int alflag;          /* set if unprocessed alarm signal */
 117: static int atend;           /* set if at end of article */
 118: static char cerase;         /* erase character */
 119: static char ckill;          /* kill character */
 120: static char cintr;          /* interrupt character */
 121: #ifdef TIOCGLTC
 122: static char cwerase;            /* word erase character */
 123: #endif /* TIOCGLTC */
 124: short ospeed;               /* terminal speed NOT STATIC */
 125: static int intflag;         /* set if interrupt received */
 126: 
 127: #ifdef SIGTSTP
 128: static int reading;         /* to keep stupid BSD from restarting reads */
 129: jmp_buf intjmp, alrmjmp;
 130: #endif /* SIGTSTP */
 131: 
 132: #ifdef MYDB
 133: static int hasdb;           /* true if article data base exists */
 134: #endif /* MYDB */
 135: 
 136: #ifdef DIGPAGE
 137: static int endsuba;         /* end of sub-article in digest */
 138: #endif
 139: 
 140: #ifdef MYDEBUG
 141: FILE *debugf;               /* file to write debugging info on */
 142: #endif
 143: 
 144: char *tft = "/tmp/folXXXXXX";
 145: 
 146: /*
 147:  * These were made static for u370 with its buggy cc.
 148:  * I judged it better to have one copy with no ifdefs than
 149:  * to conditionally compile them as automatic variables
 150:  * in readr (which they originally were).  Performance
 151:  * considerations might warrant moving some of the simple
 152:  * things into register variables, but I don't know what
 153:  * breaks the u370 cc.
 154:  */
 155: static char goodone[BUFLEN];        /* last decent article		*/
 156: static char ogroupdir[BUFLEN];      /* last groupdir		*/
 157: static char edcmdbuf[128];
 158: static int rfq = 0;         /* for last article		*/
 159: static long ongsize;            /* Previous ngsize		*/
 160: static long pngsize;            /* Printing ngsize		*/
 161: static char *bptr;          /* temp pointer.		*/
 162: static char *tfilename;         /* temporary file name 		*/
 163: static char ofilename1[BUFLEN];     /* previous file name		*/
 164: static struct hbuf hbuf1, hbuf2;    /* for minusing			*/
 165: static struct hbuf *h = &hbuf1,     /* current header		*/
 166:         *hold = &hbuf2,     /* previous header		*/
 167:         *hptr;          /* temporary			*/
 168: static char *ptr1, *ptr2, *ptr3;    /* for reply manipulation	*/
 169: static int  aabs = FALSE;       /* TRUE if we asked absolutely	*/
 170: static char *ed, tf[100];
 171: static long oobit;          /* last bit, really		*/
 172: static int dgest = 0;
 173: static FILE *fp;            /* current article to be printed*/
 174: 
 175: readr()
 176: {
 177: 
 178: #ifdef MYDEBUG
 179:     debugf = fopen("DEBUG", "w");
 180:     setbuf(debugf, (char *)NULL);
 181: #endif
 182:     if (aflag) {
 183:         if (*datebuf) {
 184:             if ((atime = cgtdate(datebuf)) == -1)
 185:                 xerror("Cannot parse date string");
 186:         } else
 187:             atime = 0;
 188:     }
 189: 
 190:     if (SigTrap)
 191:         xxit(1);
 192:     (void) mktemp(tfname);
 193:     (void) close(creat(tfname,0666));
 194:     if ((tfp = fopen(tfname, "w+")) == NULL)
 195:         xerror("Can't create temp file");
 196:     (void) unlink(tfname);
 197:     mailf = getmailname();
 198: #ifdef MYDB
 199:     if (opendb() >= 0) {
 200:         hasdb = 1;
 201:         fputs("Using article data base\n", stderr); /*DEBUG*/
 202:         getng();
 203:     }
 204: #endif
 205:     ttysave();
 206:     (void) signal(SIGINT, onint);
 207:     (void) signal(SIGQUIT, xxit);
 208:     if (SigTrap)
 209:         xxit(1);
 210:     ttyraw();
 211:     timer();
 212: 
 213:     /* loop reading articles. */
 214:     fp = NULL;
 215:     obit = -1;
 216:     nextng();
 217:     quitflg = 0;
 218:     while (quitflg == 0) {
 219:         if (getnextart(FALSE))
 220:             break;
 221:         (void) strcpy(goodone, filename);
 222:         if (SigTrap)
 223:             return;
 224:         vcmd();
 225:     }
 226: 
 227:     if (!news) {
 228:         if (!checkngs(header.nbuf, actfp))
 229:             fprintf(stderr, "No news.\n");
 230:     }
 231: }
 232: 
 233: /*
 234:  * Read and execute a command.
 235:  */
 236: vcmd() {
 237:     register c;
 238:     char *p;
 239:     long count;
 240:     int countset;
 241: 
 242:     appfile(fp, dlinno + ARTWLEN + 1);
 243: #ifdef DIGPAGE
 244:     endsuba = findend(dlinno);
 245:     if (artlines > dlinno + ARTWLEN
 246:      || endsuba > 0 && endsuba < artlines
 247: #else
 248:     if (artlines > dlinno + ARTWLEN
 249: #endif
 250:      || (prflags & HDRONLY) && artlines > hdrend) {
 251:         atend = 0;
 252:         if (prflags&HDRONLY || maxlinno == 0)
 253:             (void) strcpy(prompt, "more? ");
 254:         else
 255: #ifdef DIGPAGE
 256:             (void) sprintf(prompt, "more(%d%%)? ",
 257:                 ((((endsuba > 0) ?
 258:                 endsuba : (dlinno + ARTWLEN)) -
 259:                 hdrend) * 100) / maxlinno);
 260: #else /* !DIGPAGE */
 261:             (void) sprintf(prompt, "more(%d%%)? ",
 262:                 ((dlinno + ARTWLEN - hdrend) * 100) / maxlinno);
 263: #endif /* !DIGPAGE */
 264:     } else {
 265:         atend = 1;
 266:         (void) strcpy(prompt, "next? ");
 267:         if (!erased)
 268:             clear(bit);     /* article read */
 269:     }
 270:     curflag = CURP1;
 271:     p = prompt + strlen(prompt);
 272:     countset = 0;
 273:     count = 0;
 274:     /*
 275: 	 * Loop while accumulating a count, until an action character
 276: 	 * is entered. Also handle "meta" here.
 277: 	 *
 278: 	 * Count is the current count. Countset=0 means no count
 279: 	 * currently exists. Countset=1, count=0 is valid and means
 280: 	 * a count of 0 has been entered
 281: 	 */
 282:     for (;;) {
 283:         c = vgetc();
 284:         if (c == cerase || c == '\b' || c == '\177') {
 285:             if (countset == 0)
 286:                 break;      /* Use as action char */
 287:             if (count < 10)
 288:                 countset = 0;   /* Erase only char of count */
 289:             else
 290:                 count /= 10L;   /* Erase 1 char of count */
 291:         } else {
 292: #ifdef TIOCGLTC
 293:             if (c == ckill || c == cwerase) {
 294: #else
 295:             if (c == ckill) {
 296: #endif
 297:                 if (countset == 0)
 298:                     break;
 299:                 countset = 0;
 300:             } else if (c < '0' || c > '9')
 301:                     break;
 302:                 else {
 303:                     countset = 1;
 304:                     count = (count * 10) + (c - '0');
 305:                 }
 306:         }
 307:         if (countset) {
 308:             (void) sprintf(p, "%ld", count);
 309:         } else {
 310:             *p = '\0';
 311:             count = 0;
 312:         }
 313:     }
 314: 
 315:     if (c == '\033') {          /* escape */
 316:         (void) strcat(prompt, "M-");
 317:         c = vgetc();
 318:         if (c != cintr)
 319:             c |= META;
 320:     }
 321:     secpr[0] = '\0';
 322:     if (countset == 0)
 323:         count = 1;
 324:     docmd(c, count);
 325:     if (c != '?' && c != 'H')       /* UGGH */
 326:         prflags &=~ HELPMSG;
 327:     if (dlinno > hdrstart)
 328:         prflags &=~ HDRONLY;
 329: }
 330: 
 331: 
 332: /*
 333:  * Process one command, which has already been typed in.
 334:  */
 335: docmd(c, count)
 336: int c;
 337: long count;
 338: {
 339:     int i;
 340:     long nart, Hoffset;
 341:     char *findhist();
 342: 
 343:     switch (c) {
 344: 
 345:     /* Show more of current article, or advance to next article */
 346:     case '\n':
 347:     case ' ':
 348: #ifdef DIGPAGE
 349:     case 'm':
 350: #endif /* DIGPAGE */
 351:     case '\06': /* Control-F for vi compat */
 352:         prflags &=~ NOPRT;
 353:         if (atend)
 354:             goto next;
 355:         else if (prflags & HDRONLY) {
 356:             prflags &=~ HDRONLY;
 357:             if (hasscroll)
 358:                 dlinno = hdrstart;}
 359: #ifdef DIGPAGE
 360:         else if (endsuba > 0)
 361:             dlinno = endsuba;
 362:         else if (c == 'm') {
 363:             do {
 364:                 if (lastlin >= maxlinno)
 365:                     goto next;
 366:                 else
 367:                     appfile(fp, lastlin + 1);
 368:             } while(strncmp(linebuf, "------------------------", 24)
 369:                 != 0);
 370:             dlinno = endsuba = lastlin;
 371:         }
 372: #endif
 373:         else if ((appfile(fp, dlinno + 2 * ARTWLEN), artread)
 374:          && hasscroll && artlines - dlinno <= ARTWLEN + 2)
 375:             dlinno = artlines - ARTWLEN;
 376:         else
 377:             dlinno += ARTWLEN * count;
 378:         break;
 379: 
 380:     /* No.  Go on to next article. */
 381:     case '.':   /* useful if you have a keypad */
 382: next:   case 'n':
 383:         readmode = NEXT;
 384:         FCLOSE(fp);
 385:         clear(bit);
 386:         saveart;
 387:         nextbit();
 388:         break;
 389: 
 390: 
 391:     /* Back up count pages */
 392:     case '\b':
 393:     case '\177':
 394:         if (dlinno == 0)
 395:             goto backupone;
 396:         /* NO BREAK */
 397:     case META|'v':
 398:     case '\002':    /* Control-B */
 399:         dlinno -= ARTWLEN * count;
 400:         if (dlinno < 0)
 401:             dlinno = 0;
 402:         break;
 403: 
 404:     /* forward half a page */
 405:     case '\004':    /* Control-D, as in vi */
 406:         if (!atend)
 407:             dlinno += ARTWLEN/2 * count;
 408:         break;
 409: 
 410:     /* backward half a page */
 411:     case '\025':    /* Control-U */
 412:         dlinno -= ARTWLEN/2 * count;
 413:         if (dlinno < 0)
 414:             dlinno = 0;
 415:         break;
 416: 
 417:     /* forward count lines */
 418:     case '\016':    /* Control-N */
 419:     case '\005':    /* Control-E */
 420:         dlinno += count;
 421:         break;
 422: 
 423:     /* backwards count lines */
 424:     case '\020':    /* Control-P */
 425:     case '\031':    /* Control-Y */
 426:         dlinno -= count;
 427:         if (dlinno < 0)
 428:             dlinno = 0;
 429:         break;
 430: 
 431:     /* Turn displaying of article back on */
 432:     case 'l':
 433:     case 'd':
 434:         prflags &=~ NOPRT;
 435:         break;
 436: 
 437:     /* display header */
 438:     case 'h':
 439:         dlinno = hdrstart;
 440:         prflags |= HDRONLY;
 441:         prflags &=~ NOPRT;
 442:         break;
 443: 
 444:     /*
 445: 	 * Unsubscribe to the newsgroup and go on to next group
 446: 	 */
 447: 
 448:     case 'U':
 449:     case 'u':
 450:         strcat(prompt, "u");
 451:         c = vgetc();
 452:         if (c == 'g') {
 453:             obit = -1;
 454:             FCLOSE(fp);
 455:             zapng = TRUE;
 456:             saveart;
 457:             if (nextng()) {
 458:                 if (actdirect == BACKWARD)
 459:                     msg("Can't back up.");
 460:                 else
 461:                     quitflg = 1;    /* probably unnecessary */
 462:             }
 463:         } else {
 464:             if (c != cintr && c != ckill)
 465:                 msg("Illegal command");
 466:         }
 467:         break;
 468: 
 469:         /* Print the current version of news */
 470:     case 'v':
 471:         msg("News version: %s", news_version);
 472:         break;
 473: 
 474: 
 475:     /* Decrypt joke.  Always does rot 13 */
 476:     case 'D':
 477:         appfile(fp, 32767);
 478:         for (i = hdrend ; i < artlines ; i++) {
 479:             register char ch, *p;
 480:             tfget(linebuf, i);
 481:             for (p = linebuf ; (ch = *p) != '\0' ; p++) {
 482:                 if (ch >= 'a' && ch <= 'z')
 483:                     *p = (ch - 'a' + 13) % 26 + 'a';
 484:                 else if (ch >= 'A' && ch <= 'Z')
 485:                     *p = (ch - 'A' + 13) % 26 + 'A';
 486:             }
 487:             tfput(linebuf, i);
 488:         }
 489:         prflags |= NEWART;
 490:         prflags &=~ (HDRONLY|NOPRT);
 491:         break;
 492: 
 493:         /* write out the article someplace */
 494:         /* w writes out without the header */
 495:     case 's':
 496:     case 'w': {
 497:         char *grn = groupdir;
 498:         int wflags;
 499: 
 500:         msg("file: ");
 501:         curflag = CURP2;
 502:         while ((wflags = vgetc()) == ' ');
 503:         if (wflags == cintr) {
 504:             secpr[0] = '\0';
 505:             break;
 506:         }
 507:         if (wflags == '|') {
 508:             linebuf[0] = '|';
 509:             if (prget("| ", linebuf+1))
 510:                 break;
 511:         } else {
 512:             pushback(wflags);
 513:             if (prget("file: ", linebuf))
 514:                 break;
 515:         }
 516:         wflags = 0;
 517:         if (c == 's')
 518:             wflags |= SVHEAD;
 519:         if (count != 1)
 520:             wflags |= OVWRITE;
 521:         bptr = linebuf;
 522:         while( *bptr == ' ')
 523:             bptr++; /* strip leading spaces */
 524: 
 525:         if (*bptr != PIPECHAR && *bptr != '/') {
 526:             char    hetyped[BUFLEN];
 527:             char    *boxptr;
 528:             (void) strcpy(hetyped, bptr);
 529:             if (hetyped[0] == '~' && hetyped[1] == '/') {
 530:                 strcpy(hetyped, bptr+2);
 531:                 strcpy(bptr, userhome);
 532:             } else if (boxptr = getenv("NEWSBOX")) {
 533:                 if (index(boxptr, '%')) {
 534:                     struct stat stbf;
 535:                     sprintf(bptr, boxptr, grn);
 536:                     if (stat(bptr,&stbf) < 0) {
 537:                         if (mkdir(bptr, 0777) < 0) {
 538:                             msg("Cannot create directory %s", bptr);
 539:                             break;
 540:                         }
 541:                     } else if ((stbf.st_mode&S_IFMT) !=  S_IFDIR) {
 542:                         msg("%s not a directory", bptr);
 543:                         break;
 544:                     }
 545:                 } else
 546:                     strcpy(bptr, boxptr);
 547:              } else
 548:                 bptr[0] = '\0';
 549: 
 550:             if (bptr[0])
 551:                 (void) strcat(bptr, "/");
 552:             if (hetyped[0] != '\0')
 553:                 (void) strcat(bptr, hetyped);
 554:             else
 555:                 (void) strcat(bptr, "Articles");
 556:         }
 557:         vsave(bptr, wflags);
 558:         break;
 559:     }
 560: 
 561:         /* back up  */
 562:     case '-':
 563: caseminus:
 564:         aabs = TRUE;
 565:         if (!*ofilename1) {
 566:             msg("Can't back up.");
 567:             break;
 568:         }
 569:         FCLOSE(fp);
 570:         hptr = h;
 571:         h = hold;
 572:         hold = hptr;
 573:         (void) strcpy(bfr, filename);
 574:         (void) strcpy(filename, ofilename1);
 575:         (void) strcpy(ofilename1, bfr);
 576:         obit = bit;
 577:         if (strcmp(groupdir, ogroupdir)) {
 578:             (void) strcpy(bfr, groupdir);
 579:             selectng(ogroupdir, FALSE, FALSE);
 580:             (void) strcpy(groupdir, ogroupdir);
 581:             (void) strcpy(ogroupdir, bfr);
 582:             ngrp = 1;
 583:             back();
 584:         }
 585:         bit = oobit;
 586:         oobit = obit;
 587:         obit = -1;
 588:         getnextart(TRUE);
 589:         break;
 590: 
 591:         /* skip forwards */
 592:     case '+':
 593:     case '=':
 594: caseplus:   if (count == 0)
 595:             break;
 596:         saveart;
 597:         last = bit;
 598:         for (i = 0; i < count; i++) {
 599:             nextbit();
 600:             if ((bit > pngsize) || (rflag && bit < 1))
 601:                 break;
 602:         }
 603:         FCLOSE(fp);
 604:         obit = -1;
 605:         break;
 606: 
 607:     /* exit - time updated to that of most recently read article */
 608:     case 'q':
 609:         quitflg = 1;
 610:         break;
 611: 
 612:     case 'x':
 613:         xxit(0);
 614:         break;
 615: 
 616:     /* cancel the article. */
 617:     case 'c':
 618:         strcpy(prompt, "cancel [n]? ");
 619:         if (vgetc() != 'y') {
 620:             msg("Article not cancelled");
 621:             break;
 622:         }
 623:         cancel_command();
 624:         break;
 625: 
 626:     /* escape to shell */
 627:     case '!': {
 628:         register char *p;
 629:         int flags;
 630: 
 631:         p = linebuf;
 632:         if (prget("!", p))
 633:             break;
 634:         flags = CWAIT;
 635:         if (*p == '\0') {
 636:             (void) strcpy(linebuf, SHELL);
 637:             flags = 0;
 638:         }
 639:         while (*p) p++;
 640:         while (p > linebuf && p[-1] == ' ')
 641:             p--;
 642:         if (*--p == '&') {
 643:             *p = '\0';
 644:             flags = BKGRND;
 645:         } else if (*p == '|') {
 646:             *p = '\0';
 647:             (void) sprintf(bfr, "(%s)|mail '%s'", linebuf, username);
 648:             (void) strcpy(linebuf, bfr);
 649:             flags |= BKGRND;
 650:         } else {
 651:             prflags |= NOPRT;
 652:         }
 653:         shcmd(linebuf, flags);
 654:         break;
 655:     }
 656: 
 657:     /* mail reply */
 658:     case 'r':
 659:         reply(FALSE);
 660:         break;
 661: 
 662:     case 'R':
 663:         reply(TRUE);
 664:         break;
 665: 
 666:     case META|'r':
 667:         direct_reply();
 668:         break;
 669: 
 670:     /* next newsgroup */
 671:     case 'N':
 672:         FCLOSE(fp);
 673:         if (next_ng_command())
 674:             quitflg = 1;
 675:         break;
 676: 
 677:     /*  mark the rest of the articles in this group as read */
 678:     case 'K':
 679:         saveart;
 680:         while (bit <= ngsize && bit >= minartno) {
 681:             clear(bit);
 682:             nextbit();
 683:         }
 684:         FCLOSE(fp);
 685:         break;
 686: 
 687:     /* Print the full header */
 688:     case 'H':
 689:         if (fp == NULL) {
 690:             msg("No current article");
 691:             break;
 692:         }
 693:         move(ARTWIN, 0);
 694:         Hoffset = ftell(fp);
 695:         (void) fseek(fp, 0L, 0);
 696:         for (i = 0; i < ARTWLEN; i++) {
 697:             if (fgets(linebuf, COLS, fp) == NULL)
 698:                 break;
 699:             if (linebuf[0] == '\n')
 700:                 break;
 701:             linebuf[COLS] = '\0';
 702:             addstr(linebuf);
 703:         }
 704:         (void) fseek(fp, Hoffset, 0);
 705:         for(; i < ARTWLEN; i++)
 706:             addstr(linebuf);
 707:         prflags |= HELPMSG|NEWART;
 708:         break;
 709:     case 'b':   /* backup 1 article */
 710: backupone:
 711:         count = bit - 1;
 712:         /* NO BREAK */
 713: 
 714:     case 'A':   /* specific number */
 715:         if (count > pngsize) {
 716:             msg("not that many articles");
 717:             break;
 718:         }
 719:         readmode = SPEC;
 720:         aabs = TRUE;
 721:         bit = count;
 722:         obit = -1;
 723:         FCLOSE(fp);
 724:         break;
 725: 
 726:     /* display parent article */
 727:     case 'p':
 728: #ifdef MYDB
 729:         if (hasdb && (ptr3 = findparent(h->ident, &nart)) != NULL) {
 730:             msg("parent: %s/%ld", ptr3, nart);  /*DEBUG*/
 731:             updscr();               /*DEBUG*/
 732:             goto selectart;
 733:         }
 734: #endif
 735:         if (h->followid[0] == '\0') {
 736:             msg("no references line");
 737:             break;
 738:         }
 739:         ptr1 = h->followid + strlen(h->followid);
 740:         do {
 741:             ptr2 = ptr1;
 742:             if (*ptr2 == '\0')
 743:                 ptr1 = rindex(h->followid, ' ');
 744:             else {
 745:                 *ptr2 = '\0';
 746:                 ptr1 = rindex(h->followid, ' ');
 747:                 *ptr2 = ' ';
 748:             }
 749:         } while (ptr1 != NULL && --count > 0);
 750:         if (ptr1 == NULL)
 751:             ptr1 = h->followid;
 752:         else    ++ptr1;
 753:         (void) strncpy(linebuf, ptr1, ptr2 - ptr1);
 754:         linebuf[ptr2 - ptr1] = '\0';
 755:         msg("%s", linebuf);
 756:         curflag = CURP2;
 757:         updscr();       /* may take this out later */
 758:         goto searchid;
 759:     /* specific message ID. */
 760:     case '<':
 761:         /* could improve this */
 762:         linebuf[0] = '<';
 763:         if (prget("<", linebuf+1))
 764:             break;
 765: searchid:   secpr[0] = '\0';
 766:         if (index(linebuf, '@') == NULL && index(linebuf, '>') == NULL) {
 767:             ptr1 = linebuf;
 768:             if (*ptr1 == '<')
 769:                 ptr1++;
 770:             ptr2 = index(ptr1, '.');
 771:             if (ptr2 != NULL) {
 772:                 *ptr2++ = '\0';
 773:                 (void) sprintf(bfr, "<%s@%s.UUCP>", ptr2, ptr1);
 774:                 (void) strcpy(linebuf, bfr);
 775:             }
 776:         }
 777:         if (index(linebuf, '>') == NULL)
 778:             (void) strcat(linebuf, ">");
 779: 
 780:         ptr1 = findhist(linebuf);
 781:         if (ptr1 == NULL) {
 782:             msg("%s not found", linebuf);
 783:             break;
 784:         }
 785:         ptr2 = index(ptr1, '\t');
 786:         ptr3 = index(++ptr2, '\t');
 787:         ptr2 = index(++ptr3, ' ');
 788:         if (ptr2)
 789:             *ptr2 = '\0';
 790:         ptr2 = index(ptr3, '/');
 791:         if (!ptr2) {
 792:             if (strcmp(++ptr3, "cancelled") == 0)
 793:                 msg("%s has been cancelled", linebuf);
 794:             else
 795:                 msg("%s has expired", linebuf);
 796:             break;
 797:         }
 798:         *ptr2++ = '\0';
 799:         (void) sscanf(ptr2, "%ld", &nart);
 800: 
 801:         /*
 802: 		 * Go to a given article.  Ptr3 specifies the newsgroup
 803: 		 * and nart specifies the article number.
 804: 		 */
 805: #ifdef MYDB
 806: selectart:
 807: #endif /* MYDB */
 808:         aabs = TRUE;
 809:         FCLOSE(fp);
 810:         saveart;
 811:         (void) strcpy(ogroupdir, ptr3);
 812:         if (strcmp(groupdir, ogroupdir)) {
 813:             (void) strcpy(bfr, groupdir);
 814:             selectng(ogroupdir, TRUE, PERHAPS);
 815:             (void) strcpy(groupdir, ogroupdir);
 816:             (void) strcpy(ogroupdir, bfr);
 817:             ngrp = 1;
 818:             back();
 819:         }
 820:         bit = nart;
 821:         oobit = obit;
 822:         obit = -1;
 823:         getnextart(TRUE);
 824:         if (bit != nart || strcmp(groupdir, ptr3) != 0) {
 825:             msg("can't read %s/%ld", ptr3, nart);
 826:             goto caseminus;
 827:         }
 828:         rfq = 0;
 829:         break;
 830: 
 831:     /* follow-up article */
 832:     case 'f':
 833:         if (strcmp(h->followto, "poster") == 0) {
 834:             reply(FALSE);
 835:             break;
 836:         }
 837:         (void) sprintf(bfr, "%s/%s %s", BIN, "postnews", goodone);
 838:         shcmd(bfr, CWAIT);
 839:         break;
 840: 
 841:     /* erase - pretend we haven't seen this article. */
 842:     case 'e':
 843:         erased = 1;
 844:         set(bit);
 845:         goto caseplus;  /* skip this article for now */
 846: 
 847:     case '#':
 848:         msg("Article %ld of %ld", rfq ? oobit : bit, pngsize);
 849:         break;
 850: 
 851:         /* error */
 852:     case '?':
 853:         {
 854:             FILE *helpf;
 855:             (void) sprintf(linebuf, "%s/vnews.help", LIB);
 856:             if ((helpf = fopen(linebuf, "r")) == NULL) {
 857:                 msg("Can't open help file");
 858:                 break;
 859:             }
 860:             move(ARTWIN, 0);
 861:             while (fgets(linebuf, LBUFLEN, helpf) != NULL)
 862:                 addstr(linebuf);
 863:             (void) fclose(helpf);
 864:             prflags |= HELPMSG|NEWART;
 865:         }
 866:         break;
 867: 
 868:     default:
 869:         if (c != ckill && c != cintr && c != cerase)
 870: #ifdef TIOCGLTC
 871:             if (c != cwerase)
 872: #endif
 873:             msg("Illegal command");
 874:         break;
 875:     }
 876: 
 877:     return FALSE;
 878: }
 879: 
 880: cancel_command()
 881: {
 882:     int notauthor;
 883: 
 884:     tfilename = filename;
 885:     (void) strcpy(rcbuf, h->path);
 886:     ptr1 = index(rcbuf, ' ');
 887:     if (ptr1)
 888:         *ptr1 = 0;
 889:     notauthor = strcmp(username, rcbuf);
 890:     if (uid != ROOTID && uid && notauthor) {
 891:         msg("Can't cancel what you didn't write.");
 892:         return;
 893:     }
 894:     if (!cancel(stderr, h, notauthor)) {
 895:         clear(bit);
 896:         saveart;
 897:         nextbit();
 898:         obit = -1;
 899:         fp = NULL;
 900:     }
 901:     FCLOSE(fp);
 902: }
 903: /*
 904:  * Generate replies
 905:  */
 906: 
 907: reply(include)
 908:     int include;
 909: {
 910:     char *arg[4];
 911:     register FILE *rfp;
 912:     char subj[132];
 913:     register char *p;
 914:     char *replyname();
 915:     struct stat statb;
 916:     time_t creatm;
 917: 
 918:     /* Put the user in the editor to create the body of the reply. */
 919:     ed = getenv("EDITOR");
 920:     if (ed == NULL || *ed == '\0')
 921:         ed = DFTEDITOR;
 922:     if (ed == NULL) {
 923:         msg("You don't have an editor");
 924:         return;
 925:     }
 926: 
 927:     arg[0] = "/bin/sh";
 928:     arg[1] = "-c";
 929: 
 930:     (void) strcpy(tf, tft);
 931:     (void) mktemp(tf);
 932:     (void) close(creat(tf,0600));
 933:     if ((rfp = fopen(tf, "w")) == NULL) {
 934:         msg("Can't create %s", tf) ;
 935:         return;
 936:     }
 937:     (void) strcpy(subj, h->title);
 938:     if (!prefix(subj, "Re:")){
 939:         (void) strcpy(bfr, subj);
 940:         (void) sprintf(subj, "Re: %s", bfr);
 941:     }
 942: 
 943:     p = replyname(h);
 944:     fprintf(rfp, "To: %s\n", p);
 945:     fprintf(rfp, "Subject: %s\n", subj);
 946:     fprintf(rfp, "In-reply-to: your article %s\n", h->ident);
 947: #ifdef INTERNET
 948:     fprintf(rfp, "News-Path: %s\n", h->path);
 949: #endif /* INTERNET */
 950:     (void) sprintf(rcbuf, "%s -t < %s; rm -f %s", MAILPARSER, tf, tf);
 951:     putc('\n', rfp);
 952:     if (include) {
 953:         FILE *of;
 954:         char buf[BUFSIZ];
 955: 
 956:         of = xfopen(goodone, "r");
 957:         while (fgets(buf, sizeof buf, of) != NULL)
 958:             if (buf[0] == '\n')
 959:                 break;
 960:         while (fgets(buf, sizeof buf, of) != NULL)
 961:             fprintf(rfp, "> %s", buf);
 962:         fclose(of);
 963:         putc('\n', rfp);
 964:     }
 965:     fflush(rfp);
 966:     (void) fstat(fileno(rfp), &statb);
 967:     creatm = statb.st_mtime;
 968:     (void) fclose(rfp);
 969: 
 970:     (void) sprintf(edcmdbuf, "exec %s %s", ed, tf);
 971:     arg[2] = edcmdbuf;
 972:     arg[3] = NULL;
 973:     if (prun(arg, 0) != 0) {
 974:         msg("Couldn't run editor");
 975:         (void) unlink(tf);
 976:         return;
 977:     }
 978: 
 979:     if (access(tf, 4) || stat(tf, &statb)) {
 980:         msg("No input file - mail not sent");
 981:         (void) unlink(tf);
 982:         return;
 983:     }
 984:     if (statb.st_mtime == creatm || statb.st_size < 5) {
 985:         msg("File unchanged - no message posted");
 986:         (void) unlink(tf);
 987:         return;
 988:     }
 989: 
 990:     arg[2] = rcbuf;
 991:     arg[3] = NULL;
 992:     prun(arg, BKGRND);
 993:     prflags |= NOPRT;
 994: }
 995: 
 996: direct_reply()
 997: {
 998:     register char *p;
 999:     register char *q;
1000:     char *arg[4];
1001:     char address[PATHLEN];
1002:     extern char *replyname();
1003:     extern char *getenv();
1004: 
1005:     arg[0] = "/bin/sh";
1006:     arg[1] = "-c";
1007:     p = replyname(h);
1008:     q = address;
1009:     while (*p != '\0') {
1010:         if (index("\"\\$", *p) != 0)
1011:             *q++ = '\\';
1012:         *q++ = *p++;
1013:     }
1014:     *q++ = '\0';
1015:     if ((MAILER = getenv("MAILER")) == NULL)
1016:         MAILER = "mail";
1017:     sprintf(rcbuf, MAILER, hptr->title);
1018:     sprintf(bfr, "%s %s", rcbuf, address);
1019:     arg[2] = bfr;
1020:     arg[3] = NULL;
1021:     if (prun(arg, 0) != 0) {
1022:         msg("Couldn't run mailer");
1023:         return;
1024:     }
1025:     prflags |= NOPRT;
1026: }
1027: 
1028: next_ng_command()
1029: {
1030:     obit = -1;
1031:     if (prget("group? ", linebuf))
1032:         return FALSE;
1033:     bptr = linebuf;
1034:     if (!*bptr || *bptr == '-') {
1035:         if (*bptr)
1036:             actdirect = BACKWARD;
1037:         saveart;
1038:         if (nextng()) {
1039:             if (actdirect == BACKWARD)
1040:                 msg("Can't back up.");
1041:             else
1042:                 return TRUE;
1043:         }
1044:         return FALSE;
1045:     }
1046:     while (isspace(*bptr))
1047:         bptr++;
1048:     if (!validng(bptr)) {
1049:         msg("No such group.");
1050:         return FALSE;
1051:     }
1052:     saveart;
1053:     back();
1054:     selectng(bptr, TRUE, TRUE);
1055:     return FALSE;
1056: }
1057: 
1058: /*
1059:  * Find the next article we want to consider, if we're done with
1060:  * the last one, and show the header.
1061:  */
1062: getnextart(minus)
1063: int minus;
1064: {
1065:     int noaccess;
1066:     register DIR *dirp;
1067:     register struct direct *dir;
1068:     long nextnum, tnum;
1069:     long atol();
1070: 
1071:     noaccess = 0;
1072:     if (minus)
1073:         goto nextart2;  /* Kludge for "-" command. */
1074: 
1075:     if (bit == obit)    /* Return if still on same article as last time */
1076:         return 0;
1077: 
1078: nextart:
1079:     if (news) {
1080:         curflag = CURHOME;
1081:         _amove(0, 0);
1082:         vflush();
1083:     }
1084:     dgest = 0;
1085: 
1086:     /* If done with this newsgroup, find the next one. */
1087:     while (ngsize <= 0 || (!rflag && ((long) bit > ngsize)) || (rflag && bit < minartno)) {
1088:         if (nextng()) {
1089:             if (actdirect == BACKWARD) {
1090:                 msg("Can't back up.");
1091:                 actdirect = FORWARD;
1092:                 continue;
1093:             }
1094:             else /* if (rfq++ || pflag || cflag) */
1095:                 return 1;
1096:         }
1097:         if (rflag)
1098:             bit = ngsize + 1;
1099:         else
1100:             bit = -1;
1101:         noaccess = 2;
1102:     }
1103: 
1104:     /* speed things up by not searching for article -1 */
1105:     if (bit < 0) {
1106:         bit = minartno - 1;
1107:         nextbit();
1108:         aabs = FALSE;
1109:         goto nextart;
1110:     }
1111: 
1112: nextart2:
1113:     if (rcreadok)
1114:         rcreadok = 2;   /* have seen >= 1 article */
1115:     (void) sprintf(filename, "%s/%ld", dirname(groupdir), bit);
1116:     if (rfq && goodone[0])  /* ??? */
1117:         strcpy(filename, goodone);
1118:     if (SigTrap == SIGHUP)
1119:         return 1;
1120:     /* Decide if we want to show this article. */
1121:     if ((fp = fopen(filename, "r")) == NULL) {
1122:         /* since there can be holes in legal article numbers, */
1123:         /* we wait till we hit 5 consecutive bad articles */
1124:         /* before we haul off and scan the directory */
1125:         if (++noaccess < 5)
1126:             goto badart;
1127:         noaccess = 0;
1128:         dirp = opendir(dirname(groupdir));
1129:         if (dirp == NULL) {
1130:             if (errno != EACCES)
1131:                 msg("Can't open %s", dirname(groupdir));
1132:             goto nextart;
1133:         }
1134:         nextnum = rflag ? minartno - 1 : ngsize + 1;
1135:         while ((dir = readdir(dirp)) != NULL) {
1136:             if (!dir->d_ino)
1137:                 continue;
1138:             tnum = atol(dir->d_name);
1139:             if (tnum <= 0)
1140:                 continue;
1141:             if (rflag ? (tnum > nextnum && tnum < bit)
1142:                   : (tnum < nextnum && tnum > bit))
1143:                 nextnum = tnum;
1144:         }
1145:         closedir(dirp);
1146:         if (rflag ? (nextnum >= bit) : (nextnum <= bit))
1147:             goto badart;
1148:         do {
1149:             clear(bit);
1150:             nextbit();
1151:         } while (rflag ? (nextnum < bit) : (nextnum > bit));
1152:         obit = -1;
1153:         aabs = FALSE;
1154:         goto nextart;
1155:     } else
1156:         noaccess = 0;
1157: 
1158:     if (hread(h, fp, TRUE) == NULL || (!rfq && !aselect(h, aabs))) {
1159: badart:
1160:         FCLOSE(fp);
1161:         clear(bit);
1162:         obit = -1;
1163:         nextbit();
1164:         aabs = FALSE;
1165:         goto nextart;
1166:     }
1167:     aabs = FALSE;
1168:     actdirect = FORWARD;
1169:     news = TRUE;
1170:     artbody = ftell(fp);
1171:     fmthdr();
1172:     artlines = lastlin;
1173:     artread = 0;
1174:     prflags |= NEWART;
1175:     prflags &=~ NOPRT;
1176:     if (! cflag && hdrend < ARTWLEN && !cflag)
1177:         prflags |= HDRONLY;
1178:     dlinno = 0;
1179:     maxlinno = NLINES(h, fp);
1180:     erased = 0;
1181: 
1182:     obit = bit;
1183:     return 0;
1184: }
1185: 
1186: /*
1187:  * Print out whatever the appropriate header is
1188:  */
1189: fmthdr() {
1190:     char *briefdate();
1191:     static FILE *ngfd = NULL;
1192:     static int triedopen = 0;
1193:     char pbuf[BUFLEN], *printbuffer = groupdir;
1194: 
1195:     lastlin = 0;
1196:     if (ngrp) {
1197:         pngsize = ngsize;
1198:         ngrp--;
1199:         if (!hflag) {
1200:             if (!triedopen) {
1201:                 (void) sprintf(pbuf,"%s/newsgroups", LIB);
1202:                 ngfd = fopen(pbuf, "r");
1203:                 triedopen++;
1204:             }
1205:             if (ngfd != NULL) {
1206:                 register char *p;
1207:                 char ibuf[BUFLEN];
1208:                 rewind(ngfd);
1209:                 while (fgets(ibuf, BUFLEN, ngfd) != NULL) {
1210:                     p = index(ibuf, '\t');
1211:                     if (p)
1212:                         *p++ = '\0';
1213:                     if (strcmp(ibuf, groupdir) == 0) {
1214:                         register char *q;
1215:                         q = rindex(p, '\t');
1216:                         if (q) {
1217:                             p = q;
1218:                             *p++ = '\0';
1219:                         }
1220:                         if (p) {
1221:                             q = index(p, '\n');
1222:                             if (q)
1223:                                 *q = '\0';
1224:                             if (*--q == '.')
1225:                                 *q = '\0';
1226:                         (void) sprintf(pbuf,"%s (%s)",
1227:                             groupdir, p);
1228:                             printbuffer = pbuf;
1229:                         }
1230:                         break;
1231:                     }
1232:                 }
1233:             }
1234:             (void) sprintf(linebuf, "Newsgroup %s", printbuffer);
1235:             tfappend(linebuf);
1236:         }
1237:     }
1238:     hdrstart = lastlin;
1239:     if (!hflag) {
1240:         (void) sprintf(linebuf, "Article %s %s",
1241:             h->ident, briefdate(h->subdate));
1242:         tfappend(linebuf);
1243:     }
1244:     xtabs(h);
1245:     vhprint(h, pflag ? 1 : 0);
1246:     (void) sprintf(linebuf, "(%d lines)", NLINES(h, fp)); tfappend(linebuf);
1247:     tfappend("");
1248:     hdrend = lastlin;
1249: }
1250: 
1251: /*
1252:  * Grow tabs into spaces in header fields, 'cause the rest of this
1253:  * lax program drops turds all over tabs (so it does with \b's, but ..)
1254:  */
1255: xtabs(p)
1256: register struct hbuf *p;
1257: {
1258:     xtabf(p->from, sizeof p->from);
1259:     xtabf(p->path, sizeof p->path);
1260:     xtabf(p->nbuf, sizeof p->nbuf);
1261:     xtabf(p->title, sizeof p->title);
1262:     xtabf(p->ident, sizeof p->ident);
1263:     xtabf(p->replyto, sizeof p->replyto);
1264:     xtabf(p->followid, sizeof p->followid);
1265:     xtabf(p->subdate, sizeof p->subdate);
1266:     xtabf(p->expdate, sizeof p->expdate);
1267:     xtabf(p->ctlmsg, sizeof p->ctlmsg);
1268:     xtabf(p->sender, sizeof p->sender);
1269:     xtabf(p->followto, sizeof p->followto);
1270:     xtabf(p->distribution, sizeof p->distribution);
1271:     xtabf(p->organization, sizeof p->organization);
1272:     xtabf(p->numlines, sizeof p->numlines);
1273:     xtabf(p->keywords, sizeof p->keywords);
1274:     xtabf(p->summary, sizeof p->summary);
1275:     xtabf(p->approved, sizeof p->approved);
1276:     xtabf(p->nf_id, sizeof p->nf_id);
1277:     xtabf(p->nf_from, sizeof p->nf_from);
1278: #ifdef DOXREFS
1279:     xtabf(p->xref, sizeof p->xref);
1280: #endif /* DOXREFS */
1281: }
1282: 
1283: xtabf(s, size)
1284: char *s;
1285: int size;
1286: {
1287:     register char *p, *str;
1288:     register c, i;
1289:     char buf[LBUFLEN];
1290: 
1291:     str = s;
1292:     if (index(str, '\t') == NULL)
1293:         return;
1294:     i = 0;
1295:     for (p = buf; c = *str++; i++) {
1296:         if (c == '\t') {
1297:             *p++ = ' ';
1298:             if ((i & 7) != 7)
1299:                 str--;
1300:         } else if (c == '\n') {
1301:             i = -1;
1302:             *p++ = c;
1303:         } else
1304:             *p++ = c;
1305:     }
1306:     *p = '\0';
1307:     strncpy(s, buf, size - 1);
1308: }
1309: 
1310: /*
1311:  * Print the file header to the temp file.
1312:  */
1313: vhprint(hp, verbose)
1314: register struct hbuf *hp;
1315: int verbose;
1316: {
1317:     register char   *p1, *p2;
1318:     char    fname[BUFLEN];
1319:     char *tailpath();
1320: 
1321:     fname[0] = '\0';        /* init name holder */
1322: 
1323:     p1 = index(hp->from, '(');  /* Find the sender's full name. */
1324:     if (p1 == NULL && hp->path[0])
1325:         p1 = index(hp->path, '(');
1326:     if (p1 != NULL) {
1327:         (void) strcpy(fname, p1+1);
1328:         p2 = index(fname, ')');
1329:         if (p2 != NULL)
1330:             *p2 = '\0';
1331:     }
1332: 
1333:     (void) sprintf(linebuf, "Subject: %s", hp->title);
1334:     tfappend(linebuf);
1335:     if (!hflag && hp->summary[0])
1336:         (void) sprintf(linebuf, "Summary: %s", hp->summary), tfappend(linebuf);
1337:     if (!hflag && hp->keywords[0])
1338:         (void) sprintf(linebuf, "Keywords: %s", hp->keywords), tfappend(linebuf);
1339:     if (verbose) {
1340:         (void) sprintf(linebuf, "From: %s", hp->from); tfappend(linebuf);
1341:         (void) sprintf(linebuf, "Path: %s", hp->path); tfappend(linebuf);
1342:         if (hp->organization[0]) {
1343:             (void) sprintf(linebuf, "Organization: %s", hp->organization);
1344:             tfappend(linebuf);
1345:         }
1346:     }
1347:     else {
1348:         if (p1 != NULL)
1349:             *--p1 = '\0';       /* bump over the '(' */
1350: #ifdef INTERNET
1351:         /*
1352: 		 * Prefer Path line if it's in internet format, or if we don't
1353: 		 * understand internet format here, or if there is no reply-to.
1354: 		 */
1355:         (void) sprintf(linebuf, "From: %s", hp->from);
1356: #else
1357:         (void) sprintf(linebuf, "Path: %s", tailpath(hp));
1358: #endif
1359:         if (fname[0] || hp->organization[0]) {
1360:             (void) strcat(linebuf, " (");
1361:             if (fname[0] == '\0') {
1362:                 (void) strcpy(fname,hp->from);
1363:                 p2 = index(fname,'@');
1364:                 if (p2)
1365:                     *p2 = '\0';
1366:             }
1367:             (void) strcat(linebuf, fname);
1368:             if (hp->organization[0] && !hflag) {
1369:                 (void) strcat(linebuf, " @ ");
1370:                 (void) strcat(linebuf, hp->organization);
1371:             }
1372:             (void) strcat(linebuf, ")");
1373:         }
1374:         tfappend(linebuf);
1375:         if (p1 != NULL)
1376:             *p1 = ' ';
1377:         if (hp->ctlmsg[0]) {
1378:             (void) sprintf(linebuf, "Control: %s", hp->ctlmsg);
1379:             tfappend(linebuf);
1380:         }
1381:     }
1382: 
1383:     if (verbose) {
1384:         (void) sprintf(linebuf, "Newsgroups: %s", hp->nbuf); tfappend(linebuf);
1385:         (void) sprintf(linebuf, "Date: %s", hp->subdate); tfappend(linebuf);
1386:         if (hp->sender[0]) {
1387:             (void) sprintf(linebuf, "Sender: %s", hp->sender);
1388:             tfappend(linebuf);
1389:         }
1390:         if (hp->replyto[0]) {
1391:             (void) sprintf(linebuf, "Reply-To: %s", hp->replyto);
1392:             tfappend(linebuf);
1393:         }
1394:         if (hp->followto[0]) {
1395:             (void) sprintf(linebuf, "Followup-To: %s", hp->followto);
1396:             tfappend(linebuf);
1397:         }
1398:     }
1399:     else if (strcmp(hp->nbuf, groupdir) != 0) {
1400:         (void) sprintf(linebuf, "Newsgroups: %s", hp->nbuf);
1401:         tfappend(linebuf);
1402:         timer();
1403:     }
1404: }
1405: 
1406: #ifdef MYDB
1407: 
1408: char *
1409: findparent(id, num)
1410: char *id;
1411: long *num;
1412: {
1413:     struct artrec a;
1414:     char idbuf[BUFSIZE];
1415:     char *ngname();
1416: 
1417:     strcpy(idbuf, id);
1418:     lcase(idbuf);
1419: 
1420:     if (lookart(id, &a) == DNULL)
1421:         return NULL;
1422:     if (a.parent == DNULL)
1423:         return NULL;
1424:     readrec(a.parent, &a);
1425:     *num = a.groups[0].artno;
1426:     return ngname(a.groups[0].newsgroup);
1427: }
1428: 
1429: #endif
1430: 
1431: 
1432: /*
1433:  * Append file to temp file, handling control characters, folding lines, etc.
1434:  * We don't grow the temp file to more than nlines so that a user won't have
1435:  * to wait for 20 seconds to read in a monster file from net.sources.
1436:  * What we really want is coroutines--any year now.
1437:  */
1438: 
1439: #define ULINE 0200
1440: static char *maxcol;
1441: 
1442: appfile(iop, nlines)
1443: register FILE *iop;
1444: {
1445:     register int c;
1446:     register char *icol;    /* &linebuf[0] <= icol <= maxcol */
1447: 
1448:     if (artread || artlines >= nlines || iop == NULL)
1449:         return;
1450:     maxcol = linebuf;
1451:     icol = linebuf;
1452:     while ((c = getc(iop)) != EOF) {
1453:         switch (c) {
1454:         case ' ':
1455:             if (icol == maxcol && icol < linebuf + LBUFLEN - 1) {
1456:                 *icol++ = ' ';
1457:                 maxcol = icol;
1458:             } else {
1459:                 if (*icol == '_')
1460:                     *icol++ = ULINE | ' ';
1461:                 else
1462:                     icol++;
1463:             }
1464:             break;
1465:         case '\t':
1466:             icol = (icol - linebuf &~ 07) + 8 + linebuf;
1467:             growline(icol);
1468:             break;
1469:         case '\b':
1470:             if (icol > linebuf) --icol;
1471:             break;
1472:         case '\n':
1473:             outline();
1474:             if (artlines >= nlines)
1475:                 return;
1476:             icol = linebuf;
1477:             break;
1478:         case '\r':
1479:             icol = linebuf;
1480:             break;
1481:         case '\f':
1482:             outline(); outline(); outline();
1483:             if (artlines >= nlines)
1484:                 return;
1485:             icol = linebuf;
1486:             break;
1487:         default:
1488:             if (c < ' ' || c > '~')
1489:                 break;
1490:             else if (icol >= linebuf + LBUFLEN - 1)
1491:                 icol++;
1492:             else if (icol == maxcol) {
1493:                 *icol++ = c;
1494:                 maxcol = icol; }
1495:             else if (c == '_')
1496:                 *icol++ |= ULINE;
1497:             else if (*icol == '_')
1498:                 *icol++ = (c | ULINE);
1499:             else    *icol++ = c;
1500:             break;
1501:         }
1502:     }
1503:     if (maxcol != linebuf)      /* file not terminated with newline */
1504:         outline();
1505:     artread++;
1506: }
1507: 
1508: growline(col)
1509: char *col;
1510: {
1511:     while (maxcol < col && maxcol < linebuf + LBUFLEN - 1)
1512:         *maxcol++ = ' ';
1513: }
1514: 
1515: outline()
1516: {
1517:     *maxcol = '\0';
1518:     if (strncmp(linebuf, ">From ", 6) == 0) {
1519:         register char *p;
1520:         for (p = linebuf ; (*p = p[1]) != '\0' ; p++);
1521:     }
1522:     tfappend(linebuf);
1523:     if (maxcol > linebuf)
1524:         artlines = lastlin;
1525:     maxcol = linebuf;
1526: }
1527: 
1528: prget(prompter, buf)
1529: char *prompter, *buf;
1530: {
1531:     char *p, *q, *r;
1532:     int c, lastc;
1533: 
1534:     curflag = CURP2;
1535:     r = buf;
1536:     lastc = '\0';
1537:     for (;;) {
1538:         *r = '\0';
1539:         p = secpr;
1540:         for (q = prompter ; *q ; q++)
1541:             *p++ = *q;
1542:         for (q = buf ; *q ; q++) {
1543:             if (p < &secpr[SECPRLEN-1] && *q >= ' ' && *p <= '~')
1544:                 *p++ = *q;
1545:         }
1546:         *p = '\0';
1547:         c = vgetc();
1548:         if (c == '\n' || c == cintr) {
1549:             break;
1550:         }
1551:         if (c == cerase || c == '\b' || c == '\177') {
1552:             if (lastc == '\\')
1553:                 r[-1] = c;
1554:             else if (r > buf)
1555:                 r--;
1556:         } else if (c == ckill) {
1557:             if (lastc == '\\')
1558:                 r[-1] = c;
1559:             else
1560:                 r = buf;
1561: #ifdef TIOCGLTC
1562:         } else if (c == cwerase) {
1563:             if (lastc == '\\')
1564:                 r[-1] = c;
1565:             else {
1566:                 while (r > buf && (r[-1] == ' ' || r[-1] == '\t'))
1567:                     r--;
1568:                 while (r > buf && r[-1] != ' ' && r[-1] != '\t')
1569:                     r--;
1570:             }
1571: #endif
1572:         } else {
1573:             *r++ = c;
1574:         }
1575:         lastc = c;
1576:     }
1577:     curflag = CURHOME;
1578:     secpr[0] = '\0';
1579:     return (c == cintr);
1580: }
1581: 
1582: 
1583: 
1584: /*
1585:  * Execute a shell command.
1586:  */
1587: 
1588: shcmd(cmd, flags)
1589: char *cmd;
1590: {
1591:     char *arg[4];
1592: 
1593:     arg[0] = SHELL, arg[1] = "-c", arg[2] = cmd, arg[3] = NULL;
1594:     return prun(arg, flags);
1595: }
1596: 
1597: 
1598: prun(args, flags)
1599: char **args;
1600: {
1601:     int pid;
1602:     int i;
1603:     int (*savequit)();
1604:     char *env[100], **envp;
1605:     char a[BUFLEN + 2];
1606:     extern char **environ;
1607:     int pstatus, retval;
1608: 
1609:     if (!(flags & BKGRND)) {
1610:         botscreen();
1611:         ttycooked();
1612: #ifdef SIGTSTP
1613:         (void) signal(SIGTSTP, SIG_DFL);
1614:         (void) signal(SIGTTIN, SIG_DFL);
1615:         (void) signal(SIGTTOU, SIG_DFL);
1616: #endif
1617:     }
1618:     while ((pid = fork()) == -1)
1619:         sleep(1);       /* must not clear alarm */
1620:     if (pid == 0) {
1621:         for (i = 3 ; i < 20 ; i++)
1622:             close(i);
1623:         if (flags & BKGRND) {
1624:             (void) signal(SIGINT, SIG_IGN);
1625:             (void) signal(SIGQUIT, SIG_IGN);
1626: #ifdef SIGTSTP
1627:             (void) signal(SIGTSTP, SIG_IGN);
1628:             (void) signal(SIGTTIN, SIG_IGN);
1629:             (void) signal(SIGTTOU, SIG_IGN);
1630: #endif
1631:             (void) close(0);
1632:             (void) close(1);
1633:             (void) open("/dev/null", 2);
1634:             (void) dup(0);
1635:         }
1636:         /* set $A */
1637:         (void) sprintf(a, "A=%s", filename);
1638:         env[0] = a;
1639:         for (envp = env + 1 ; *environ != NULL && envp < env + 98 ; environ++)
1640:             if ((*environ)[0] != 'A' || (*environ)[1] != '=')
1641:                 *envp++ = *environ;
1642:         *envp = NULL;
1643: 
1644:         (void) umask(savmask);
1645:         execve(args[0], args, env);
1646:         fprintf(stderr, "%s: not found\n", args[0]);
1647:         exit(20);
1648:     }
1649:     if (!(flags & BKGRND)) {
1650:         savequit = signal(SIGQUIT, SIG_IGN);
1651:         while ((i = wait(&pstatus)) != pid && (i != -1 || errno == EINTR))
1652:             ;
1653:         if (i == -1)
1654:             retval = 1;
1655:         else
1656:             retval = pstatus;
1657:         if (flags & CWAIT) {
1658:             fprintf(stderr, "[Hit return to continue]");
1659:             while ((errno = 0, i = getchar()) != '\n'
1660:                 && (i != EOF || errno == EINTR));
1661:         }
1662:         (void) signal(SIGQUIT, savequit);
1663:         ttyraw();
1664:         clearok(curscr, 1);
1665: #ifdef SIGTSTP
1666:         (void) signal(SIGTSTP, onstop);
1667:         (void) signal(SIGTTIN, onstop);
1668:         (void) signal(SIGTTOU, onstop);
1669: #endif
1670:         return retval;
1671:     } else
1672:         return 0;
1673: }
1674: 
1675: #ifdef DIGPAGE
1676: 
1677: 
1678: /*
1679:  * Find end of current subarticle in digest.
1680:  */
1681: 
1682: findend(l)
1683: {
1684:     register int i, n;
1685:     register char *p;
1686: 
1687:     for (i = l ; i < l + ARTWLEN && i < lastlin ; i++) {
1688:         tfget(linebuf, i);
1689:         for (p = linebuf ; *p == '-' ; p++)
1690:             ;
1691:         n = (int)p - (int)linebuf;
1692:         if ( (n > 23 && n < 33) || (n > 65 && n < 79)) {
1693:             tfget(linebuf, ++i);
1694:             if (linebuf[0] == '\0')
1695:                 return i + 1;
1696:         }
1697:     }
1698:     return 0;
1699: }
1700: 
1701: #endif
1702: 
1703: 
1704: /*** Routines for handling temporary file ***/
1705: 
1706: /*
1707:  * Append to temp file.
1708:  * Long lines are folded.
1709:  */
1710: 
1711: tfappend(tline)
1712: register char *tline;
1713: {
1714:     register char *nxtlin;
1715: 
1716:     do {
1717:         nxtlin = index(tline, '\n');
1718:         if (nxtlin)
1719:             *nxtlin++ = '\0';
1720: 
1721:         while (strlen(tline) > COLS) {
1722:             tfput(tline, lastlin++);
1723:             tline += COLS;
1724:             maxlinno++;
1725:         }
1726:         tfput(tline, lastlin++);
1727:     } while ((tline = nxtlin) != NULL);
1728: }
1729: 
1730: 
1731: tfput(tline, linno)
1732: char *tline;
1733: {
1734:     register char *p;
1735:     register FILE *rtfp;        /* try to make it a little faster */
1736:     register int i;
1737: 
1738:     p = tline, i = even(COLS);
1739:     tfseek(linno, 1);
1740:     rtfp = tfp;
1741:     while (--i >= 0) {
1742:         if (*p)
1743:             putc(*p++, rtfp);
1744:         else
1745:             putc('\0', rtfp);
1746:     }
1747:     tflinno++;
1748: }
1749: 
1750: 
1751: tfget(tline, linno)
1752: char *tline;
1753: {
1754:     tfseek(linno, 0);
1755:     fread(tline, even(COLS), 1, tfp);
1756:     tline[COLS] = '\0';
1757:     tflinno++;
1758: }
1759: 
1760: 
1761: tfseek(linno, wrflag)
1762: {
1763:     static int lastwrflag = 1;
1764: 
1765:     if (linno != tflinno || wrflag != lastwrflag) {
1766:         (void) fseek(tfp, (long)linno * even(COLS), 0);
1767:         tflinno = linno;
1768:         lastwrflag = wrflag;
1769:     }
1770: }
1771: 
1772: /* VARARGS1 */
1773: msg(s, a1, a2, a3, a4)
1774: char *s;
1775: {
1776:     (void) sprintf(secpr, s, a1, a2, a3, a4);
1777: }
1778: 
1779: 
1780: /*
1781:  * Update the display.
1782:  * The display is entirely controlled by this routine,
1783:  * which means that this routine may get pretty snarled.
1784:  */
1785: 
1786: static int savelinno = -1;      /* dlinno on last call to updscr */
1787: static int savepr;          /* prflags on last call */
1788: #ifdef TIOCGWINSZ
1789: static int UPDATING = 0, WINCH = 0;
1790: 
1791: /*
1792:  * called by winch() from virtterm.c -- resets state information back
1793:  * to start-up state and forces a full redraw of the screen.  The
1794:  * current article is rewound to the beginning because it's would
1795:  * be very difficult to get the screen to return to the exact point
1796:  * in the file that the user left off (I know, I tried).
1797:  */
1798: winch_upd()
1799: {
1800:     if(UPDATING)    /* concurrency.  wow! */
1801:         WINCH++;
1802:     else if((WINCH == 0) && (savelinno >= 0)) {
1803:         int saveline = dlinno, saveflag = curflag;
1804: 
1805:         /* reread the article */
1806:         FCLOSE(fp);
1807:         obit = -1;
1808:         getnextart(FALSE);
1809:         appfile(fp, dlinno + ARTWLEN + 1);
1810: 
1811:         /* fix up the screen */
1812:         curflag = saveflag;
1813:         strcpy(prompt,"more? ");
1814:         clearok(curscr, 1);
1815:         updscr();
1816:     }
1817: }
1818: #endif /* TIOCGWINSZ */
1819: 
1820: 
1821: updscr()
1822: {
1823:     int count;
1824:     int i;
1825: 
1826: #ifdef TIOCGWINSZ
1827:     UPDATING++;
1828: #endif /* TIOCGWINSZ */
1829:     if (checkin())
1830:         return;
1831:     if ((prflags & HELPMSG) == 0
1832:      && (dlinno != savelinno || savepr != prflags)
1833:      && quitflg == 0) {
1834:         if (dlinno != savelinno)
1835:             prflags &=~ NOPRT;
1836:         count = ARTWLEN;
1837:         if (prflags & NOPRT)
1838:             count = 0;
1839:         if ((prflags & HDRONLY) && count > hdrend)
1840:             count = hdrend - dlinno;
1841: #ifdef DIGPAGE
1842:         if (endsuba > 0 && count > endsuba - dlinno)
1843:             count = endsuba - dlinno;
1844: #endif
1845:         if ((prflags & NEWART) == 0)
1846:             ushift(ARTWIN, ARTWIN+ARTWLEN-1, dlinno - savelinno);
1847:         if (count > lastlin - dlinno)
1848:             count = lastlin - dlinno;
1849:         for (i = ARTWIN ; i < ARTWIN + ARTWLEN ; i++)
1850:             clrline(i);
1851:         for (i = 0 ; i < count ; i++) {
1852:             tfget(linebuf, dlinno + i);
1853:             mvaddstr(ARTWIN + i, 0, linebuf);
1854:         }
1855:         prflags &=~ NEWART;
1856:         savepr = prflags;
1857:         savelinno = dlinno;
1858:     }
1859:     clrline(SPLINE), clrline(PRLINE);
1860: #ifdef STATTOP
1861:     mvaddstr(PRLINE, 0, prompt);
1862: #else
1863:     if (strlen(secpr) <= COLS)
1864:         mvaddstr(PRLINE, 0, prompt);
1865: #endif
1866:     mvaddstr(PRLINE, 59, timestr);
1867:     mvaddstr(PRLINE, 15, groupdir);
1868:     addch(' '); addnum(bit); addch('/'); addnum(pngsize); addch(' ');
1869:     if (ismail)
1870:         mvaddstr(PRLINE, 75, ismail > 1? "MAIL" : "mail");
1871:     mvaddstr(SPLINE, 0, secpr);
1872:     if (curflag == CURP1)
1873:         move(PRLINE, strlen(prompt));
1874:     else if (curflag == CURHOME)
1875:         move(0, 0);
1876:     refresh();
1877: #ifdef TIOCGWINSZ
1878:     UPDATING=0;
1879:     if (WINCH) { /* window changed while updating screen */
1880:         WINCH = 0;
1881:         winch_upd();
1882:     }
1883: #endif /* TIOCGWINSZ */
1884: }
1885: 
1886: addnum(n)
1887: register long n;
1888: {
1889:     if (n >= 10)
1890:         addnum(n / 10);
1891:     addch((char)(n % 10 + '0'));
1892: }
1893: 
1894: /*
1895:  * Called on alarm signal.
1896:  * Simply sets flag, signal processed later.
1897:  */
1898: 
1899: onalarm()
1900: {
1901: #ifdef SIGTSTP
1902:     int dojump = reading;
1903: 
1904:     reading = FALSE;
1905:     alflag++;
1906:     if (dojump)
1907:         longjmp(alrmjmp, 1);
1908: #else /* !SIGTSTP */
1909:     alflag++;
1910: #endif
1911: }
1912: 
1913: /*
1914:  * Process alarm signal (or start clock)
1915:  */
1916: timer()
1917: {
1918:     time_t tod;
1919:     int hour;
1920:     int i;
1921:     struct tm *t;
1922:     struct stat statb;
1923:     struct tm *localtime();
1924:     static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
1925:     static long oldmsize = 1000000L;
1926:     static int rccount = 10;
1927:     static time_t lastismail = 0;
1928: 
1929:     alflag = 0;
1930:     (void) signal(SIGALRM, onalarm);
1931:     (void) time(&tod);
1932:     t = localtime(&tod);
1933:     i = 60 - t->tm_sec;
1934:     (void) alarm(i > 30? 30 : i);           /* reset alarm */
1935:     hour = t->tm_hour % 12;
1936:     if (hour == 0)  hour = 12;
1937:     (void) sprintf(timestr, "%.3s %d %d:%02d",
1938:         months + 3 * t->tm_mon, t->tm_mday, hour, t->tm_min);
1939:     if (mailf == NULL || stat(mailf, &statb) < 0) {
1940:         statb.st_size = 0;
1941:     }
1942:     if (statb.st_size > oldmsize) {
1943:         ismail = 2;
1944:         beep();
1945:     } else {
1946:         if (statb.st_size == 0)
1947:             ismail = 0;
1948:                     /* force MAIL for at least 30 seconds */
1949:         else if (ismail > 1 && (lastismail+30) < tod)
1950:             ismail = 1;
1951:     }
1952:     oldmsize = statb.st_size;
1953:     lastismail = tod;
1954:     if (uflag && !xflag && --rccount < 0) {
1955:         writeoutrc();
1956:         if (secpr[0] == '\0')
1957:             (void) strcpy(secpr, ".newsrc updated");
1958:         rccount = 10;
1959:     }
1960: }
1961: 
1962: char *
1963: getmailname()
1964: {
1965:     static char mailname[32];
1966:     register char *p;
1967: 
1968:     if( (p = getenv("MAIL")) != NULL)
1969:         return p;
1970: #ifndef MMDF
1971:     if (username[0] == '\0' || strlen(username) > 15)
1972:         return NULL;
1973: #ifdef USG
1974:     (void) sprintf(mailname, "/usr/mail/%s", username);
1975: #else /* !USG */
1976:     (void) sprintf(mailname, "/usr/spool/mail/%s", username);
1977: #endif /* !USG */
1978: #else /* MMDF */
1979:     (void) sprintf(mailname, "%s/mailbox", userhome);
1980: #endif /* MMDF */
1981:     return mailname;
1982: }
1983: 
1984: 
1985: 
1986: /*** Terminal I/O ***/
1987: 
1988: #define INBUFSIZ 8
1989: 
1990: char inbuf[INBUFSIZ];           /* input buffer */
1991: char outbuf[BUFSIZ];            /* output buffer */
1992: int innleft = 0;            /* # of chars in input buffer */
1993: int outnleft = BUFSIZ;          /* room left in output buffer */
1994: char *innext;               /* next input character */
1995: char *outnext = outbuf;         /* next space in output buffer */
1996: #ifdef USG
1997: int oflags;             /* fcntl flags (for nodelay read) */
1998: #endif
1999: 
2000: /*
2001:  * Input a character
2002:  */
2003: 
2004: vgetc()
2005: {
2006:     register c;
2007: #if defined(BSD4_2) || defined(BSD4_1C)
2008:     int readfds, exceptfds;
2009: #endif
2010: 
2011: recurse:
2012:     if (--innleft >= 0) {
2013:         c = *innext++;
2014:     } else {
2015:         if (alflag)
2016:             timer();
2017:         updscr();   /* update the display */
2018:         for (;;) {
2019:             if (innleft > 0 || alflag)
2020:                 goto recurse;
2021:             intflag = 0;
2022: #ifdef USG
2023:             if (oflags & O_NDELAY) {
2024:                 oflags &=~ O_NDELAY;
2025:                 fcntl(0, F_SETFL, oflags);
2026:             }
2027: #endif
2028: #ifdef SIGTSTP
2029:             if (setjmp(alrmjmp))
2030:                 continue;
2031:             if (setjmp(intjmp))
2032:                 return cintr;
2033:             reading = TRUE;
2034: #endif /* SIGTSTP */
2035: #if defined(BSD4_2) || defined(BSD4_1C)
2036:             /* Use a select because it can be interrupted. */
2037:             readfds = 1; exceptfds = 1;
2038:             select(1, &readfds, (int *)0, &exceptfds, (int *)0);
2039:             if (!(readfds & 1))
2040:                 break;
2041: #endif
2042:             innleft = read(0, inbuf, INBUFSIZ);
2043: #ifdef SIGTSTP
2044:             reading = FALSE;
2045: #endif /* SIGTSTP */
2046:             if (innleft > 0)
2047:                 break;
2048:             if (innleft == 0) {
2049:                 quitflg++;
2050:                 return cintr;
2051:             }
2052:             if (errno != EINTR)
2053:                 abort();    /* "Can't happen" */
2054:             if (intflag) {
2055:                 intflag--;
2056:                 return cintr;
2057:             }
2058:         }
2059:         innext = inbuf + 1;
2060:         innleft--;
2061:         c = inbuf[0];
2062:     }
2063: #ifndef USG
2064: #ifndef CBREAK
2065:     c &= 0177;
2066:     if (c == '\034')    /* FS character */
2067:         xxit(0);
2068: #endif
2069: #endif
2070:     if (c == '\f') {
2071:         clearok(curscr, 1);
2072:         goto recurse;
2073:     }
2074:     if (c == '\r')
2075:         c = '\n';
2076:     return c;
2077: }
2078: 
2079: 
2080: /*
2081:  * Push a character back onto the input stream.
2082:  */
2083: 
2084: pushback(c)
2085: {
2086:     if (innext <= inbuf)
2087:         abort();
2088:     *--innext = c;
2089:     innleft++;
2090: }
2091: 
2092: /*
2093:  * Check for terminal input
2094:  */
2095: 
2096: checkin()
2097: {
2098: #ifdef FIONREAD
2099:     int count;
2100: #endif
2101: #ifdef STATTOP
2102:     if (innleft > 0)
2103: #else
2104:     if (innleft > 0 || alflag)
2105: #endif
2106:         return 1;
2107: #if defined(USG) || defined(FIONREAD)
2108:     if (ospeed >= B9600)
2109:         return 0;
2110:     vflush();
2111:     if (ospeed <= B300)
2112:         ttyowait();
2113: #ifdef USG
2114:     if ((oflags & O_NDELAY) == 0) {
2115:         oflags |= O_NDELAY;
2116:         (void) fcntl(0, F_SETFL, oflags);
2117:     }
2118:     if ((innleft = read(0, inbuf, INBUFSIZ)) > 0) {
2119:         innext = inbuf;
2120:         return 1;
2121:     }
2122: #endif
2123: #ifdef FIONREAD
2124:     count = 0;          /* in case FIONREAD fails */
2125:     (void) ioctl(0, FIONREAD, (char *)&count);
2126:     if (count)
2127:         return 1;
2128: #endif
2129: #endif
2130:     return 0;
2131: }
2132: 
2133: 
2134: 
2135: /*
2136:  * flush terminal input queue.
2137:  */
2138: 
2139: clearin()
2140: {
2141: #ifdef USG
2142:     (void) ioctl(0, TCFLSH, (char *)0);
2143: #else
2144: #ifdef TIOCFLUSH
2145:     (void) ioctl(0, TIOCFLUSH, (char *)0);
2146: #else
2147:     struct sgttyb tty;
2148:     (void) ioctl(0, TIOCGETP, &tty);
2149:     (void) ioctl(0, TIOCSETP, &tty);
2150: #endif
2151: #endif
2152:     innleft = 0;
2153: }
2154: 
2155: vputc(c)
2156: {
2157:     if (--outnleft < 0) {
2158:         vflush();
2159:         outnleft--;
2160:     }
2161:     *outnext++ = c;
2162: }
2163: 
2164: /*
2165:  * Flush the output buffer
2166:  */
2167: 
2168: vflush()
2169: {
2170:     register char *p;
2171:     register int i;
2172: #ifdef BSD4_2
2173:     int mask;
2174: #else
2175:     unsigned oalarm;
2176: #endif
2177: 
2178: #ifdef BSD4_2
2179:     mask = sigblock(1 << (SIGALRM-1));
2180: #else
2181:     oalarm = alarm(0);
2182: #endif
2183:     for (p = outbuf ; p < outnext ; p += i) {
2184:         if ((i = write(1, p, outnext - p)) < 0) {
2185:             if (errno != EINTR)
2186:                 abort();    /* "Can't happen" */
2187:             i = 0;
2188:         }
2189:     }
2190:     outnleft = BUFSIZ;
2191:     outnext = outbuf;
2192: #ifdef BSD4_2
2193:     sigsetmask(mask);
2194: #else
2195:     (void) alarm(oalarm);
2196: #endif
2197: }
2198: 
2199: /*** terminal modes ***/
2200: 
2201: #ifdef USG
2202: static struct termio oldtty, newtty;
2203: 
2204: /*
2205:  * Save tty modes
2206:  */
2207: 
2208: ttysave()
2209: {
2210:     if (ioctl(1, TCGETA, &oldtty) < 0)
2211:         xerror("Can't get tty modes");
2212:     newtty = oldtty;
2213:     newtty.c_iflag &=~ (INLCR|IGNCR|ICRNL);
2214:     newtty.c_oflag &=~ (OPOST);
2215:     newtty.c_lflag &=~ (ICANON|ECHO|ECHOE|ECHOK|ECHONL);
2216:     newtty.c_lflag |=  (NOFLSH);
2217:     newtty.c_cc[VMIN] = 1;
2218:     newtty.c_cc[VTIME] = 0;
2219:     cerase = oldtty.c_cc[VERASE];
2220:     ckill = oldtty.c_cc[VKILL];
2221:     cintr = oldtty.c_cc[VINTR];
2222:     ospeed = oldtty.c_cflag & CBAUD;
2223:     initterm();
2224: }
2225: 
2226: 
2227: /*
2228:  * Set tty modes for visual processing
2229:  */
2230: 
2231: ttyraw()
2232: {
2233:     while (ioctl(1, TCSETAF, &newtty) < 0 && errno == EINTR)
2234:         ;
2235:     rawterm();
2236: }
2237: 
2238: ttyowait()
2239: {   /* wait for output queue to drain */
2240:     while (ioctl(1, TCSETAW, &newtty) < 0 && errno == EINTR)
2241:         ;
2242: }
2243: 
2244: /*
2245:  * Restore tty modes
2246:  */
2247: 
2248: ttycooked()
2249: {
2250:     cookedterm();
2251:     vflush();
2252:     while (ioctl(1, TCSETAF, &oldtty) < 0 && errno == EINTR)
2253:         ;
2254:     oflags &=~ O_NDELAY;
2255:     (void) fcntl(0, F_SETFL, oflags) ;
2256: }
2257: 
2258: #else
2259: 
2260: static struct sgttyb oldtty, newtty;
2261: #ifdef TIOCGLTC
2262: static struct ltchars oldltchars, newltchars;
2263: #endif
2264: 
2265: /*
2266:  * Save tty modes
2267:  */
2268: 
2269: ttysave()
2270: {
2271: #ifdef CBREAK
2272:     struct tchars tchars;   /* special characters, including interrupt */
2273: #endif
2274: #ifdef SIGTSTP
2275:     int getpgrp();
2276: #if defined(BSD4_2) || defined(BSD4_1C)
2277:     int tpgrp;
2278: #else /* BSD4_1 */
2279:     short tpgrp;
2280: #endif /* BSD4_1 */
2281: 
2282: retry:
2283: #ifdef BSD4_2
2284:     (void) sigblock(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU));
2285: #else /* !BSD4_2 */
2286:     (void) signal(SIGTSTP, SIG_HOLD);
2287:     (void) signal(SIGTTIN, SIG_HOLD);
2288:     (void) signal(SIGTTOU, SIG_HOLD);
2289: #endif /* !BSD4_2 */
2290:     if (ioctl(2, TIOCGPGRP, (char *)&tpgrp) < 0)
2291:         goto nottty;
2292:     if (tpgrp != getpgrp(0)) { /* not in foreground */
2293:         (void) signal(SIGTTOU, SIG_DFL);
2294: #ifdef BSD4_2
2295:         (void) sigsetmask(sigblock(0) & ~sigmask(SIGTTOU));
2296: #endif /* BSD4_2 */
2297:         (void) kill(0, SIGTTOU);
2298:         /* job stops here waiting for SIGCONT */
2299:         goto retry;
2300:     }
2301:     (void) signal(SIGTTIN, onstop);
2302:     (void) signal(SIGTTOU, onstop);
2303:     (void) signal(SIGTSTP, onstop);
2304: #ifdef BSD4_2
2305:     (void) sigsetmask(sigblock(0) & ~(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU)));
2306: #endif /* BSD4_2 */
2307: #endif /* SIGTSTP */
2308:     if (ioctl(1, TIOCGETP, (char *)&oldtty) < 0)
2309: nottty:     xerror("Can't get tty modes");
2310:     newtty = oldtty;
2311:     newtty.sg_flags &=~ (CRMOD|ECHO|XTABS);
2312: #ifdef CBREAK
2313:     newtty.sg_flags |= CBREAK;
2314:     ioctl(1, TIOCGETC, (char *)&tchars);
2315:     cintr = tchars.t_intrc;
2316: #else /* !CBREAK */
2317:     newtty.sg_flags |= RAW;
2318:     cintr = '\0177';    /* forcibly this on V6 systems */
2319: #endif /* !CBREAK */
2320:     cerase = oldtty.sg_erase;
2321:     ckill = oldtty.sg_kill;
2322:     ospeed = oldtty.sg_ospeed;
2323: #ifdef  TIOCGLTC
2324:     if (ioctl(1, TIOCGLTC, (char *)&oldltchars) >= 0) {
2325:         newltchars = oldltchars;
2326:         newltchars.t_dsuspc = -1;
2327:         cwerase = oldltchars.t_werasc;
2328:     }
2329: #endif
2330:     initterm();
2331: }
2332: 
2333: 
2334: /*
2335:  * Set tty modes for visual processing
2336:  */
2337: 
2338: ttyraw()
2339: {
2340:     while (ioctl(1, TIOCSETN, (char *)&newtty) < 0 && errno == EINTR)
2341:         ;
2342: #ifdef TIOCGLTC
2343:     if (newltchars.t_dsuspc == '\377')
2344:       while (ioctl(1, TIOCSLTC, (char *)&newltchars) < 0 && errno == EINTR)
2345:         ;
2346: #endif
2347:     rawterm();
2348: }
2349: 
2350: ttyowait()
2351: {   /* wait for output queue to drain */
2352: #ifdef TIOCDRAIN    /* This ioctl is a local mod on linus */
2353:     (void) ioctl(1, TIOCDRAIN, (char *)0);
2354: #endif
2355: }
2356: 
2357: 
2358: /*
2359:  * Restore tty modes
2360:  */
2361: 
2362: ttycooked()
2363: {
2364:     cookedterm();
2365:     vflush();
2366:     while (ioctl(1, TIOCSETN, (char *)&oldtty) < 0 && errno == EINTR)
2367:         ;
2368: #ifdef TIOCGLTC
2369:     if (newltchars.t_dsuspc == '\377')
2370:       while (ioctl(1, TIOCSLTC, (char *)&oldltchars) < 0 && errno == EINTR)
2371:         ;
2372: #endif
2373: }
2374: 
2375: #endif
2376: 
2377: 
2378: 
2379: /*** signal handlers ***/
2380: 
2381: onint() {
2382: #ifdef SIGTSTP
2383:     int dojump = reading;
2384: 
2385:     reading = FALSE;
2386: #endif /* SIGTSTP */
2387:     if (!news) {
2388:         ttycooked();
2389:         xxit(1);
2390:     }
2391:     (void) signal(SIGINT, onint);
2392:     clearin();          /* flush input queue */
2393: #ifdef SIGTSTP
2394:     if (dojump)
2395:         longjmp(intjmp, 1);
2396: #endif /* SIGTSTP */
2397:     intflag++;
2398: }
2399: 
2400: #ifdef SIGTSTP
2401: onstop(signo)
2402: int signo;
2403: {
2404:     /* restore old terminal state */
2405:     botscreen();
2406:     vflush();
2407:     ttycooked();
2408:     (void) signal(signo, SIG_DFL);
2409: #ifdef BSD4_2
2410:     (void) sigblock(sigmask(SIGALRM)|sigmask(SIGINT));
2411:     (void) sigsetmask(sigblock(0) & ~sigmask(signo));
2412: #else /* BSD4_1 */
2413:     (void) alarm(0);
2414: #endif /* BSD4_1 */
2415:     (void) kill(0, signo);  /* stop here until continued */
2416: 
2417:     (void) signal(signo, onstop);
2418:     /* restore our special terminal state */
2419:     ttyraw();
2420: #ifdef TIOCGWINSZ
2421:     winch();    /* get current window size and redraw screen */
2422: #endif 	/* TIOCGWINSZ */
2423:     clearok(curscr, 1);
2424:     updscr();
2425: #ifdef BSD4_2
2426:     (void) sigsetmask(sigblock(0) & ~(sigmask(SIGALRM)|sigmask(SIGINT)));
2427: #else /* BSD4_1 */
2428:     timer();
2429: #endif /* BSD4_1 */
2430: }
2431: #endif
2432: 
2433: /*** stolen from rfuncs2.c and modified ***/
2434: 
2435: vsave(to, flags)
2436: register char *to;
2437: {
2438:     register FILE *ufp;
2439:     int isprogram = 0;
2440:     int isnew = 1;
2441:     long    saveoff;
2442:     char    temp[20];
2443:     char    *fname;
2444:     char    prog[BUFLEN + 24];
2445: 
2446:     saveoff = ftell(fp);
2447:     (void) fseek(fp, artbody, 0);
2448:     fname = to;
2449:     if (*to == PIPECHAR) {
2450:         if (strlen(to) > BUFLEN) {
2451:             msg("Command name too long");
2452:             goto out;
2453:         }
2454:         flags |= OVWRITE;
2455:         (void) strcpy(temp, "/tmp/vnXXXXXX");
2456:         (void) mktemp(temp);
2457:         fname = temp;
2458:         _amove(ROWS - 1, 0);
2459:         vflush();
2460:     }
2461:     if ((flags & OVWRITE) == 0) {
2462:         ufp = fopen(fname, "r");
2463:         if (ufp != NULL) {
2464:             (void) fclose(ufp);
2465:             isnew = 0;
2466:         }
2467:     }
2468:     (void) umask(savmask);
2469: 
2470:     if (*to == PIPECHAR)
2471:         isprogram++;
2472:     if ((ufp = fopen(fname, (flags & OVWRITE) == 0? "a" : "w")) == NULL) {
2473:         msg("Cannot open %s", fname);
2474:         goto out;
2475:     }
2476:     /*
2477: 	 * V7MAIL code is here to conform to V7 mail format.
2478: 	 * If you need a different format to be able to
2479: 	 * use your local mail command (such as four ^A's
2480: 	 * on the end of articles) substitute it here.
2481: 	 */
2482:     if (flags & SVHEAD) {
2483: #ifdef MMDF
2484:         if (!isprogram)
2485:             fprintf(ufp, "\001\001\001\001\n");
2486: #endif /* MMDF */
2487: #ifdef V7MAIL
2488:         h->subtime = cgtdate(h->subdate);
2489:         fprintf(ufp, "From %s %s",
2490: #ifdef INTERNET
2491:                 h->from,
2492: #else
2493:                 h->path,
2494: #endif
2495:                     ctime(&h->subtime));
2496: #endif
2497:         hprint(h, ufp, 2);
2498: #ifdef V7MAIL
2499:         tprint(fp, ufp, TRUE);
2500:         putc('\n', ufp);    /* force blank line at end (ugh) */
2501: #else
2502:         tprint(fp, ufp, FALSE);
2503: #endif
2504:     } else {
2505:         tprint(fp, ufp, FALSE);
2506:     }
2507: 
2508:     fclose(ufp);
2509:     if (isprogram) {
2510:         (void) sprintf(prog, "(%s)<%s", to + 1, fname);
2511:         shcmd(prog, CWAIT);
2512:         prflags |= NOPRT;
2513:     } else {
2514:         if ((flags & OVWRITE) == 0)
2515:             msg("file: %s %s", to, isnew ? "created" : "appended");
2516:         else
2517:             msg("file: %s written", to);
2518:     }
2519: 
2520: out:
2521:     if (isprogram) {
2522:         (void) unlink(fname);
2523:     }
2524:     (void) umask(N_UMASK);
2525:     (void) fseek(fp, saveoff, 0);
2526: }
2527: 
2528: xxit(status)
2529: int status;
2530: {
2531:     (void) unlink(infile);
2532:     (void) unlink(outfile);
2533: #ifdef SORTACTIVE
2534:     if (strncmp(ACTIVE,"/tmp/", 5) == 0)
2535:         (void) unlink(ACTIVE);
2536: #endif /* SORTACTIVE */
2537:     if (ospeed) {   /* is == 0, we haven't been in raw mode yet */
2538:         botscreen();
2539:         vflush();
2540:         ttycooked();
2541:     }
2542:     exit(status);
2543: }

Defined functions

addnum defined in line 1886; used 3 times
appfile defined in line 1442; used 5 times
cancel_command defined in line 880; used 1 times
checkin defined in line 2096; used 4 times
clearin defined in line 2139; used 1 times
direct_reply defined in line 996; used 1 times
docmd defined in line 335; used 1 times
findend defined in line 1682; used 1 times
findparent defined in line 1408; used 2 times
fmthdr defined in line 1189; used 1 times
getmailname defined in line 1962; used 2 times
getnextart defined in line 1062; used 4 times
growline defined in line 1508; used 1 times
msg defined in line 1773; used 35 times
next_ng_command defined in line 1028; used 1 times
onalarm defined in line 1899; used 1 times
onint defined in line 2381; used 3 times
onstop defined in line 2401; used 8 times
outline defined in line 1515; used 5 times
prget defined in line 1528; used 5 times
prun defined in line 1598; used 4 times
pushback defined in line 2084; used 1 times
readr defined in line 175; used 1 times
reply defined in line 907; used 3 times
shcmd defined in line 1588; used 3 times
tfappend defined in line 1711; used 19 times
tfget defined in line 1751; used 4 times
tfput defined in line 1731; used 3 times
tfseek defined in line 1761; used 2 times
timer defined in line 1916; used 4 times
ttycooked defined in line 2362; used 4 times
ttyowait defined in line 2350; used 1 times
ttyraw defined in line 2338; used 3 times
ttysave defined in line 2269; used 1 times
updscr defined in line 1821; used 6 times
vcmd defined in line 236; used 1 times
vflush defined in line 2168; used 11 times
vgetc defined in line 2004; used 6 times
vhprint defined in line 1313; used 1 times
vputc defined in line 2155; used 24 times
vsave defined in line 2435; used 1 times
winch_upd defined in line 1798; used 2 times
xtabf defined in line 1283; used 21 times
xtabs defined in line 1255; used 1 times

Defined variables

Progname defined in line 92; used 3 times
SccsId defined in line 7; never used
UPDATING defined in line 1789; used 3 times
aabs defined in line 169; used 8 times
alflag defined in line 116; used 6 times
alrmjmp defined in line 129; used 2 times
artbody defined in line 98; used 2 times
artlines defined in line 101; used 12 times
artread defined in line 102; used 4 times
atend defined in line 117; used 4 times
bptr defined in line 161; used 28 times
cerase defined in line 118; used 5 times
cintr defined in line 120; used 12 times
ckill defined in line 119; used 7 times
curflag defined in line 111; used 10 times
cwerase defined in line 122; used 4 times
dgest defined in line 172; used 1 times
dlinno defined in line 112; used 40 times
ed defined in line 170; used 6 times
edcmdbuf defined in line 157; used 2 times
endsuba defined in line 137; used 11 times
erased defined in line 100; used 3 times
goodone defined in line 155; used 5 times
h defined in line 165; used 40 times
hasdb defined in line 133; used 2 times
hbuf1 defined in line 164; used 1 times
hbuf2 defined in line 164; used 1 times
hdrend defined in line 104; used 8 times
hdrstart defined in line 103; used 4 times
inbuf defined in line 1990; used 6 times
innext defined in line 1994; used 5 times
innleft defined in line 1992; used 11 times
intflag defined in line 125; used 4 times
intjmp defined in line 129; used 2 times
ismail defined in line 114; used 6 times
lastlin defined in line 105; used 13 times
linebuf defined in line 95; used 104 times
mailf defined in line 115; used 3 times
maxcol defined in line 1440; used 12 times
maxlinno defined in line 107; used 6 times
newltchars defined in line 2262; used 5 times
newtty defined in line 2260; used 14 times
ofilename1 defined in line 163; used 4 times
oflags defined in line 1997; used 8 times
ogroupdir defined in line 156; used 10 times
oldltchars defined in line 2262; used 4 times
oldtty defined in line 2260; used 13 times
ongsize defined in line 159; used 1 times
  • in line 73
oobit defined in line 171; used 5 times
ospeed defined in line 124; used 5 times
outbuf defined in line 1991; used 3 times
outnext defined in line 1995; used 4 times
outnleft defined in line 1993; used 3 times
pngsize defined in line 160; used 6 times
prflags defined in line 110; used 29 times
prompt defined in line 109; used 13 times
ptr1 defined in line 168; used 22 times
ptr2 defined in line 168; used 19 times
ptr3 defined in line 168; used 9 times
quitflg defined in line 99; used 7 times
reading defined in line 128; used 6 times
rfq defined in line 158; used 4 times
savelinno defined in line 1786; used 5 times
savepr defined in line 1787; used 2 times
secpr defined in line 108; used 11 times
tf defined in line 170; used 13 times
tfilename defined in line 162; used 1 times
tflinno defined in line 106; used 4 times
tfname defined in line 97; used 4 times
tft defined in line 144; used 1 times
timestr defined in line 113; used 2 times

Defined macros

ARTWIN defined in line 50; used 7 times
ARTWLEN defined in line 40; used 20 times
BKGRND defined in line 63; used 6 times
CURHOME defined in line 67; used 3 times
CURP1 defined in line 65; used 2 times
CURP2 defined in line 66; used 3 times
CWAIT defined in line 62; used 4 times
HDRONLY defined in line 57; used 9 times
HELPMSG defined in line 60; used 4 times
INBUFSIZ defined in line 1988; used 3 times
META defined in line 55; used 3 times
NEWART defined in line 59; used 6 times
NLINES defined in line 74; used 2 times
NOPRT defined in line 58; used 11 times
OVWRITE defined in line 70; used 5 times
PIPECHAR defined in line 54; used 3 times
PRLINE defined in line 48; used 7 times
SECPRLEN defined in line 51; used 2 times
SPLINE defined in line 49; used 2 times
SVHEAD defined in line 69; used 2 times
ULINE defined in line 1439; used 3 times
clearok defined in line 78; used 4 times
even defined in line 41; used 3 times
saveart defined in line 73; used 8 times
sigmask defined in line 27; used 13 times
Last modified: 1986-03-20
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 7660
Valid CSS Valid XHTML 1.0 Strict