/* * visual - visual news interface. * Kenneth Almquist */ #ifdef SCCSID static char *SccsId = "@(#)visual.c 1.28 3/19/86"; #endif /* SCCSID */ #include "rparams.h" #ifdef USG #include #include #include #else /* !USG */ #include #endif /* !USG */ #include #if defined(BSD4_2) || defined(BSD4_1C) #include #else #include "ndir.h" #endif #ifdef BSD4_2 #ifndef sigmask #define sigmask(m) (1<<((m)-1)) #endif /* !sigmask */ #endif /* BSD4_2 */ #ifdef MYDB #include "db.h" #endif /* MYDB */ extern int errno; #ifdef SIGTSTP #include #endif /* SIGTSTP */ #define ARTWLEN (ROWS-2)/* number of lines used to display article */ #define even(cols) ((cols&1) ? cols + 1 : cols) #ifdef STATTOP #define PRLINE 0 /* prompter line */ #define SPLINE 1 /* secondary prompt line */ #define ARTWIN 2 /* first line of article window */ #define SECPRLEN 81 /* length of secondary prompter */ #else #define PRLINE (ROWS-1)/* prompter line */ #define SPLINE (ROWS-2)/* secondary prompt line */ #define ARTWIN 0 /* first line of article window */ #define SECPRLEN 100 /* length of secondary prompter */ #endif #define PIPECHAR '|' /* indicate save command should pipe to program */ #define META 0200 /* meta character bit (as in emacs) */ /* print (display) flags */ #define HDRONLY 0001 /* print header only */ #define NOPRT 0002 /* don't print at all */ #define NEWART 0004 /* force article display to be regenerated */ #define HELPMSG 0010 /* display currently contains help message */ /* prun flags */ #define CWAIT 0001 /* type "continue?" and wait for return */ #define BKGRND 0002 /* run process in the background */ /* values of curflag */ #define CURP1 1 /* cursor after prompt */ #define CURP2 2 /* cursor after secondary prompt */ #define CURHOME 3 /* cursor at home position */ /* flags for vsave routine */ #define SVHEAD 01 /* write out article header */ #define OVWRITE 02 /* overwrite the file if it already exists */ /* other files */ #define saveart oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hptr = h;h = hold;hold = hptr;ongsize = pngsize #define NLINES(h, fp) (h->numlines[0] ? h->intnumlines : (h->intnumlines=linecnt(fp),sprintf(h->numlines, "%d", h->intnumlines), h->intnumlines)) /* terminal handler stuff */ extern int _junked; #define clearok(xxx, flag) _junked = flag extern int COLS; extern int ROWS; extern int hasscroll; FILE *tmpfile(); char *getmailname(); #ifdef MYDB char *findparent(); #endif /* MYDB */ int onint(); int onstop(); int xxit(); char *Progname = "vnews"; /* for xerror */ /* variables shared between vnews routines */ static char linebuf[LBUFLEN]; /* temporary workspace */ static FILE *tfp; /* temporary file */ static char tfname[] = "/tmp/vnXXXXXX"; /* name of temp file */ static long artbody; /* offset of body into article */ static int quitflg; /* if set, then quit */ static int erased; /* current article has been erased */ static int artlines; /* # lines in article body */ static int artread; /* entire article has been read */ static int hdrstart; /* beginning of header */ static int hdrend; /* end of header */ static int lastlin; /* number of lines in tempfile */ static int tflinno = 0; /* next line in tempfile */ static int maxlinno; /* number of lines in file + folded */ static char secpr[SECPRLEN]; /* secondary prompt */ static char prompt[30]; /* prompter */ static short prflags; /* print flags (controls updscr) */ static short curflag; /* where to locate cursor */ static int dlinno; /* top line on screen */ static char timestr[20]; /* current time */ static int ismail; /* true if user has mail */ static char *mailf; /* user's mail file */ static int alflag; /* set if unprocessed alarm signal */ static int atend; /* set if at end of article */ static char cerase; /* erase character */ static char ckill; /* kill character */ static char cintr; /* interrupt character */ #ifdef TIOCGLTC static char cwerase; /* word erase character */ #endif /* TIOCGLTC */ short ospeed; /* terminal speed NOT STATIC */ static int intflag; /* set if interrupt received */ #ifdef SIGTSTP static int reading; /* to keep stupid BSD from restarting reads */ jmp_buf intjmp, alrmjmp; #endif /* SIGTSTP */ #ifdef MYDB static int hasdb; /* true if article data base exists */ #endif /* MYDB */ #ifdef DIGPAGE static int endsuba; /* end of sub-article in digest */ #endif #ifdef MYDEBUG FILE *debugf; /* file to write debugging info on */ #endif char *tft = "/tmp/folXXXXXX"; /* * These were made static for u370 with its buggy cc. * I judged it better to have one copy with no ifdefs than * to conditionally compile them as automatic variables * in readr (which they originally were). Performance * considerations might warrant moving some of the simple * things into register variables, but I don't know what * breaks the u370 cc. */ static char goodone[BUFLEN]; /* last decent article */ static char ogroupdir[BUFLEN]; /* last groupdir */ static char edcmdbuf[128]; static int rfq = 0; /* for last article */ static long ongsize; /* Previous ngsize */ static long pngsize; /* Printing ngsize */ static char *bptr; /* temp pointer. */ static char *tfilename; /* temporary file name */ static char ofilename1[BUFLEN]; /* previous file name */ static struct hbuf hbuf1, hbuf2; /* for minusing */ static struct hbuf *h = &hbuf1, /* current header */ *hold = &hbuf2, /* previous header */ *hptr; /* temporary */ static char *ptr1, *ptr2, *ptr3; /* for reply manipulation */ static int aabs = FALSE; /* TRUE if we asked absolutely */ static char *ed, tf[100]; static long oobit; /* last bit, really */ static int dgest = 0; static FILE *fp; /* current article to be printed*/ readr() { #ifdef MYDEBUG debugf = fopen("DEBUG", "w"); setbuf(debugf, (char *)NULL); #endif if (aflag) { if (*datebuf) { if ((atime = cgtdate(datebuf)) == -1) xerror("Cannot parse date string"); } else atime = 0; } if (SigTrap) xxit(1); (void) mktemp(tfname); (void) close(creat(tfname,0666)); if ((tfp = fopen(tfname, "w+")) == NULL) xerror("Can't create temp file"); (void) unlink(tfname); mailf = getmailname(); #ifdef MYDB if (opendb() >= 0) { hasdb = 1; fputs("Using article data base\n", stderr); /*DEBUG*/ getng(); } #endif ttysave(); (void) signal(SIGINT, onint); (void) signal(SIGQUIT, xxit); if (SigTrap) xxit(1); ttyraw(); timer(); /* loop reading articles. */ fp = NULL; obit = -1; nextng(); quitflg = 0; while (quitflg == 0) { if (getnextart(FALSE)) break; (void) strcpy(goodone, filename); if (SigTrap) return; vcmd(); } if (!news) { if (!checkngs(header.nbuf, actfp)) fprintf(stderr, "No news.\n"); } } /* * Read and execute a command. */ vcmd() { register c; char *p; long count; int countset; appfile(fp, dlinno + ARTWLEN + 1); #ifdef DIGPAGE endsuba = findend(dlinno); if (artlines > dlinno + ARTWLEN || endsuba > 0 && endsuba < artlines #else if (artlines > dlinno + ARTWLEN #endif || (prflags & HDRONLY) && artlines > hdrend) { atend = 0; if (prflags&HDRONLY || maxlinno == 0) (void) strcpy(prompt, "more? "); else #ifdef DIGPAGE (void) sprintf(prompt, "more(%d%%)? ", ((((endsuba > 0) ? endsuba : (dlinno + ARTWLEN)) - hdrend) * 100) / maxlinno); #else /* !DIGPAGE */ (void) sprintf(prompt, "more(%d%%)? ", ((dlinno + ARTWLEN - hdrend) * 100) / maxlinno); #endif /* !DIGPAGE */ } else { atend = 1; (void) strcpy(prompt, "next? "); if (!erased) clear(bit); /* article read */ } curflag = CURP1; p = prompt + strlen(prompt); countset = 0; count = 0; /* * Loop while accumulating a count, until an action character * is entered. Also handle "meta" here. * * Count is the current count. Countset=0 means no count * currently exists. Countset=1, count=0 is valid and means * a count of 0 has been entered */ for (;;) { c = vgetc(); if (c == cerase || c == '\b' || c == '\177') { if (countset == 0) break; /* Use as action char */ if (count < 10) countset = 0; /* Erase only char of count */ else count /= 10L; /* Erase 1 char of count */ } else { #ifdef TIOCGLTC if (c == ckill || c == cwerase) { #else if (c == ckill) { #endif if (countset == 0) break; countset = 0; } else if (c < '0' || c > '9') break; else { countset = 1; count = (count * 10) + (c - '0'); } } if (countset) { (void) sprintf(p, "%ld", count); } else { *p = '\0'; count = 0; } } if (c == '\033') { /* escape */ (void) strcat(prompt, "M-"); c = vgetc(); if (c != cintr) c |= META; } secpr[0] = '\0'; if (countset == 0) count = 1; docmd(c, count); if (c != '?' && c != 'H') /* UGGH */ prflags &=~ HELPMSG; if (dlinno > hdrstart) prflags &=~ HDRONLY; } /* * Process one command, which has already been typed in. */ docmd(c, count) int c; long count; { int i; long nart, Hoffset; char *findhist(); switch (c) { /* Show more of current article, or advance to next article */ case '\n': case ' ': #ifdef DIGPAGE case 'm': #endif /* DIGPAGE */ case '\06': /* Control-F for vi compat */ prflags &=~ NOPRT; if (atend) goto next; else if (prflags & HDRONLY) { prflags &=~ HDRONLY; if (hasscroll) dlinno = hdrstart;} #ifdef DIGPAGE else if (endsuba > 0) dlinno = endsuba; else if (c == 'm') { do { if (lastlin >= maxlinno) goto next; else appfile(fp, lastlin + 1); } while(strncmp(linebuf, "------------------------", 24) != 0); dlinno = endsuba = lastlin; } #endif else if ((appfile(fp, dlinno + 2 * ARTWLEN), artread) && hasscroll && artlines - dlinno <= ARTWLEN + 2) dlinno = artlines - ARTWLEN; else dlinno += ARTWLEN * count; break; /* No. Go on to next article. */ case '.': /* useful if you have a keypad */ next: case 'n': readmode = NEXT; FCLOSE(fp); clear(bit); saveart; nextbit(); break; /* Back up count pages */ case '\b': case '\177': if (dlinno == 0) goto backupone; /* NO BREAK */ case META|'v': case '\002': /* Control-B */ dlinno -= ARTWLEN * count; if (dlinno < 0) dlinno = 0; break; /* forward half a page */ case '\004': /* Control-D, as in vi */ if (!atend) dlinno += ARTWLEN/2 * count; break; /* backward half a page */ case '\025': /* Control-U */ dlinno -= ARTWLEN/2 * count; if (dlinno < 0) dlinno = 0; break; /* forward count lines */ case '\016': /* Control-N */ case '\005': /* Control-E */ dlinno += count; break; /* backwards count lines */ case '\020': /* Control-P */ case '\031': /* Control-Y */ dlinno -= count; if (dlinno < 0) dlinno = 0; break; /* Turn displaying of article back on */ case 'l': case 'd': prflags &=~ NOPRT; break; /* display header */ case 'h': dlinno = hdrstart; prflags |= HDRONLY; prflags &=~ NOPRT; break; /* * Unsubscribe to the newsgroup and go on to next group */ case 'U': case 'u': strcat(prompt, "u"); c = vgetc(); if (c == 'g') { obit = -1; FCLOSE(fp); zapng = TRUE; saveart; if (nextng()) { if (actdirect == BACKWARD) msg("Can't back up."); else quitflg = 1; /* probably unnecessary */ } } else { if (c != cintr && c != ckill) msg("Illegal command"); } break; /* Print the current version of news */ case 'v': msg("News version: %s", news_version); break; /* Decrypt joke. Always does rot 13 */ case 'D': appfile(fp, 32767); for (i = hdrend ; i < artlines ; i++) { register char ch, *p; tfget(linebuf, i); for (p = linebuf ; (ch = *p) != '\0' ; p++) { if (ch >= 'a' && ch <= 'z') *p = (ch - 'a' + 13) % 26 + 'a'; else if (ch >= 'A' && ch <= 'Z') *p = (ch - 'A' + 13) % 26 + 'A'; } tfput(linebuf, i); } prflags |= NEWART; prflags &=~ (HDRONLY|NOPRT); break; /* write out the article someplace */ /* w writes out without the header */ case 's': case 'w': { char *grn = groupdir; int wflags; msg("file: "); curflag = CURP2; while ((wflags = vgetc()) == ' '); if (wflags == cintr) { secpr[0] = '\0'; break; } if (wflags == '|') { linebuf[0] = '|'; if (prget("| ", linebuf+1)) break; } else { pushback(wflags); if (prget("file: ", linebuf)) break; } wflags = 0; if (c == 's') wflags |= SVHEAD; if (count != 1) wflags |= OVWRITE; bptr = linebuf; while( *bptr == ' ') bptr++; /* strip leading spaces */ if (*bptr != PIPECHAR && *bptr != '/') { char hetyped[BUFLEN]; char *boxptr; (void) strcpy(hetyped, bptr); if (hetyped[0] == '~' && hetyped[1] == '/') { strcpy(hetyped, bptr+2); strcpy(bptr, userhome); } else if (boxptr = getenv("NEWSBOX")) { if (index(boxptr, '%')) { struct stat stbf; sprintf(bptr, boxptr, grn); if (stat(bptr,&stbf) < 0) { if (mkdir(bptr, 0777) < 0) { msg("Cannot create directory %s", bptr); break; } } else if ((stbf.st_mode&S_IFMT) != S_IFDIR) { msg("%s not a directory", bptr); break; } } else strcpy(bptr, boxptr); } else bptr[0] = '\0'; if (bptr[0]) (void) strcat(bptr, "/"); if (hetyped[0] != '\0') (void) strcat(bptr, hetyped); else (void) strcat(bptr, "Articles"); } vsave(bptr, wflags); break; } /* back up */ case '-': caseminus: aabs = TRUE; if (!*ofilename1) { msg("Can't back up."); break; } FCLOSE(fp); hptr = h; h = hold; hold = hptr; (void) strcpy(bfr, filename); (void) strcpy(filename, ofilename1); (void) strcpy(ofilename1, bfr); obit = bit; if (strcmp(groupdir, ogroupdir)) { (void) strcpy(bfr, groupdir); selectng(ogroupdir, FALSE, FALSE); (void) strcpy(groupdir, ogroupdir); (void) strcpy(ogroupdir, bfr); ngrp = 1; back(); } bit = oobit; oobit = obit; obit = -1; getnextart(TRUE); break; /* skip forwards */ case '+': case '=': caseplus: if (count == 0) break; saveart; last = bit; for (i = 0; i < count; i++) { nextbit(); if ((bit > pngsize) || (rflag && bit < 1)) break; } FCLOSE(fp); obit = -1; break; /* exit - time updated to that of most recently read article */ case 'q': quitflg = 1; break; case 'x': xxit(0); break; /* cancel the article. */ case 'c': strcpy(prompt, "cancel [n]? "); if (vgetc() != 'y') { msg("Article not cancelled"); break; } cancel_command(); break; /* escape to shell */ case '!': { register char *p; int flags; p = linebuf; if (prget("!", p)) break; flags = CWAIT; if (*p == '\0') { (void) strcpy(linebuf, SHELL); flags = 0; } while (*p) p++; while (p > linebuf && p[-1] == ' ') p--; if (*--p == '&') { *p = '\0'; flags = BKGRND; } else if (*p == '|') { *p = '\0'; (void) sprintf(bfr, "(%s)|mail '%s'", linebuf, username); (void) strcpy(linebuf, bfr); flags |= BKGRND; } else { prflags |= NOPRT; } shcmd(linebuf, flags); break; } /* mail reply */ case 'r': reply(FALSE); break; case 'R': reply(TRUE); break; case META|'r': direct_reply(); break; /* next newsgroup */ case 'N': FCLOSE(fp); if (next_ng_command()) quitflg = 1; break; /* mark the rest of the articles in this group as read */ case 'K': saveart; while (bit <= ngsize && bit >= minartno) { clear(bit); nextbit(); } FCLOSE(fp); break; /* Print the full header */ case 'H': if (fp == NULL) { msg("No current article"); break; } move(ARTWIN, 0); Hoffset = ftell(fp); (void) fseek(fp, 0L, 0); for (i = 0; i < ARTWLEN; i++) { if (fgets(linebuf, COLS, fp) == NULL) break; if (linebuf[0] == '\n') break; linebuf[COLS] = '\0'; addstr(linebuf); } (void) fseek(fp, Hoffset, 0); for(; i < ARTWLEN; i++) addstr(linebuf); prflags |= HELPMSG|NEWART; break; case 'b': /* backup 1 article */ backupone: count = bit - 1; /* NO BREAK */ case 'A': /* specific number */ if (count > pngsize) { msg("not that many articles"); break; } readmode = SPEC; aabs = TRUE; bit = count; obit = -1; FCLOSE(fp); break; /* display parent article */ case 'p': #ifdef MYDB if (hasdb && (ptr3 = findparent(h->ident, &nart)) != NULL) { msg("parent: %s/%ld", ptr3, nart); /*DEBUG*/ updscr(); /*DEBUG*/ goto selectart; } #endif if (h->followid[0] == '\0') { msg("no references line"); break; } ptr1 = h->followid + strlen(h->followid); do { ptr2 = ptr1; if (*ptr2 == '\0') ptr1 = rindex(h->followid, ' '); else { *ptr2 = '\0'; ptr1 = rindex(h->followid, ' '); *ptr2 = ' '; } } while (ptr1 != NULL && --count > 0); if (ptr1 == NULL) ptr1 = h->followid; else ++ptr1; (void) strncpy(linebuf, ptr1, ptr2 - ptr1); linebuf[ptr2 - ptr1] = '\0'; msg("%s", linebuf); curflag = CURP2; updscr(); /* may take this out later */ goto searchid; /* specific message ID. */ case '<': /* could improve this */ linebuf[0] = '<'; if (prget("<", linebuf+1)) break; searchid: secpr[0] = '\0'; if (index(linebuf, '@') == NULL && index(linebuf, '>') == NULL) { ptr1 = linebuf; if (*ptr1 == '<') ptr1++; ptr2 = index(ptr1, '.'); if (ptr2 != NULL) { *ptr2++ = '\0'; (void) sprintf(bfr, "<%s@%s.UUCP>", ptr2, ptr1); (void) strcpy(linebuf, bfr); } } if (index(linebuf, '>') == NULL) (void) strcat(linebuf, ">"); ptr1 = findhist(linebuf); if (ptr1 == NULL) { msg("%s not found", linebuf); break; } ptr2 = index(ptr1, '\t'); ptr3 = index(++ptr2, '\t'); ptr2 = index(++ptr3, ' '); if (ptr2) *ptr2 = '\0'; ptr2 = index(ptr3, '/'); if (!ptr2) { if (strcmp(++ptr3, "cancelled") == 0) msg("%s has been cancelled", linebuf); else msg("%s has expired", linebuf); break; } *ptr2++ = '\0'; (void) sscanf(ptr2, "%ld", &nart); /* * Go to a given article. Ptr3 specifies the newsgroup * and nart specifies the article number. */ #ifdef MYDB selectart: #endif /* MYDB */ aabs = TRUE; FCLOSE(fp); saveart; (void) strcpy(ogroupdir, ptr3); if (strcmp(groupdir, ogroupdir)) { (void) strcpy(bfr, groupdir); selectng(ogroupdir, TRUE, PERHAPS); (void) strcpy(groupdir, ogroupdir); (void) strcpy(ogroupdir, bfr); ngrp = 1; back(); } bit = nart; oobit = obit; obit = -1; getnextart(TRUE); if (bit != nart || strcmp(groupdir, ptr3) != 0) { msg("can't read %s/%ld", ptr3, nart); goto caseminus; } rfq = 0; break; /* follow-up article */ case 'f': if (strcmp(h->followto, "poster") == 0) { reply(FALSE); break; } (void) sprintf(bfr, "%s/%s %s", BIN, "postnews", goodone); shcmd(bfr, CWAIT); break; /* erase - pretend we haven't seen this article. */ case 'e': erased = 1; set(bit); goto caseplus; /* skip this article for now */ case '#': msg("Article %ld of %ld", rfq ? oobit : bit, pngsize); break; /* error */ case '?': { FILE *helpf; (void) sprintf(linebuf, "%s/vnews.help", LIB); if ((helpf = fopen(linebuf, "r")) == NULL) { msg("Can't open help file"); break; } move(ARTWIN, 0); while (fgets(linebuf, LBUFLEN, helpf) != NULL) addstr(linebuf); (void) fclose(helpf); prflags |= HELPMSG|NEWART; } break; default: if (c != ckill && c != cintr && c != cerase) #ifdef TIOCGLTC if (c != cwerase) #endif msg("Illegal command"); break; } return FALSE; } cancel_command() { int notauthor; tfilename = filename; (void) strcpy(rcbuf, h->path); ptr1 = index(rcbuf, ' '); if (ptr1) *ptr1 = 0; notauthor = strcmp(username, rcbuf); if (uid != ROOTID && uid && notauthor) { msg("Can't cancel what you didn't write."); return; } if (!cancel(stderr, h, notauthor)) { clear(bit); saveart; nextbit(); obit = -1; fp = NULL; } FCLOSE(fp); } /* * Generate replies */ reply(include) int include; { char *arg[4]; register FILE *rfp; char subj[132]; register char *p; char *replyname(); struct stat statb; time_t creatm; /* Put the user in the editor to create the body of the reply. */ ed = getenv("EDITOR"); if (ed == NULL || *ed == '\0') ed = DFTEDITOR; if (ed == NULL) { msg("You don't have an editor"); return; } arg[0] = "/bin/sh"; arg[1] = "-c"; (void) strcpy(tf, tft); (void) mktemp(tf); (void) close(creat(tf,0600)); if ((rfp = fopen(tf, "w")) == NULL) { msg("Can't create %s", tf) ; return; } (void) strcpy(subj, h->title); if (!prefix(subj, "Re:")){ (void) strcpy(bfr, subj); (void) sprintf(subj, "Re: %s", bfr); } p = replyname(h); fprintf(rfp, "To: %s\n", p); fprintf(rfp, "Subject: %s\n", subj); fprintf(rfp, "In-reply-to: your article %s\n", h->ident); #ifdef INTERNET fprintf(rfp, "News-Path: %s\n", h->path); #endif /* INTERNET */ (void) sprintf(rcbuf, "%s -t < %s; rm -f %s", MAILPARSER, tf, tf); putc('\n', rfp); if (include) { FILE *of; char buf[BUFSIZ]; of = xfopen(goodone, "r"); while (fgets(buf, sizeof buf, of) != NULL) if (buf[0] == '\n') break; while (fgets(buf, sizeof buf, of) != NULL) fprintf(rfp, "> %s", buf); fclose(of); putc('\n', rfp); } fflush(rfp); (void) fstat(fileno(rfp), &statb); creatm = statb.st_mtime; (void) fclose(rfp); (void) sprintf(edcmdbuf, "exec %s %s", ed, tf); arg[2] = edcmdbuf; arg[3] = NULL; if (prun(arg, 0) != 0) { msg("Couldn't run editor"); (void) unlink(tf); return; } if (access(tf, 4) || stat(tf, &statb)) { msg("No input file - mail not sent"); (void) unlink(tf); return; } if (statb.st_mtime == creatm || statb.st_size < 5) { msg("File unchanged - no message posted"); (void) unlink(tf); return; } arg[2] = rcbuf; arg[3] = NULL; prun(arg, BKGRND); prflags |= NOPRT; } direct_reply() { register char *p; register char *q; char *arg[4]; char address[PATHLEN]; extern char *replyname(); extern char *getenv(); arg[0] = "/bin/sh"; arg[1] = "-c"; p = replyname(h); q = address; while (*p != '\0') { if (index("\"\\$", *p) != 0) *q++ = '\\'; *q++ = *p++; } *q++ = '\0'; if ((MAILER = getenv("MAILER")) == NULL) MAILER = "mail"; sprintf(rcbuf, MAILER, hptr->title); sprintf(bfr, "%s %s", rcbuf, address); arg[2] = bfr; arg[3] = NULL; if (prun(arg, 0) != 0) { msg("Couldn't run mailer"); return; } prflags |= NOPRT; } next_ng_command() { obit = -1; if (prget("group? ", linebuf)) return FALSE; bptr = linebuf; if (!*bptr || *bptr == '-') { if (*bptr) actdirect = BACKWARD; saveart; if (nextng()) { if (actdirect == BACKWARD) msg("Can't back up."); else return TRUE; } return FALSE; } while (isspace(*bptr)) bptr++; if (!validng(bptr)) { msg("No such group."); return FALSE; } saveart; back(); selectng(bptr, TRUE, TRUE); return FALSE; } /* * Find the next article we want to consider, if we're done with * the last one, and show the header. */ getnextart(minus) int minus; { int noaccess; register DIR *dirp; register struct direct *dir; long nextnum, tnum; long atol(); noaccess = 0; if (minus) goto nextart2; /* Kludge for "-" command. */ if (bit == obit) /* Return if still on same article as last time */ return 0; nextart: if (news) { curflag = CURHOME; _amove(0, 0); vflush(); } dgest = 0; /* If done with this newsgroup, find the next one. */ while (ngsize <= 0 || (!rflag && ((long) bit > ngsize)) || (rflag && bit < minartno)) { if (nextng()) { if (actdirect == BACKWARD) { msg("Can't back up."); actdirect = FORWARD; continue; } else /* if (rfq++ || pflag || cflag) */ return 1; } if (rflag) bit = ngsize + 1; else bit = -1; noaccess = 2; } /* speed things up by not searching for article -1 */ if (bit < 0) { bit = minartno - 1; nextbit(); aabs = FALSE; goto nextart; } nextart2: if (rcreadok) rcreadok = 2; /* have seen >= 1 article */ (void) sprintf(filename, "%s/%ld", dirname(groupdir), bit); if (rfq && goodone[0]) /* ??? */ strcpy(filename, goodone); if (SigTrap == SIGHUP) return 1; /* Decide if we want to show this article. */ if ((fp = fopen(filename, "r")) == NULL) { /* since there can be holes in legal article numbers, */ /* we wait till we hit 5 consecutive bad articles */ /* before we haul off and scan the directory */ if (++noaccess < 5) goto badart; noaccess = 0; dirp = opendir(dirname(groupdir)); if (dirp == NULL) { if (errno != EACCES) msg("Can't open %s", dirname(groupdir)); goto nextart; } nextnum = rflag ? minartno - 1 : ngsize + 1; while ((dir = readdir(dirp)) != NULL) { if (!dir->d_ino) continue; tnum = atol(dir->d_name); if (tnum <= 0) continue; if (rflag ? (tnum > nextnum && tnum < bit) : (tnum < nextnum && tnum > bit)) nextnum = tnum; } closedir(dirp); if (rflag ? (nextnum >= bit) : (nextnum <= bit)) goto badart; do { clear(bit); nextbit(); } while (rflag ? (nextnum < bit) : (nextnum > bit)); obit = -1; aabs = FALSE; goto nextart; } else noaccess = 0; if (hread(h, fp, TRUE) == NULL || (!rfq && !aselect(h, aabs))) { badart: FCLOSE(fp); clear(bit); obit = -1; nextbit(); aabs = FALSE; goto nextart; } aabs = FALSE; actdirect = FORWARD; news = TRUE; artbody = ftell(fp); fmthdr(); artlines = lastlin; artread = 0; prflags |= NEWART; prflags &=~ NOPRT; if (! cflag && hdrend < ARTWLEN && !cflag) prflags |= HDRONLY; dlinno = 0; maxlinno = NLINES(h, fp); erased = 0; obit = bit; return 0; } /* * Print out whatever the appropriate header is */ fmthdr() { char *briefdate(); static FILE *ngfd = NULL; static int triedopen = 0; char pbuf[BUFLEN], *printbuffer = groupdir; lastlin = 0; if (ngrp) { pngsize = ngsize; ngrp--; if (!hflag) { if (!triedopen) { (void) sprintf(pbuf,"%s/newsgroups", LIB); ngfd = fopen(pbuf, "r"); triedopen++; } if (ngfd != NULL) { register char *p; char ibuf[BUFLEN]; rewind(ngfd); while (fgets(ibuf, BUFLEN, ngfd) != NULL) { p = index(ibuf, '\t'); if (p) *p++ = '\0'; if (strcmp(ibuf, groupdir) == 0) { register char *q; q = rindex(p, '\t'); if (q) { p = q; *p++ = '\0'; } if (p) { q = index(p, '\n'); if (q) *q = '\0'; if (*--q == '.') *q = '\0'; (void) sprintf(pbuf,"%s (%s)", groupdir, p); printbuffer = pbuf; } break; } } } (void) sprintf(linebuf, "Newsgroup %s", printbuffer); tfappend(linebuf); } } hdrstart = lastlin; if (!hflag) { (void) sprintf(linebuf, "Article %s %s", h->ident, briefdate(h->subdate)); tfappend(linebuf); } xtabs(h); vhprint(h, pflag ? 1 : 0); (void) sprintf(linebuf, "(%d lines)", NLINES(h, fp)); tfappend(linebuf); tfappend(""); hdrend = lastlin; } /* * Grow tabs into spaces in header fields, 'cause the rest of this * lax program drops turds all over tabs (so it does with \b's, but ..) */ xtabs(p) register struct hbuf *p; { xtabf(p->from, sizeof p->from); xtabf(p->path, sizeof p->path); xtabf(p->nbuf, sizeof p->nbuf); xtabf(p->title, sizeof p->title); xtabf(p->ident, sizeof p->ident); xtabf(p->replyto, sizeof p->replyto); xtabf(p->followid, sizeof p->followid); xtabf(p->subdate, sizeof p->subdate); xtabf(p->expdate, sizeof p->expdate); xtabf(p->ctlmsg, sizeof p->ctlmsg); xtabf(p->sender, sizeof p->sender); xtabf(p->followto, sizeof p->followto); xtabf(p->distribution, sizeof p->distribution); xtabf(p->organization, sizeof p->organization); xtabf(p->numlines, sizeof p->numlines); xtabf(p->keywords, sizeof p->keywords); xtabf(p->summary, sizeof p->summary); xtabf(p->approved, sizeof p->approved); xtabf(p->nf_id, sizeof p->nf_id); xtabf(p->nf_from, sizeof p->nf_from); #ifdef DOXREFS xtabf(p->xref, sizeof p->xref); #endif /* DOXREFS */ } xtabf(s, size) char *s; int size; { register char *p, *str; register c, i; char buf[LBUFLEN]; str = s; if (index(str, '\t') == NULL) return; i = 0; for (p = buf; c = *str++; i++) { if (c == '\t') { *p++ = ' '; if ((i & 7) != 7) str--; } else if (c == '\n') { i = -1; *p++ = c; } else *p++ = c; } *p = '\0'; strncpy(s, buf, size - 1); } /* * Print the file header to the temp file. */ vhprint(hp, verbose) register struct hbuf *hp; int verbose; { register char *p1, *p2; char fname[BUFLEN]; char *tailpath(); fname[0] = '\0'; /* init name holder */ p1 = index(hp->from, '('); /* Find the sender's full name. */ if (p1 == NULL && hp->path[0]) p1 = index(hp->path, '('); if (p1 != NULL) { (void) strcpy(fname, p1+1); p2 = index(fname, ')'); if (p2 != NULL) *p2 = '\0'; } (void) sprintf(linebuf, "Subject: %s", hp->title); tfappend(linebuf); if (!hflag && hp->summary[0]) (void) sprintf(linebuf, "Summary: %s", hp->summary), tfappend(linebuf); if (!hflag && hp->keywords[0]) (void) sprintf(linebuf, "Keywords: %s", hp->keywords), tfappend(linebuf); if (verbose) { (void) sprintf(linebuf, "From: %s", hp->from); tfappend(linebuf); (void) sprintf(linebuf, "Path: %s", hp->path); tfappend(linebuf); if (hp->organization[0]) { (void) sprintf(linebuf, "Organization: %s", hp->organization); tfappend(linebuf); } } else { if (p1 != NULL) *--p1 = '\0'; /* bump over the '(' */ #ifdef INTERNET /* * Prefer Path line if it's in internet format, or if we don't * understand internet format here, or if there is no reply-to. */ (void) sprintf(linebuf, "From: %s", hp->from); #else (void) sprintf(linebuf, "Path: %s", tailpath(hp)); #endif if (fname[0] || hp->organization[0]) { (void) strcat(linebuf, " ("); if (fname[0] == '\0') { (void) strcpy(fname,hp->from); p2 = index(fname,'@'); if (p2) *p2 = '\0'; } (void) strcat(linebuf, fname); if (hp->organization[0] && !hflag) { (void) strcat(linebuf, " @ "); (void) strcat(linebuf, hp->organization); } (void) strcat(linebuf, ")"); } tfappend(linebuf); if (p1 != NULL) *p1 = ' '; if (hp->ctlmsg[0]) { (void) sprintf(linebuf, "Control: %s", hp->ctlmsg); tfappend(linebuf); } } if (verbose) { (void) sprintf(linebuf, "Newsgroups: %s", hp->nbuf); tfappend(linebuf); (void) sprintf(linebuf, "Date: %s", hp->subdate); tfappend(linebuf); if (hp->sender[0]) { (void) sprintf(linebuf, "Sender: %s", hp->sender); tfappend(linebuf); } if (hp->replyto[0]) { (void) sprintf(linebuf, "Reply-To: %s", hp->replyto); tfappend(linebuf); } if (hp->followto[0]) { (void) sprintf(linebuf, "Followup-To: %s", hp->followto); tfappend(linebuf); } } else if (strcmp(hp->nbuf, groupdir) != 0) { (void) sprintf(linebuf, "Newsgroups: %s", hp->nbuf); tfappend(linebuf); timer(); } } #ifdef MYDB char * findparent(id, num) char *id; long *num; { struct artrec a; char idbuf[BUFSIZE]; char *ngname(); strcpy(idbuf, id); lcase(idbuf); if (lookart(id, &a) == DNULL) return NULL; if (a.parent == DNULL) return NULL; readrec(a.parent, &a); *num = a.groups[0].artno; return ngname(a.groups[0].newsgroup); } #endif /* * Append file to temp file, handling control characters, folding lines, etc. * We don't grow the temp file to more than nlines so that a user won't have * to wait for 20 seconds to read in a monster file from net.sources. * What we really want is coroutines--any year now. */ #define ULINE 0200 static char *maxcol; appfile(iop, nlines) register FILE *iop; { register int c; register char *icol; /* &linebuf[0] <= icol <= maxcol */ if (artread || artlines >= nlines || iop == NULL) return; maxcol = linebuf; icol = linebuf; while ((c = getc(iop)) != EOF) { switch (c) { case ' ': if (icol == maxcol && icol < linebuf + LBUFLEN - 1) { *icol++ = ' '; maxcol = icol; } else { if (*icol == '_') *icol++ = ULINE | ' '; else icol++; } break; case '\t': icol = (icol - linebuf &~ 07) + 8 + linebuf; growline(icol); break; case '\b': if (icol > linebuf) --icol; break; case '\n': outline(); if (artlines >= nlines) return; icol = linebuf; break; case '\r': icol = linebuf; break; case '\f': outline(); outline(); outline(); if (artlines >= nlines) return; icol = linebuf; break; default: if (c < ' ' || c > '~') break; else if (icol >= linebuf + LBUFLEN - 1) icol++; else if (icol == maxcol) { *icol++ = c; maxcol = icol; } else if (c == '_') *icol++ |= ULINE; else if (*icol == '_') *icol++ = (c | ULINE); else *icol++ = c; break; } } if (maxcol != linebuf) /* file not terminated with newline */ outline(); artread++; } growline(col) char *col; { while (maxcol < col && maxcol < linebuf + LBUFLEN - 1) *maxcol++ = ' '; } outline() { *maxcol = '\0'; if (strncmp(linebuf, ">From ", 6) == 0) { register char *p; for (p = linebuf ; (*p = p[1]) != '\0' ; p++); } tfappend(linebuf); if (maxcol > linebuf) artlines = lastlin; maxcol = linebuf; } prget(prompter, buf) char *prompter, *buf; { char *p, *q, *r; int c, lastc; curflag = CURP2; r = buf; lastc = '\0'; for (;;) { *r = '\0'; p = secpr; for (q = prompter ; *q ; q++) *p++ = *q; for (q = buf ; *q ; q++) { if (p < &secpr[SECPRLEN-1] && *q >= ' ' && *p <= '~') *p++ = *q; } *p = '\0'; c = vgetc(); if (c == '\n' || c == cintr) { break; } if (c == cerase || c == '\b' || c == '\177') { if (lastc == '\\') r[-1] = c; else if (r > buf) r--; } else if (c == ckill) { if (lastc == '\\') r[-1] = c; else r = buf; #ifdef TIOCGLTC } else if (c == cwerase) { if (lastc == '\\') r[-1] = c; else { while (r > buf && (r[-1] == ' ' || r[-1] == '\t')) r--; while (r > buf && r[-1] != ' ' && r[-1] != '\t') r--; } #endif } else { *r++ = c; } lastc = c; } curflag = CURHOME; secpr[0] = '\0'; return (c == cintr); } /* * Execute a shell command. */ shcmd(cmd, flags) char *cmd; { char *arg[4]; arg[0] = SHELL, arg[1] = "-c", arg[2] = cmd, arg[3] = NULL; return prun(arg, flags); } prun(args, flags) char **args; { int pid; int i; int (*savequit)(); char *env[100], **envp; char a[BUFLEN + 2]; extern char **environ; int pstatus, retval; if (!(flags & BKGRND)) { botscreen(); ttycooked(); #ifdef SIGTSTP (void) signal(SIGTSTP, SIG_DFL); (void) signal(SIGTTIN, SIG_DFL); (void) signal(SIGTTOU, SIG_DFL); #endif } while ((pid = fork()) == -1) sleep(1); /* must not clear alarm */ if (pid == 0) { for (i = 3 ; i < 20 ; i++) close(i); if (flags & BKGRND) { (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); #ifdef SIGTSTP (void) signal(SIGTSTP, SIG_IGN); (void) signal(SIGTTIN, SIG_IGN); (void) signal(SIGTTOU, SIG_IGN); #endif (void) close(0); (void) close(1); (void) open("/dev/null", 2); (void) dup(0); } /* set $A */ (void) sprintf(a, "A=%s", filename); env[0] = a; for (envp = env + 1 ; *environ != NULL && envp < env + 98 ; environ++) if ((*environ)[0] != 'A' || (*environ)[1] != '=') *envp++ = *environ; *envp = NULL; (void) umask(savmask); execve(args[0], args, env); fprintf(stderr, "%s: not found\n", args[0]); exit(20); } if (!(flags & BKGRND)) { savequit = signal(SIGQUIT, SIG_IGN); while ((i = wait(&pstatus)) != pid && (i != -1 || errno == EINTR)) ; if (i == -1) retval = 1; else retval = pstatus; if (flags & CWAIT) { fprintf(stderr, "[Hit return to continue]"); while ((errno = 0, i = getchar()) != '\n' && (i != EOF || errno == EINTR)); } (void) signal(SIGQUIT, savequit); ttyraw(); clearok(curscr, 1); #ifdef SIGTSTP (void) signal(SIGTSTP, onstop); (void) signal(SIGTTIN, onstop); (void) signal(SIGTTOU, onstop); #endif return retval; } else return 0; } #ifdef DIGPAGE /* * Find end of current subarticle in digest. */ findend(l) { register int i, n; register char *p; for (i = l ; i < l + ARTWLEN && i < lastlin ; i++) { tfget(linebuf, i); for (p = linebuf ; *p == '-' ; p++) ; n = (int)p - (int)linebuf; if ( (n > 23 && n < 33) || (n > 65 && n < 79)) { tfget(linebuf, ++i); if (linebuf[0] == '\0') return i + 1; } } return 0; } #endif /*** Routines for handling temporary file ***/ /* * Append to temp file. * Long lines are folded. */ tfappend(tline) register char *tline; { register char *nxtlin; do { nxtlin = index(tline, '\n'); if (nxtlin) *nxtlin++ = '\0'; while (strlen(tline) > COLS) { tfput(tline, lastlin++); tline += COLS; maxlinno++; } tfput(tline, lastlin++); } while ((tline = nxtlin) != NULL); } tfput(tline, linno) char *tline; { register char *p; register FILE *rtfp; /* try to make it a little faster */ register int i; p = tline, i = even(COLS); tfseek(linno, 1); rtfp = tfp; while (--i >= 0) { if (*p) putc(*p++, rtfp); else putc('\0', rtfp); } tflinno++; } tfget(tline, linno) char *tline; { tfseek(linno, 0); fread(tline, even(COLS), 1, tfp); tline[COLS] = '\0'; tflinno++; } tfseek(linno, wrflag) { static int lastwrflag = 1; if (linno != tflinno || wrflag != lastwrflag) { (void) fseek(tfp, (long)linno * even(COLS), 0); tflinno = linno; lastwrflag = wrflag; } } /* VARARGS1 */ msg(s, a1, a2, a3, a4) char *s; { (void) sprintf(secpr, s, a1, a2, a3, a4); } /* * Update the display. * The display is entirely controlled by this routine, * which means that this routine may get pretty snarled. */ static int savelinno = -1; /* dlinno on last call to updscr */ static int savepr; /* prflags on last call */ #ifdef TIOCGWINSZ static int UPDATING = 0, WINCH = 0; /* * called by winch() from virtterm.c -- resets state information back * to start-up state and forces a full redraw of the screen. The * current article is rewound to the beginning because it's would * be very difficult to get the screen to return to the exact point * in the file that the user left off (I know, I tried). */ winch_upd() { if(UPDATING) /* concurrency. wow! */ WINCH++; else if((WINCH == 0) && (savelinno >= 0)) { int saveline = dlinno, saveflag = curflag; /* reread the article */ FCLOSE(fp); obit = -1; getnextart(FALSE); appfile(fp, dlinno + ARTWLEN + 1); /* fix up the screen */ curflag = saveflag; strcpy(prompt,"more? "); clearok(curscr, 1); updscr(); } } #endif /* TIOCGWINSZ */ updscr() { int count; int i; #ifdef TIOCGWINSZ UPDATING++; #endif /* TIOCGWINSZ */ if (checkin()) return; if ((prflags & HELPMSG) == 0 && (dlinno != savelinno || savepr != prflags) && quitflg == 0) { if (dlinno != savelinno) prflags &=~ NOPRT; count = ARTWLEN; if (prflags & NOPRT) count = 0; if ((prflags & HDRONLY) && count > hdrend) count = hdrend - dlinno; #ifdef DIGPAGE if (endsuba > 0 && count > endsuba - dlinno) count = endsuba - dlinno; #endif if ((prflags & NEWART) == 0) ushift(ARTWIN, ARTWIN+ARTWLEN-1, dlinno - savelinno); if (count > lastlin - dlinno) count = lastlin - dlinno; for (i = ARTWIN ; i < ARTWIN + ARTWLEN ; i++) clrline(i); for (i = 0 ; i < count ; i++) { tfget(linebuf, dlinno + i); mvaddstr(ARTWIN + i, 0, linebuf); } prflags &=~ NEWART; savepr = prflags; savelinno = dlinno; } clrline(SPLINE), clrline(PRLINE); #ifdef STATTOP mvaddstr(PRLINE, 0, prompt); #else if (strlen(secpr) <= COLS) mvaddstr(PRLINE, 0, prompt); #endif mvaddstr(PRLINE, 59, timestr); mvaddstr(PRLINE, 15, groupdir); addch(' '); addnum(bit); addch('/'); addnum(pngsize); addch(' '); if (ismail) mvaddstr(PRLINE, 75, ismail > 1? "MAIL" : "mail"); mvaddstr(SPLINE, 0, secpr); if (curflag == CURP1) move(PRLINE, strlen(prompt)); else if (curflag == CURHOME) move(0, 0); refresh(); #ifdef TIOCGWINSZ UPDATING=0; if (WINCH) { /* window changed while updating screen */ WINCH = 0; winch_upd(); } #endif /* TIOCGWINSZ */ } addnum(n) register long n; { if (n >= 10) addnum(n / 10); addch((char)(n % 10 + '0')); } /* * Called on alarm signal. * Simply sets flag, signal processed later. */ onalarm() { #ifdef SIGTSTP int dojump = reading; reading = FALSE; alflag++; if (dojump) longjmp(alrmjmp, 1); #else /* !SIGTSTP */ alflag++; #endif } /* * Process alarm signal (or start clock) */ timer() { time_t tod; int hour; int i; struct tm *t; struct stat statb; struct tm *localtime(); static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; static long oldmsize = 1000000L; static int rccount = 10; static time_t lastismail = 0; alflag = 0; (void) signal(SIGALRM, onalarm); (void) time(&tod); t = localtime(&tod); i = 60 - t->tm_sec; (void) alarm(i > 30? 30 : i); /* reset alarm */ hour = t->tm_hour % 12; if (hour == 0) hour = 12; (void) sprintf(timestr, "%.3s %d %d:%02d", months + 3 * t->tm_mon, t->tm_mday, hour, t->tm_min); if (mailf == NULL || stat(mailf, &statb) < 0) { statb.st_size = 0; } if (statb.st_size > oldmsize) { ismail = 2; beep(); } else { if (statb.st_size == 0) ismail = 0; /* force MAIL for at least 30 seconds */ else if (ismail > 1 && (lastismail+30) < tod) ismail = 1; } oldmsize = statb.st_size; lastismail = tod; if (uflag && !xflag && --rccount < 0) { writeoutrc(); if (secpr[0] == '\0') (void) strcpy(secpr, ".newsrc updated"); rccount = 10; } } char * getmailname() { static char mailname[32]; register char *p; if( (p = getenv("MAIL")) != NULL) return p; #ifndef MMDF if (username[0] == '\0' || strlen(username) > 15) return NULL; #ifdef USG (void) sprintf(mailname, "/usr/mail/%s", username); #else /* !USG */ (void) sprintf(mailname, "/usr/spool/mail/%s", username); #endif /* !USG */ #else /* MMDF */ (void) sprintf(mailname, "%s/mailbox", userhome); #endif /* MMDF */ return mailname; } /*** Terminal I/O ***/ #define INBUFSIZ 8 char inbuf[INBUFSIZ]; /* input buffer */ char outbuf[BUFSIZ]; /* output buffer */ int innleft = 0; /* # of chars in input buffer */ int outnleft = BUFSIZ; /* room left in output buffer */ char *innext; /* next input character */ char *outnext = outbuf; /* next space in output buffer */ #ifdef USG int oflags; /* fcntl flags (for nodelay read) */ #endif /* * Input a character */ vgetc() { register c; #if defined(BSD4_2) || defined(BSD4_1C) int readfds, exceptfds; #endif recurse: if (--innleft >= 0) { c = *innext++; } else { if (alflag) timer(); updscr(); /* update the display */ for (;;) { if (innleft > 0 || alflag) goto recurse; intflag = 0; #ifdef USG if (oflags & O_NDELAY) { oflags &=~ O_NDELAY; fcntl(0, F_SETFL, oflags); } #endif #ifdef SIGTSTP if (setjmp(alrmjmp)) continue; if (setjmp(intjmp)) return cintr; reading = TRUE; #endif /* SIGTSTP */ #if defined(BSD4_2) || defined(BSD4_1C) /* Use a select because it can be interrupted. */ readfds = 1; exceptfds = 1; select(1, &readfds, (int *)0, &exceptfds, (int *)0); if (!(readfds & 1)) break; #endif innleft = read(0, inbuf, INBUFSIZ); #ifdef SIGTSTP reading = FALSE; #endif /* SIGTSTP */ if (innleft > 0) break; if (innleft == 0) { quitflg++; return cintr; } if (errno != EINTR) abort(); /* "Can't happen" */ if (intflag) { intflag--; return cintr; } } innext = inbuf + 1; innleft--; c = inbuf[0]; } #ifndef USG #ifndef CBREAK c &= 0177; if (c == '\034') /* FS character */ xxit(0); #endif #endif if (c == '\f') { clearok(curscr, 1); goto recurse; } if (c == '\r') c = '\n'; return c; } /* * Push a character back onto the input stream. */ pushback(c) { if (innext <= inbuf) abort(); *--innext = c; innleft++; } /* * Check for terminal input */ checkin() { #ifdef FIONREAD int count; #endif #ifdef STATTOP if (innleft > 0) #else if (innleft > 0 || alflag) #endif return 1; #if defined(USG) || defined(FIONREAD) if (ospeed >= B9600) return 0; vflush(); if (ospeed <= B300) ttyowait(); #ifdef USG if ((oflags & O_NDELAY) == 0) { oflags |= O_NDELAY; (void) fcntl(0, F_SETFL, oflags); } if ((innleft = read(0, inbuf, INBUFSIZ)) > 0) { innext = inbuf; return 1; } #endif #ifdef FIONREAD count = 0; /* in case FIONREAD fails */ (void) ioctl(0, FIONREAD, (char *)&count); if (count) return 1; #endif #endif return 0; } /* * flush terminal input queue. */ clearin() { #ifdef USG (void) ioctl(0, TCFLSH, (char *)0); #else #ifdef TIOCFLUSH (void) ioctl(0, TIOCFLUSH, (char *)0); #else struct sgttyb tty; (void) ioctl(0, TIOCGETP, &tty); (void) ioctl(0, TIOCSETP, &tty); #endif #endif innleft = 0; } vputc(c) { if (--outnleft < 0) { vflush(); outnleft--; } *outnext++ = c; } /* * Flush the output buffer */ vflush() { register char *p; register int i; #ifdef BSD4_2 int mask; #else unsigned oalarm; #endif #ifdef BSD4_2 mask = sigblock(1 << (SIGALRM-1)); #else oalarm = alarm(0); #endif for (p = outbuf ; p < outnext ; p += i) { if ((i = write(1, p, outnext - p)) < 0) { if (errno != EINTR) abort(); /* "Can't happen" */ i = 0; } } outnleft = BUFSIZ; outnext = outbuf; #ifdef BSD4_2 sigsetmask(mask); #else (void) alarm(oalarm); #endif } /*** terminal modes ***/ #ifdef USG static struct termio oldtty, newtty; /* * Save tty modes */ ttysave() { if (ioctl(1, TCGETA, &oldtty) < 0) xerror("Can't get tty modes"); newtty = oldtty; newtty.c_iflag &=~ (INLCR|IGNCR|ICRNL); newtty.c_oflag &=~ (OPOST); newtty.c_lflag &=~ (ICANON|ECHO|ECHOE|ECHOK|ECHONL); newtty.c_lflag |= (NOFLSH); newtty.c_cc[VMIN] = 1; newtty.c_cc[VTIME] = 0; cerase = oldtty.c_cc[VERASE]; ckill = oldtty.c_cc[VKILL]; cintr = oldtty.c_cc[VINTR]; ospeed = oldtty.c_cflag & CBAUD; initterm(); } /* * Set tty modes for visual processing */ ttyraw() { while (ioctl(1, TCSETAF, &newtty) < 0 && errno == EINTR) ; rawterm(); } ttyowait() { /* wait for output queue to drain */ while (ioctl(1, TCSETAW, &newtty) < 0 && errno == EINTR) ; } /* * Restore tty modes */ ttycooked() { cookedterm(); vflush(); while (ioctl(1, TCSETAF, &oldtty) < 0 && errno == EINTR) ; oflags &=~ O_NDELAY; (void) fcntl(0, F_SETFL, oflags) ; } #else static struct sgttyb oldtty, newtty; #ifdef TIOCGLTC static struct ltchars oldltchars, newltchars; #endif /* * Save tty modes */ ttysave() { #ifdef CBREAK struct tchars tchars; /* special characters, including interrupt */ #endif #ifdef SIGTSTP int getpgrp(); #if defined(BSD4_2) || defined(BSD4_1C) int tpgrp; #else /* BSD4_1 */ short tpgrp; #endif /* BSD4_1 */ retry: #ifdef BSD4_2 (void) sigblock(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU)); #else /* !BSD4_2 */ (void) signal(SIGTSTP, SIG_HOLD); (void) signal(SIGTTIN, SIG_HOLD); (void) signal(SIGTTOU, SIG_HOLD); #endif /* !BSD4_2 */ if (ioctl(2, TIOCGPGRP, (char *)&tpgrp) < 0) goto nottty; if (tpgrp != getpgrp(0)) { /* not in foreground */ (void) signal(SIGTTOU, SIG_DFL); #ifdef BSD4_2 (void) sigsetmask(sigblock(0) & ~sigmask(SIGTTOU)); #endif /* BSD4_2 */ (void) kill(0, SIGTTOU); /* job stops here waiting for SIGCONT */ goto retry; } (void) signal(SIGTTIN, onstop); (void) signal(SIGTTOU, onstop); (void) signal(SIGTSTP, onstop); #ifdef BSD4_2 (void) sigsetmask(sigblock(0) & ~(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU))); #endif /* BSD4_2 */ #endif /* SIGTSTP */ if (ioctl(1, TIOCGETP, (char *)&oldtty) < 0) nottty: xerror("Can't get tty modes"); newtty = oldtty; newtty.sg_flags &=~ (CRMOD|ECHO|XTABS); #ifdef CBREAK newtty.sg_flags |= CBREAK; ioctl(1, TIOCGETC, (char *)&tchars); cintr = tchars.t_intrc; #else /* !CBREAK */ newtty.sg_flags |= RAW; cintr = '\0177'; /* forcibly this on V6 systems */ #endif /* !CBREAK */ cerase = oldtty.sg_erase; ckill = oldtty.sg_kill; ospeed = oldtty.sg_ospeed; #ifdef TIOCGLTC if (ioctl(1, TIOCGLTC, (char *)&oldltchars) >= 0) { newltchars = oldltchars; newltchars.t_dsuspc = -1; cwerase = oldltchars.t_werasc; } #endif initterm(); } /* * Set tty modes for visual processing */ ttyraw() { while (ioctl(1, TIOCSETN, (char *)&newtty) < 0 && errno == EINTR) ; #ifdef TIOCGLTC if (newltchars.t_dsuspc == '\377') while (ioctl(1, TIOCSLTC, (char *)&newltchars) < 0 && errno == EINTR) ; #endif rawterm(); } ttyowait() { /* wait for output queue to drain */ #ifdef TIOCDRAIN /* This ioctl is a local mod on linus */ (void) ioctl(1, TIOCDRAIN, (char *)0); #endif } /* * Restore tty modes */ ttycooked() { cookedterm(); vflush(); while (ioctl(1, TIOCSETN, (char *)&oldtty) < 0 && errno == EINTR) ; #ifdef TIOCGLTC if (newltchars.t_dsuspc == '\377') while (ioctl(1, TIOCSLTC, (char *)&oldltchars) < 0 && errno == EINTR) ; #endif } #endif /*** signal handlers ***/ onint() { #ifdef SIGTSTP int dojump = reading; reading = FALSE; #endif /* SIGTSTP */ if (!news) { ttycooked(); xxit(1); } (void) signal(SIGINT, onint); clearin(); /* flush input queue */ #ifdef SIGTSTP if (dojump) longjmp(intjmp, 1); #endif /* SIGTSTP */ intflag++; } #ifdef SIGTSTP onstop(signo) int signo; { /* restore old terminal state */ botscreen(); vflush(); ttycooked(); (void) signal(signo, SIG_DFL); #ifdef BSD4_2 (void) sigblock(sigmask(SIGALRM)|sigmask(SIGINT)); (void) sigsetmask(sigblock(0) & ~sigmask(signo)); #else /* BSD4_1 */ (void) alarm(0); #endif /* BSD4_1 */ (void) kill(0, signo); /* stop here until continued */ (void) signal(signo, onstop); /* restore our special terminal state */ ttyraw(); #ifdef TIOCGWINSZ winch(); /* get current window size and redraw screen */ #endif /* TIOCGWINSZ */ clearok(curscr, 1); updscr(); #ifdef BSD4_2 (void) sigsetmask(sigblock(0) & ~(sigmask(SIGALRM)|sigmask(SIGINT))); #else /* BSD4_1 */ timer(); #endif /* BSD4_1 */ } #endif /*** stolen from rfuncs2.c and modified ***/ vsave(to, flags) register char *to; { register FILE *ufp; int isprogram = 0; int isnew = 1; long saveoff; char temp[20]; char *fname; char prog[BUFLEN + 24]; saveoff = ftell(fp); (void) fseek(fp, artbody, 0); fname = to; if (*to == PIPECHAR) { if (strlen(to) > BUFLEN) { msg("Command name too long"); goto out; } flags |= OVWRITE; (void) strcpy(temp, "/tmp/vnXXXXXX"); (void) mktemp(temp); fname = temp; _amove(ROWS - 1, 0); vflush(); } if ((flags & OVWRITE) == 0) { ufp = fopen(fname, "r"); if (ufp != NULL) { (void) fclose(ufp); isnew = 0; } } (void) umask(savmask); if (*to == PIPECHAR) isprogram++; if ((ufp = fopen(fname, (flags & OVWRITE) == 0? "a" : "w")) == NULL) { msg("Cannot open %s", fname); goto out; } /* * V7MAIL code is here to conform to V7 mail format. * If you need a different format to be able to * use your local mail command (such as four ^A's * on the end of articles) substitute it here. */ if (flags & SVHEAD) { #ifdef MMDF if (!isprogram) fprintf(ufp, "\001\001\001\001\n"); #endif /* MMDF */ #ifdef V7MAIL h->subtime = cgtdate(h->subdate); fprintf(ufp, "From %s %s", #ifdef INTERNET h->from, #else h->path, #endif ctime(&h->subtime)); #endif hprint(h, ufp, 2); #ifdef V7MAIL tprint(fp, ufp, TRUE); putc('\n', ufp); /* force blank line at end (ugh) */ #else tprint(fp, ufp, FALSE); #endif } else { tprint(fp, ufp, FALSE); } fclose(ufp); if (isprogram) { (void) sprintf(prog, "(%s)<%s", to + 1, fname); shcmd(prog, CWAIT); prflags |= NOPRT; } else { if ((flags & OVWRITE) == 0) msg("file: %s %s", to, isnew ? "created" : "appended"); else msg("file: %s written", to); } out: if (isprogram) { (void) unlink(fname); } (void) umask(N_UMASK); (void) fseek(fp, saveoff, 0); } xxit(status) int status; { (void) unlink(infile); (void) unlink(outfile); #ifdef SORTACTIVE if (strncmp(ACTIVE,"/tmp/", 5) == 0) (void) unlink(ACTIVE); #endif /* SORTACTIVE */ if (ospeed) { /* is == 0, we haven't been in raw mode yet */ botscreen(); vflush(); ttycooked(); } exit(status); }