Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!uwm.edu!lll-winken!brutus.cs.uiuc.edu!samsung!munnari.oz.au!basser!ultima!nick From: nick@ultima.cs.uts.oz (Nick Andrew) Newsgroups: comp.os.minix Subject: News for Minix (part 1 of 12) Keywords: news Message-ID: <16742@ultima.cs.uts.oz> Date: 7 Dec 89 11:47:25 GMT Organization: Comp Sci, NSWIT, Australia Lines: 2785 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'README' <<'END_OF_FILE' XWARNING: Posting not for the faint of heart. X X This is a very messy implementation of News 2.11 for MINIX. I will Xundoubtedly get flamed for posting such mess; such is life. I've been Xusing this software successfully with a news feed from ACSnet for over 6 Xmonths now, and it is high time Minix had some N_E_W_S software. X XSome installation notes (read & follow before typing make!) ... X X- I have been primarily using News to read, not post. The links into XACSnet were set up in an unusual way, and since I don't run UUPC on my Minix, XI don't know what needs to be changed for UUCP. X X- You will have to make some directories: /usr/spool, /usr/spool/batch, X/usr/spool/news, /usr/lib/news, /usr/local/bin. Make sure /usr/spool is on Xa filesystem with plenty of space free (depending on how many newsgroups Xyou want to get). X X- Several programs use the "r+", "w+" type modes in fopen(). You'll Xneed to have one of the recently posted Stdio packages for things to work. XI just coded a quick one, and it is known in the Makefile as libcio.a (check Xfor the -lcio parameter). X X- Some programs won't compile, for some reason or other. caesar.c for Xexample. I guess you can safely ignore those; I did! The ones to worry Xabout are inews, rnews, postnews, expire, and unbatch. X X- getdate.y required yacc. I used a real unix yacc to make getdate.c Xbut have only included getdate.s (shar'ed as getdate.s.uu). X X- The original makefile did a lot of customisation work (running Xcustomize shell scripts before recompiling etc..). These have all been Xremoved as they stood in the way of the port. What you see is what you Xget. X X- I haven't tried vnews at all; preferring to use vn. X X- You will have to set up various config files; active, newsgroups, Xsys, distributions, etc... The install manual should tell a lot about this. X X- unbatch has been heavily modified to do its work on a compressed Xnews batch by decompressing to a temporary file, and then running inews on Xthat file. Minix's memory limitations don't allow any other method. X X- This posting comes as 12 source postings (src directory), followed Xin a day or two by postings of the (essentially unmodified) manuals and Xuser guides etc. X X X X XMy aim in making this posting is to allow the dedicated Minix hackers to Xcreate a real News for Minix. I have not the time nor the resources to Xspend further on News, and I feel that since the software is running Xfairly stable on my own system, it would benefit the rest of the net, Xdespite the obvious work people will have to go through to get it running Xlocally. X XFinally, good luck, and happy newsreading. Nick. X X "Zeta Microcomputer Software" XACSnet: nick@ultima.cs.uts.oz XUUCP: ...!uunet!munnari!ultima.cs.uts.oz!nick XFidonet: Nick Andrew on 3:713/602 (Zeta) END_OF_FILE if test 2761 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'Notes' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Notes'\" else echo shar: Extracting \"'Notes'\" \(519 characters\) sed "s/^X//" >'Notes' <<'END_OF_FILE' XNick@ultima.cs.uts.oz's notes about News ... X Xinews -C fails (create new newsgroup), because it forks and executes X itself again. The fork fails. Perhaps an execl would have been X a better choice! (does execl get rid of the old process before X creating the new one?) X Xcompress works ... but it is only a 12-bit compress. It seems to be X compatible with the 1.4a compress (the braindead one) and the X previous, compatible compress. X Xbatch & unbatch work ... fine X Xrnews must be in bindir, while inews must be in libdir. END_OF_FILE if test 519 -ne `wc -c <'Notes'`; then echo shar: \"'Notes'\" unpacked with wrong size! fi # end of 'Notes' fi if test -f 'visual.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'visual.c'\" else echo shar: Extracting \"'visual.c'\" \(55369 characters\) sed "s/^X//" >'visual.c' <<'END_OF_FILE' X/* X * visual - visual news interface. X * Kenneth Almquist X */ X X#ifdef SCCSID Xstatic char *SccsId = "@(#)visual.c 1.36 3/21/87"; X#endif /* SCCSID */ X X#include "rparams.h" X#ifdef USG X#include X#include X#include X#else /* !USG */ X#include X#endif /* !USG */ X X#include X#if defined(BSD4_2) || defined(BSD4_1C) X#include X#else X#include "ndir.h" X#endif X#ifdef BSD4_2 X#ifndef sigmask X#define sigmask(m) (1<<((m)-1)) X#endif /* !sigmask */ X#endif /* BSD4_2 */ X#ifdef MYDB X#include "db.h" X#endif /* MYDB */ X Xextern int errno; X X#ifdef SIGTSTP X#include X#endif /* SIGTSTP */ X X#define ARTWLEN (ROWS-2)/* number of lines used to display article */ X#define even(cols) ((cols&1) ? cols + 1 : cols) X#ifdef STATTOP X#define PRLINE 0 /* prompter line */ X#define SPLINE 1 /* secondary prompt line */ X#define ARTWIN 2 /* first line of article window */ X#define SECPRLEN 81 /* length of secondary prompter */ X#else X#define PRLINE (ROWS-1)/* prompter line */ X#define SPLINE (ROWS-2)/* secondary prompt line */ X#define ARTWIN 0 /* first line of article window */ X#define SECPRLEN 100 /* length of secondary prompter */ X#endif X X#define PIPECHAR '|' /* indicate save command should pipe to program */ X#define CAGAIN ('e'&0x1F) /* Save-to-same-place indicator */ X#define META 0200 /* meta character bit (as in emacs) */ X/* print (display) flags */ X#define HDRONLY 0001 /* print header only */ X#define NOPRT 0002 /* don't print at all */ X#define NEWART 0004 /* force article display to be regenerated */ X#define HELPMSG 0010 /* display currently contains help message */ X/* prun flags */ X#define CWAIT 0001 /* type "continue?" and wait for return */ X#define BKGRND 0002 /* run process in the background */ X/* values of curflag */ X#define CURP1 1 /* cursor after prompt */ X#define CURP2 2 /* cursor after secondary prompt */ X#define CURHOME 3 /* cursor at home position */ X/* flags for vsave routine */ X#define SVHEAD 01 /* write out article header */ X#define OVWRITE 02 /* overwrite the file if it already exists */ X/* other files */ X X#define saveart oobit = bit;strcpy(ofilename1, filename);strcpy(ogroupdir, groupdir);hptr = h;h = hold;hold = hptr;ongsize = pngsize X#define NLINES(h, fp) (h->numlines[0] ? h->intnumlines : (h->intnumlines=linecnt(fp),sprintf(h->numlines, "%d", h->intnumlines), h->intnumlines)) X X/* terminal handler stuff */ Xextern int _junked; X#define clearok(xxx, flag) _junked = flag Xextern int COLS; Xextern int ROWS; Xextern int hasscroll; X XFILE *tmpfile(); Xchar *getmailname(); X#ifdef MYDB Xchar *findparent(); X#endif /* MYDB */ Xint onint(); Xint onstop(); Xint xxit(); X Xchar *Progname = "vnews"; /* for xerror */ X X/* variables shared between vnews routines */ Xstatic char linebuf[LBUFLEN]; /* temporary workspace */ Xstatic FILE *tfp; /* temporary file */ Xstatic char tfname[] = "/tmp/vnXXXXXX"; /* name of temp file */ Xstatic long artbody; /* offset of body into article */ Xstatic int quitflg; /* if set, then quit */ Xstatic int erased; /* current article has been erased */ Xstatic int artlines; /* # lines in article body */ Xstatic int artread; /* entire article has been read */ Xstatic int hdrstart; /* beginning of header */ Xstatic int hdrend; /* end of header */ Xstatic int lastlin; /* number of lines in tempfile */ Xstatic int tflinno = 0; /* next line in tempfile */ Xstatic int maxlinno; /* number of lines in file + folded */ Xstatic char secpr[SECPRLEN]; /* secondary prompt */ Xstatic char prompt[30]; /* prompter */ Xstatic short prflags; /* print flags (controls updscr) */ Xstatic short curflag; /* where to locate cursor */ Xstatic int dlinno; /* top line on screen */ Xstatic char timestr[20]; /* current time */ Xstatic int ismail; /* true if user has mail */ Xstatic char *mailf; /* user's mail file */ Xstatic int alflag; /* set if unprocessed alarm signal */ Xstatic int atend; /* set if at end of article */ Xstatic char cerase; /* erase character */ Xstatic char ckill; /* kill character */ Xstatic char cintr; /* interrupt character */ X#ifdef TIOCGLTC Xstatic char cwerase; /* word erase character */ X#endif /* TIOCGLTC */ Xshort ospeed; /* terminal speed NOT STATIC */ Xstatic int intflag; /* set if interrupt received */ X X#ifdef SIGTSTP Xstatic int reading; /* to keep stupid BSD from restarting reads */ Xjmp_buf intjmp, alrmjmp; X#endif /* SIGTSTP */ X X#ifdef MYDB Xstatic int hasdb; /* true if article data base exists */ X#endif /* MYDB */ X X#ifdef DIGPAGE Xstatic int endsuba; /* end of sub-article in digest */ X#endif X X#ifdef MYDEBUG XFILE *debugf; /* file to write debugging info on */ X#endif X Xchar *tft = "/tmp/folXXXXXX"; X X/* X * These were made static for u370 with its buggy cc. X * I judged it better to have one copy with no ifdefs than X * to conditionally compile them as automatic variables X * in readr (which they originally were). Performance X * considerations might warrant moving some of the simple X * things into register variables, but I don't know what X * breaks the u370 cc. X */ Xstatic char goodone[BUFLEN]; /* last decent article */ Xstatic char ogroupdir[BUFLEN]; /* last groupdir */ Xstatic char edcmdbuf[128]; Xstatic int rfq = 0; /* for last article */ Xstatic long ongsize; /* Previous ngsize */ Xstatic long pngsize; /* Printing ngsize */ Xstatic char *bptr; /* temp pointer. */ Xstatic char *tfilename; /* temporary file name */ Xstatic char ofilename1[BUFLEN]; /* previous file name */ Xstatic struct hbuf hbuf1, hbuf2; /* for minusing */ Xstatic struct hbuf *h = &hbuf1, /* current header */ X *hold = &hbuf2, /* previous header */ X *hptr; /* temporary */ Xstatic char *ptr1, *ptr2, *ptr3; /* for reply manipulation */ Xstatic int aabs = FALSE; /* TRUE if we asked absolutely */ Xstatic char *ed, tf[100]; Xstatic long oobit; /* last bit, really */ Xstatic int dgest = 0; Xstatic FILE *fp; /* current article to be printed*/ X Xreadr() X{ X X#ifdef MYDEBUG X debugf = fopen("DEBUG", "w"); X setbuf(debugf, (char *)NULL); X#endif X if (aflag) { X if (*datebuf) { X if ((atime = cgtdate(datebuf)) == -1) X xerror("Cannot parse date string"); X } else X atime = 0; X } X X if (SigTrap) X xxit(1); X (void) mktemp(tfname); X (void) close(creat(tfname,0666)); X if ((tfp = fopen(tfname, "w+")) == NULL) X xerror("Can't create temp file"); X (void) unlink(tfname); X mailf = getmailname(); X#ifdef MYDB X if (opendb() >= 0) { X hasdb = 1; X fputs("Using article data base\n", stderr); /*DEBUG*/ X getng(); X } X#endif X ttysave(); X (void) signal(SIGINT, onint); X (void) signal(SIGQUIT, xxit); X if (SigTrap) X xxit(1); X ttyraw(); X timer(); X X /* loop reading articles. */ X fp = NULL; X obit = -1; X nextng(); X quitflg = 0; X while (quitflg == 0) { X if (getnextart(FALSE)) X break; X (void) strcpy(goodone, filename); X if (SigTrap) X return; X vcmd(); X } X X if (!news) { X ttycooked(); X ospeed = 0; /* to convince xxit() not to clear screen */ X fprintf(stderr, "No news.\n"); X } X} X X/* X * Read and execute a command. X */ Xvcmd() { X register c; X char *p; X long count; X int countset; X X if (prflags & HDRONLY) X appfile(fp, lastlin + 1); X else X appfile(fp, dlinno + ARTWLEN + 1); X X#ifdef DIGPAGE X endsuba = findend(dlinno); X if (artlines > dlinno + ARTWLEN X || endsuba > 0 && endsuba < artlines X#else X if (artlines > dlinno + ARTWLEN X#endif X || (prflags & HDRONLY) && artlines > hdrend) { X atend = 0; X if (prflags&HDRONLY || maxlinno == 0) X (void) strcpy(prompt, "more? "); X else X#ifdef DIGPAGE X (void) sprintf(prompt, "more(%d%%)? ", X ((((endsuba > 0) ? X endsuba : (dlinno + ARTWLEN)) - X hdrend) * 100) / maxlinno); X#else /* !DIGPAGE */ X (void) sprintf(prompt, "more(%d%%)? ", X ((dlinno + ARTWLEN - hdrend) * 100) / maxlinno); X#endif /* !DIGPAGE */ X } else { X atend = 1; X (void) strcpy(prompt, "next? "); X if (!erased) X clear(bit); /* article read */ X } X curflag = CURP1; X p = prompt + strlen(prompt); X countset = 0; X count = 0; X /* X * Loop while accumulating a count, until an action character X * is entered. Also handle "meta" here. X * X * Count is the current count. Countset=0 means no count X * currently exists. Countset=1, count=0 is valid and means X * a count of 0 has been entered X */ X for (;;) { X c = vgetc(); X if (c == cerase || c == '\b' || c == '\177') { X if (countset == 0) X break; /* Use as action char */ X if (count < 10) X countset = 0; /* Erase only char of count */ X else X count /= 10L; /* Erase 1 char of count */ X } else { X#ifdef TIOCGLTC X if (c == ckill || c == cwerase) { X#else X if (c == ckill) { X#endif X if (countset == 0) X break; X countset = 0; X } else if (c < '0' || c > '9') X break; X else { X countset = 1; X count = (count * 10) + (c - '0'); X } X } X if (countset) { X (void) sprintf(p, "%ld", count); X } else { X *p = '\0'; X count = 0; X } X } X X if (c == '\033') { /* escape */ X (void) strcat(prompt, "M-"); X c = vgetc(); X if (c != cintr) X c |= META; X } X secpr[0] = '\0'; X if (countset == 0) X count = 1; X docmd(c, count, countset); X if (c != '?' && c != 'H') /* UGGH */ X prflags &=~ HELPMSG; X if (dlinno > hdrstart) X prflags &=~ HDRONLY; X} X X X/* X * Process one command, which has already been typed in. X */ Xdocmd(c, count, countset) Xint c; Xlong count; Xint countset; X{ X int i; X long nart, Hoffset; X char *findhist(); X X switch (c) { X X /* display list of articles in current group */ X case 'l': X case 'L': X botscreen(); X ttycooked(); X list_group(groupdir, countset ? count : 0, X (c == 'l') ? FALSE : TRUE, pngsize); X ttyraw(); X clearok(curscr, 1); X updscr(); X break; X X /* Show more of current article, or advance to next article */ X case '\n': X case ' ': X#ifdef DIGPAGE X case 'm': X#endif /* DIGPAGE */ X case '\06': /* Control-F for vi compat */ X prflags &=~ NOPRT; X if (atend) X goto next; X else if (prflags & HDRONLY) { X prflags &=~ HDRONLY; X if (hasscroll) X dlinno = hdrstart;} X#ifdef DIGPAGE X else if (endsuba > 0) X dlinno = endsuba; X else if (c == 'm') { X do { X if (lastlin >= maxlinno) X goto next; X else X appfile(fp, lastlin + 1); X } while(strncmp(linebuf, "------------------------", 24) X != 0); X dlinno = endsuba = lastlin; X } X#endif X else if ((appfile(fp, dlinno + 2 * ARTWLEN), artread) X && hasscroll && artlines - dlinno <= ARTWLEN + 2) X dlinno = artlines - ARTWLEN; X else X dlinno += ARTWLEN * count; X break; X X /* No. Go on to next article. */ X case '.': /* useful if you have a keypad */ Xnext: case 'n': X readmode = NEXT; X FCLOSE(fp); X clear(bit); X saveart; X nextbit(); X break; X X X /* Back up count pages */ X case '\b': X case '\177': X if (dlinno == 0) X goto backupone; X /* NO BREAK */ X case META|'v': X case '\002': /* Control-B */ X dlinno -= ARTWLEN * count; X if (dlinno < 0) X dlinno = 0; X break; X X /* forward half a page */ X case '\004': /* Control-D, as in vi */ X if (!atend) X dlinno += ARTWLEN/2 * count; X break; X X /* backward half a page */ X case '\025': /* Control-U */ X dlinno -= ARTWLEN/2 * count; X if (dlinno < 0) X dlinno = 0; X break; X X /* forward count lines */ X case '\016': /* Control-N */ X case '\005': /* Control-E */ X dlinno += count; X break; X X /* backwards count lines */ X case '\020': /* Control-P */ X case '\031': /* Control-Y */ X dlinno -= count; X if (dlinno < 0) X dlinno = 0; X break; X X /* Turn displaying of article back on */ X case 'd': X prflags &=~ NOPRT; X break; X X /* display header */ X case 'h': X dlinno = hdrstart; X prflags |= HDRONLY; X prflags &=~ NOPRT; X break; X X /* X * Unsubscribe to the newsgroup and go on to next group X */ X X case 'U': X case 'u': X strcat(prompt, "u"); X c = vgetc(); X if (c == 'g') { X obit = -1; X FCLOSE(fp); X zapng = TRUE; X saveart; X if (nextng()) { X if (actdirect == BACKWARD) X msg("Can't back up."); X else X quitflg = 1; /* probably unnecessary */ X } X } else { X if (c != cintr && c != ckill) X beep(); X msg("Illegal command"); X } X break; X X /* Print the current version of news */ X case 'v': X msg("News version: %s", news_version); X break; X X X /* Decrypt joke. Always does rot 13 */ X case 'D': X appfile(fp, 32767); X for (i = hdrend ; i < artlines ; i++) { X register char ch, *p; X tfget(linebuf, i); X for (p = linebuf ; (ch = *p) != '\0' ; p++) { X if (ch >= 'a' && ch <= 'z') X *p = (ch - 'a' + 13) % 26 + 'a'; X else if (ch >= 'A' && ch <= 'Z') X *p = (ch - 'A' + 13) % 26 + 'A'; X } X tfput(linebuf, i); X } X prflags |= NEWART; X prflags &=~ (HDRONLY|NOPRT); X break; X X /* write out the article someplace */ X /* w writes out without the header */ X /* | defaults to pipeing */ X { X static char savebuf[BUFLEN]; X int wflags; X X case PIPECHAR: X case 's': X case 'w': X /* We loop back to here each time user types ^U to prompt */ X do { X /* Prompt based on command char */ X msg( (c==PIPECHAR)? "|": "file: "); X curflag = CURP2; X while ((wflags = vgetc()) == ' '); X if (wflags == cintr) { X secpr[0] = '\0'; X break; X } X if (wflags != CAGAIN) { X if ((wflags & 0x1F) == wflags) { /* control char */ X pushback(wflags); X savebuf[0] = 0; X } else { X if (c == PIPECHAR) { X savebuf[0] = PIPECHAR; X savebuf[1] = wflags; X savebuf[2] = 0; X } else { X savebuf[0] = wflags; X savebuf[1] = 0; X } X } X } else { X /* don't let them pipe to a saved filename */ X if (c == PIPECHAR && savebuf[0] != PIPECHAR) { X savebuf[0] = PIPECHAR; X savebuf[1] = 0; X } X } X X wflags = prget( (savebuf[0] == PIPECHAR) ? "" : "file: ", X savebuf); X } while (wflags == 2); X if (wflags) break; /* Interrupted out */ X wflags = 0; X if (c == PIPECHAR) c = 's'; X if (c == 's') X wflags |= SVHEAD; X if (count != 1) X wflags |= OVWRITE; X bptr = savebuf; X while( *bptr == ' ') X bptr++; /* strip leading spaces */ X X if (*bptr != PIPECHAR && *bptr != '/') { X char hetyped[BUFLEN]; X char *boxptr; X (void) strcpy(hetyped, bptr); X if (hetyped[0] == '~' && hetyped[1] == '/') { X strcpy(hetyped, bptr+2); X strcpy(bptr, userhome); X } else if (boxptr = getenv("NEWSBOX")) { X if (index(boxptr, '%')) { X struct stat stbf; X sprintf(bptr, boxptr, groupdir); X if (stat(bptr,&stbf) < 0) { X if (mkdir(bptr, 0777) < 0) { X msg("Cannot create directory %s", bptr); X break; X } X } else if ((stbf.st_mode&S_IFMT) != S_IFDIR) { X msg("%s not a directory", bptr); X break; X } X } else X strcpy(bptr, boxptr); X } else X bptr[0] = '\0'; X X if (bptr[0]) X (void) strcat(bptr, "/"); X if (hetyped[0] != '\0') X (void) strcat(bptr, hetyped); X else X (void) strcat(bptr, "Articles"); X } X X /* handle ~/ for pipes */ X if (*bptr == PIPECHAR) { X char fullname[BUFLEN]; X bptr++; /* skip PIPECHAR */ X while( *bptr == ' ') X bptr++; /* strip leading spaces */ X if (bptr[0] == '~' && bptr[1] == '/') { X strcpy(fullname,userhome); X strcat(fullname,bptr+2); X } else X strcpy(fullname,bptr); X /* we know PIPECHAR is in *savebuf */ X strcpy(savebuf+1,fullname); X bptr = savebuf; X } X X vsave(bptr, wflags); X break; X } X X /* back up */ X case '-': Xcaseminus: X aabs = TRUE; X if (!*ofilename1) { X msg("Can't back up."); X break; X } X FCLOSE(fp); X hptr = h; X h = hold; X hold = hptr; X (void) strcpy(bfr, filename); X (void) strcpy(filename, ofilename1); X (void) strcpy(ofilename1, bfr); X obit = bit; X if (strcmp(groupdir, ogroupdir)) { X (void) strcpy(bfr, groupdir); X selectng(ogroupdir, FALSE, FALSE); X (void) strcpy(groupdir, ogroupdir); X (void) strcpy(ogroupdir, bfr); X ngrp = 1; X back(); X } X bit = oobit; X oobit = obit; X obit = -1; X getnextart(TRUE); X break; X X /* skip forwards */ X case '+': X case '=': Xcaseplus: if (count == 0) X break; X saveart; X last = bit; X for (i = 0; i < count; i++) { X nextbit(); X if ((bit > pngsize) || (rflag && bit < 1)) X break; X } X FCLOSE(fp); X obit = -1; X break; X X /* exit - time updated to that of most recently read article */ X case 'q': X quitflg = 1; X break; X X case 'x': X xxit(0); X break; X X /* cancel the article. */ X case 'c': X strcpy(prompt, "cancel [n]? "); X if (vgetc() != 'y') { X msg("Article not cancelled"); X break; X } X cancel_command(); X break; X X /* escape to shell */ X case '!': { X register char *p; X int flags; X X p = linebuf; X *p = 0; X if (prget("!", p)) X break; X flags = CWAIT; X if (*p == '\0') { X (void) strcpy(linebuf, SHELL); X flags = 0; X } X while (*p) p++; X while (p > linebuf && p[-1] == ' ') X p--; X if (*--p == '&') { X *p = '\0'; X flags = BKGRND; X } else if (*p == PIPECHAR) { X *p = '\0'; X (void) sprintf(bfr, "(%s)%cmail '%s'", linebuf, PIPECHAR, username); X (void) strcpy(linebuf, bfr); X flags |= BKGRND; X } else { X prflags |= NOPRT; X } X shcmd(linebuf, flags); X break; X } X X /* mail reply */ X case 'r': X reply(FALSE); X break; X X case 'R': X reply(TRUE); X break; X X case META|'r': X direct_reply(); X break; X X /* next newsgroup */ X case 'N': X FCLOSE(fp); X if (next_ng_command()) X quitflg = 1; X break; X X /* mark the rest of the articles in this group as read */ X case 'K': X saveart; X while (bit <= ngsize && bit >= minartno) { X clear(bit); X nextbit(); X } X FCLOSE(fp); X break; X X /* Print the full header */ X case 'H': X if (fp == NULL) { X msg("No current article"); X break; X } X move(ARTWIN, 0); X Hoffset = ftell(fp); X (void) fseek(fp, 0L, 0); X for (i = 0; i < ARTWLEN; i++) { X if (fgets(linebuf, COLS, fp) == NULL) X break; X if (linebuf[0] == '\n') X break; X linebuf[COLS] = '\0'; X addstr(linebuf); X } X (void) fseek(fp, Hoffset, 0); X for(; i < ARTWLEN; i++) X addstr(linebuf); X prflags |= HELPMSG|NEWART; X break; X case 'b': /* backup 1 article */ Xbackupone: X count = bit - 1; X /* NO BREAK */ X X case 'A': /* specific number */ X if (count > pngsize) { X msg("not that many articles"); X break; X } X readmode = SPEC; X aabs = TRUE; X bit = count; X obit = -1; X FCLOSE(fp); X break; X X /* display parent article */ X case 'p': X#ifdef MYDB X if (hasdb && (ptr3 = findparent(h->ident, &nart)) != NULL) { X msg("parent: %s/%ld", ptr3, nart); /*DEBUG*/ X updscr(); /*DEBUG*/ X goto selectart; X } X#endif X if (h->followid[0] == '\0') { X msg("no references line"); X break; X } X ptr1 = h->followid + strlen(h->followid); X do { X ptr2 = ptr1; X if (*ptr2 == '\0') X ptr1 = rindex(h->followid, ' '); X else { X *ptr2 = '\0'; X ptr1 = rindex(h->followid, ' '); X *ptr2 = ' '; X } X } while (ptr1 != NULL && --count > 0); X if (ptr1 == NULL) X ptr1 = h->followid; X else ++ptr1; X (void) strncpy(linebuf, ptr1, ptr2 - ptr1); X linebuf[ptr2 - ptr1] = '\0'; X msg("%s", linebuf); X curflag = CURP2; X updscr(); /* may take this out later */ X goto searchid; X /* specific message ID. */ X case '<': X /* could improve this */ X linebuf[0] = '<'; linebuf[1] = 0; X if (prget("", linebuf)) { X secpr[0] = 0; X break; X } Xsearchid: secpr[0] = '\0'; X if (index(linebuf, '@') == NULL && index(linebuf, '>') == NULL) { X ptr1 = linebuf; X if (*ptr1 == '<') X ptr1++; X ptr2 = index(ptr1, '.'); X if (ptr2 != NULL) { X *ptr2++ = '\0'; X (void) sprintf(bfr, "<%s@%s.UUCP>", ptr2, ptr1); X (void) strcpy(linebuf, bfr); X } X } X if (index(linebuf, '>') == NULL) X (void) strcat(linebuf, ">"); X X ptr1 = findhist(linebuf); X if (ptr1 == NULL) { X msg("%s not found", linebuf); X break; X } X ptr2 = index(ptr1, '\t'); X ptr3 = index(++ptr2, '\t'); X ptr2 = index(++ptr3, ' '); X if (ptr2) X *ptr2 = '\0'; X ptr2 = index(ptr3, '/'); X if (!ptr2) { X if (strcmp(ptr3, "cancelled") == 0) X msg("%s has been cancelled", linebuf); X else X msg("%s has expired", linebuf); X break; X } X *ptr2++ = '\0'; X (void) sscanf(ptr2, "%ld", &nart); X X /* X * Go to a given article. Ptr3 specifies the newsgroup X * and nart specifies the article number. X */ X#ifdef MYDB Xselectart: X#endif /* MYDB */ X aabs = TRUE; X FCLOSE(fp); X saveart; X (void) strcpy(ogroupdir, ptr3); X if (strcmp(groupdir, ogroupdir)) { X (void) strcpy(bfr, groupdir); X selectng(ogroupdir, TRUE, PERHAPS); X (void) strcpy(groupdir, ogroupdir); X (void) strcpy(ogroupdir, bfr); X ngrp = 1; X back(); X } X bit = nart; X oobit = obit; X obit = -1; X getnextart(TRUE); X if (bit != nart || strcmp(groupdir, ptr3) != 0) { X msg("can't read %s/%ld", ptr3, nart); X goto caseminus; X } X rfq = 0; X break; X X /* follow-up article */ X case 'f': X if (strcmp(h->followto, "poster") == 0) { X reply(FALSE); X break; X } X (void) sprintf(bfr, "%s/%s %s", BIN, "postnews", goodone); X shcmd(bfr, CWAIT); X break; X X /* erase - pretend we haven't seen this article. */ X case 'e': X erased = 1; X set(bit); X goto caseplus; /* skip this article for now */ X X case '#': X msg("Article %ld of %ld", rfq ? oobit : bit, pngsize); X break; X X /* error */ X case '?': X { X FILE *helpf; X (void) sprintf(linebuf, "%s/vnews.help", LIB); X if ((helpf = fopen(linebuf, "r")) == NULL) { X msg("Can't open help file"); X break; X } X move(ARTWIN, 0); X while (fgets(linebuf, LBUFLEN, helpf) != NULL) X addstr(linebuf); X (void) fclose(helpf); X prflags |= HELPMSG|NEWART; X } X break; X X default: X if (c != ckill && c != cintr && c != cerase) X#ifdef TIOCGLTC X if (c != cwerase) X#endif X { X beep(); X msg("Illegal command"); X } X break; X } X} X Xcancel_command() X{ X register char *poster, *r; X int notauthor; X char *senderof(); X X poster = senderof(&h); X /* only compare up to '.' or ' ' */ X r = index(poster,'.'); X if (r == NULL) X r = index(poster,' '); X if (r != NULL) X *r = '\0'; X tfilename = filename; X notauthor = strcmp(username, poster); X if (uid != ROOTID && uid && notauthor) { X msg("Can't cancel what you didn't write."); X return; X } X if (!cancel(stderr, h, notauthor)) { X clear(bit); X saveart; X nextbit(); X obit = -1; X fp = NULL; X } X FCLOSE(fp); X} X/* X * Generate replies X */ X Xreply(include) X int include; X{ X char *arg[4]; X register FILE *rfp; X char subj[132]; X register char *p; X char *replyname(); X struct stat statb; X time_t creatm; X X /* Put the user in the editor to create the body of the reply. */ X ed = getenv("EDITOR"); X if (ed == NULL || *ed == '\0') X ed = DFTEDITOR; X if (ed == NULL) { X msg("You don't have an editor"); X return; X } X X arg[0] = "/bin/sh"; X arg[1] = "-c"; X X (void) strcpy(tf, tft); X (void) mktemp(tf); X (void) close(creat(tf,0600)); X if ((rfp = fopen(tf, "w")) == NULL) { X msg("Can't create %s", tf) ; X return; X } X (void) strcpy(subj, h->title); X if (!prefix(subj, "Re:")){ X (void) strcpy(bfr, subj); X (void) sprintf(subj, "Re: %s", bfr); X } X X p = replyname(h); X fprintf(rfp, "To: %s\n", p); X fprintf(rfp, "Subject: %s\n", subj); X fprintf(rfp, "In-reply-to: your article %s\n", h->ident); X#ifdef INTERNET X fprintf(rfp, "News-Path: %s\n", h->path); X#endif /* INTERNET */ X (void) sprintf(rcbuf, "%s -t < %s; rm -f %s", MAILPARSER, tf, tf); X putc('\n', rfp); X if (include) { X FILE *of; X char buf[BUFSIZ]; X X of = xart_open(goodone, "r"); X while (fgets(buf, sizeof buf, of) != NULL) X if (buf[0] == '\n') X break; X while (fgets(buf, sizeof buf, of) != NULL) X fprintf(rfp, "> %s", buf); X fclose(of); X putc('\n', rfp); X } X fflush(rfp); X (void) fstat(fileno(rfp), &statb); X creatm = statb.st_mtime; X (void) fclose(rfp); X X (void) sprintf(edcmdbuf, "exec %s %s", ed, tf); X arg[2] = edcmdbuf; X arg[3] = NULL; X if (prun(arg, 0) != 0) { X msg("Couldn't run editor"); X (void) unlink(tf); X return; X } X X if (access(tf, 4) || stat(tf, &statb)) { X msg("No input file - mail not sent"); X (void) unlink(tf); X return; X } X if (statb.st_mtime == creatm || statb.st_size < 5) { X msg("File unchanged - no message posted"); X (void) unlink(tf); X return; X } X X arg[2] = rcbuf; X arg[3] = NULL; X prun(arg, BKGRND); X prflags |= NOPRT; X} X Xdirect_reply() X{ X register char *p; X register char *q; X char *arg[4]; X char address[PATHLEN]; X extern char *replyname(); X extern char *getenv(); X X arg[0] = "/bin/sh"; X arg[1] = "-c"; X p = replyname(h); X q = address; X while (*p != '\0') { X if (index("\"\\$", *p) != 0) X *q++ = '\\'; X *q++ = *p++; X } X *q++ = '\0'; X if ((MAILER = getenv("MAILER")) == NULL) X MAILER = "mail"; X sprintf(rcbuf, MAILER, hptr->title); X sprintf(bfr, "%s %s", rcbuf, address); X arg[2] = bfr; X arg[3] = NULL; X if (prun(arg, 0) != 0) { X msg("Couldn't run mailer"); X return; X } X prflags |= NOPRT; X} X Xnext_ng_command() X{ X set(bit); X obit = -1; X linebuf[0] = 0; X if (prget("group? ", linebuf)) X return FALSE; X bptr = linebuf; X if (!*bptr || *bptr == '-') { X if (*bptr) X actdirect = BACKWARD; X saveart; X if (nextng()) { X if (actdirect == BACKWARD) X msg("Can't back up."); X else X return TRUE; X } X return FALSE; X } X while (isspace(*bptr)) X bptr++; X if (!validng(bptr)) { X msg("No such group."); X return FALSE; X } X saveart; X back(); X selectng(bptr, TRUE, TRUE); X return FALSE; X} X X/* X * Find the next article we want to consider, if we're done with X * the last one, and show the header. X */ Xgetnextart(minus) Xint minus; X{ X int noaccess; X register DIR *dirp; X register struct direct *dir; X long nextnum, tnum; X long atol(); X X noaccess = 0; X if (minus) X goto nextart2; /* Kludge for "-" command. */ X X if (bit == obit) /* Return if still on same article as last time */ X return 0; X Xnextart: X if (news) { X curflag = CURHOME; X _amove(0, 0); X vflush(); X } X dgest = 0; X X /* If done with this newsgroup, find the next one. */ X while (ngsize <= 0 || (!rflag && ((long) bit > ngsize)) || (rflag && bit < minartno)) { X if (nextng()) { X if (actdirect == BACKWARD) { X msg("Can't back up."); X actdirect = FORWARD; X continue; X } X else /* if (rfq++ || pflag || cflag) */ X return 1; X } X if (rflag) X bit = ngsize + 1; X else X bit = -1; X noaccess = 2; X } X X /* speed things up by not searching for article -1 */ X if (bit < 0) { X bit = minartno - 1; X nextbit(); X aabs = FALSE; X goto nextart; X } X Xnextart2: X if (rcreadok) X rcreadok = 2; /* have seen >= 1 article */ X (void) sprintf(filename, "%s/%ld", dirname(groupdir), bit); X if (rfq && goodone[0]) /* ??? */ X strcpy(filename, goodone); X if (SigTrap == SIGHUP) X return 1; X /* Decide if we want to show this article. */ X if ((fp = art_open(filename, "r")) == NULL) { X /* since there can be holes in legal article numbers, */ X /* we wait till we hit 5 consecutive bad articles */ X /* before we haul off and scan the directory */ X if (++noaccess < 5) X goto badart; X noaccess = 0; X dirp = opendir(dirname(groupdir)); X if (dirp == NULL) { X if (errno != EACCES) X msg("Can't open %s", dirname(groupdir)); X goto nextart; X } X nextnum = rflag ? minartno - 1 : ngsize + 1; X while ((dir = readdir(dirp)) != NULL) { X if (!dir->d_ino) X continue; X tnum = atol(dir->d_name); X if (tnum <= 0) X continue; X if (rflag ? (tnum > nextnum && tnum < bit) X : (tnum < nextnum && tnum > bit)) X nextnum = tnum; X } X closedir(dirp); X if (rflag ? (nextnum >= bit) : (nextnum <= bit)) X goto badart; X do { X clear(bit); X nextbit(); X } while (rflag ? (nextnum < bit) : (nextnum > bit)); X obit = -1; X aabs = FALSE; X goto nextart; X } else X noaccess = 0; X X if (hread(h, fp, TRUE) == NULL || (!rfq && !aselect(h, aabs))) { Xbadart: X FCLOSE(fp); X clear(bit); X obit = -1; X nextbit(); X aabs = FALSE; X goto nextart; X } X aabs = FALSE; X actdirect = FORWARD; X news = TRUE; X artbody = ftell(fp); X fmthdr(); X artlines = lastlin; X artread = 0; X prflags |= NEWART; X prflags &=~ NOPRT; X if (! cflag && hdrend < ARTWLEN && !cflag) X prflags |= HDRONLY; X dlinno = 0; X maxlinno = NLINES(h, fp); X erased = 0; X X obit = bit; X return 0; X} X X/* X * Print out whatever the appropriate header is X */ Xfmthdr() { X char *briefdate(); X static FILE *ngfd = NULL; X static int triedopen = 0; X char pbuf[BUFLEN], *printbuffer = groupdir; X X lastlin = 0; X if (ngrp) { X pngsize = ngsize; X ngrp--; X if (!hflag) { X if (!triedopen) { X (void) sprintf(pbuf,"%s/newsgroups", LIB); X ngfd = fopen(pbuf, "r"); X triedopen++; X } X if (ngfd != NULL) { X register char *p; X char ibuf[BUFLEN]; X rewind(ngfd); X while (fgets(ibuf, BUFLEN, ngfd) != NULL) { X p = index(ibuf, '\t'); X if (p) X *p++ = '\0'; X if (strcmp(ibuf, groupdir) == 0) { X register char *q; X q = rindex(p, '\t'); X if (q) { X p = q; X *p++ = '\0'; X } X if (p) { X q = index(p, '\n'); X if (q) X *q = '\0'; X if (*--q == '.') X *q = '\0'; X (void) sprintf(pbuf,"%s (%s)", X groupdir, p); X printbuffer = pbuf; X } X break; X } X } X } X (void) sprintf(linebuf, "Newsgroup %s", printbuffer); X tfappend(linebuf); X } X } X hdrstart = lastlin; X if (!hflag) { X (void) sprintf(linebuf, "Article %s %s", X h->ident, briefdate(h->subdate)); X tfappend(linebuf); X } X xtabs(h); X vhprint(h, pflag ? 1 : 0); X (void) sprintf(linebuf, "(%d lines)", NLINES(h, fp)); tfappend(linebuf); X tfappend(""); X hdrend = lastlin; X} X X/* X * Grow tabs into spaces in header fields, 'cause the rest of this X * lax program drops turds all over tabs (so it does with \b's, but ..) X */ Xxtabs(p) Xregister struct hbuf *p; X{ X xtabf(p->from, sizeof p->from); X xtabf(p->path, sizeof p->path); X xtabf(p->nbuf, sizeof p->nbuf); X xtabf(p->title, sizeof p->title); X xtabf(p->ident, sizeof p->ident); X xtabf(p->replyto, sizeof p->replyto); X xtabf(p->followid, sizeof p->followid); X xtabf(p->subdate, sizeof p->subdate); X xtabf(p->expdate, sizeof p->expdate); X xtabf(p->ctlmsg, sizeof p->ctlmsg); X xtabf(p->sender, sizeof p->sender); X xtabf(p->followto, sizeof p->followto); X xtabf(p->distribution, sizeof p->distribution); X xtabf(p->organization, sizeof p->organization); X xtabf(p->numlines, sizeof p->numlines); X xtabf(p->keywords, sizeof p->keywords); X xtabf(p->summary, sizeof p->summary); X xtabf(p->approved, sizeof p->approved); X xtabf(p->nf_id, sizeof p->nf_id); X xtabf(p->nf_from, sizeof p->nf_from); X#ifdef DOXREFS X xtabf(p->xref, sizeof p->xref); X#endif /* DOXREFS */ X} X Xxtabf(s, size) Xchar *s; Xint size; X{ X register char *p, *str; X register c, i; X char buf[LBUFLEN]; X X str = s; X if (index(str, '\t') == NULL) X return; X i = 0; X for (p = buf; c = *str++; i++) { X if (c == '\t') { X *p++ = ' '; X if ((i & 7) != 7) X str--; X } else if (c == '\n') { X i = -1; X *p++ = c; X } else X *p++ = c; X } X *p = '\0'; X strncpy(s, buf, size - 1); X} X X/* X * Print the file header to the temp file. X */ Xvhprint(hp, verbose) Xregister struct hbuf *hp; Xint verbose; X{ X register char *p1, *p2; X char fname[BUFLEN]; X char *tailpath(); X X fname[0] = '\0'; /* init name holder */ X X p1 = index(hp->from, '('); /* Find the sender's full name. */ X if (p1 == NULL && hp->path[0]) X p1 = index(hp->path, '('); X if (p1 != NULL) { X (void) strcpy(fname, p1+1); X p2 = index(fname, ')'); X if (p2 != NULL) X *p2 = '\0'; X } X X (void) sprintf(linebuf, "Subject: %s", hp->title); X tfappend(linebuf); X if (!hflag && hp->summary[0]) X (void) sprintf(linebuf, "Summary: %s", hp->summary), tfappend(linebuf); X if (!hflag && hp->keywords[0]) X (void) sprintf(linebuf, "Keywords: %s", hp->keywords), tfappend(linebuf); X if (verbose) { X (void) sprintf(linebuf, "From: %s", hp->from); tfappend(linebuf); X (void) sprintf(linebuf, "Path: %s", hp->path); tfappend(linebuf); X if (hp->organization[0]) { X (void) sprintf(linebuf, "Organization: %s", hp->organization); X tfappend(linebuf); X } X } X else { X if (p1 != NULL) X *--p1 = '\0'; /* bump over the '(' */ X#ifdef INTERNET X /* X * Prefer Path line if it's in internet format, or if we don't X * understand internet format here, or if there is no reply-to. X */ X (void) sprintf(linebuf, "From: %s", hp->from); X#else X (void) sprintf(linebuf, "Path: %s", tailpath(hp)); X#endif X if (fname[0] || (hp->organization[0] && !hflag)) { X (void) strcat(linebuf, " ("); X if (fname[0] == '\0') { X (void) strcpy(fname, hp->from); X p2 = index(fname,'@'); X if (p2) X *p2 = '\0'; X } X (void) strcat(linebuf, fname); X if (hp->organization[0] && !hflag) { X (void) strcat(linebuf, " @ "); X (void) strcat(linebuf, hp->organization); X } X (void) strcat(linebuf, ")"); X } X tfappend(linebuf); X if (p1 != NULL) X *p1 = ' '; X if (hp->ctlmsg[0]) { X (void) sprintf(linebuf, "Control: %s", hp->ctlmsg); X tfappend(linebuf); X } X } X X if (verbose) { X (void) sprintf(linebuf, "Newsgroups: %s", hp->nbuf); tfappend(linebuf); X (void) sprintf(linebuf, "Date: %s", hp->subdate); tfappend(linebuf); X if (hp->sender[0]) { X (void) sprintf(linebuf, "Sender: %s", hp->sender); X tfappend(linebuf); X } X if (hp->replyto[0]) { X (void) sprintf(linebuf, "Reply-To: %s", hp->replyto); X tfappend(linebuf); X } X if (hp->followto[0]) { X (void) sprintf(linebuf, "Followup-To: %s", hp->followto); X tfappend(linebuf); X } X } X else if (strcmp(hp->nbuf, groupdir) != 0) { X (void) sprintf(linebuf, "Newsgroups: %s", hp->nbuf); X tfappend(linebuf); X timer(); X } X} X X#ifdef MYDB X Xchar * Xfindparent(id, num) Xchar *id; Xlong *num; X{ X struct artrec a; X char idbuf[BUFSIZE]; X char *ngname(); X X strcpy(idbuf, id); X lcase(idbuf); X X if (lookart(id, &a) == DNULL) X return NULL; X if (a.parent == DNULL) X return NULL; X readrec(a.parent, &a); X *num = a.groups[0].artno; X return ngname(a.groups[0].newsgroup); X} X X#endif X X X/* X * Append file to temp file, handling control characters, folding lines, etc. X * We don't grow the temp file to more than nlines so that a user won't have X * to wait for 20 seconds to read in a monster file from net.sources. X * What we really want is coroutines--any year now. X */ X X#define ULINE 0200 Xstatic char *maxcol; X Xappfile(iop, nlines) Xregister FILE *iop; X{ X register int c; X register char *icol; /* &linebuf[0] <= icol <= maxcol */ X X if (artread || artlines >= nlines || iop == NULL) X return; X maxcol = linebuf; X icol = linebuf; X while ((c = getc(iop)) != EOF) { X switch (c) { X case ' ': X if (icol == maxcol && icol < linebuf + LBUFLEN - 1) { X *icol++ = ' '; X maxcol = icol; X } else { X if (*icol == '_') X *icol++ = ULINE | ' '; X else X icol++; X } X break; X case '\t': X icol = (icol - linebuf &~ 07) + 8 + linebuf; X growline(icol); X break; X case '\b': X if (icol > linebuf) --icol; X break; X case '\n': X outline(); X if (artlines >= nlines) X return; X icol = linebuf; X break; X case '\r': X icol = linebuf; X break; X case '\f': X outline(); outline(); outline(); X if (artlines >= nlines) X return; X icol = linebuf; X break; X default: X if (c < ' ' || c > '~') X break; X else if (icol >= linebuf + LBUFLEN - 1) X icol++; X else if (icol == maxcol) { X *icol++ = c; X maxcol = icol; } X else if (c == '_') X *icol++ |= ULINE; X else if (*icol == '_') X *icol++ = (c | ULINE); X else *icol++ = c; X break; X } X } X if (maxcol != linebuf) /* file not terminated with newline */ X outline(); X artread++; X} X Xgrowline(col) Xchar *col; X{ X while (maxcol < col && maxcol < linebuf + LBUFLEN - 1) X *maxcol++ = ' '; X} X Xoutline() X{ X *maxcol = '\0'; X if (strncmp(linebuf, ">From ", 6) == 0) { X register char *p; X for (p = linebuf ; (*p = p[1]) != '\0' ; p++); X } X tfappend(linebuf); X if (maxcol > linebuf) X artlines = lastlin; X maxcol = linebuf; X} X X X/* X * Prompt the user and get a line. X * "prompter" is the prompt. "buf" contains a string which X * will be used as the initial user response (which may be edited X * by the user with backspace, ^U, etc). The resulting line is X * returned in "buf". The result of prget() is: X * 0 if the line was terminated by NL or CR X * 1 if it was terminated by the interrupt character. X * 2 if it was terminated by erasing all the characters, including X * one or more that were prompted initially in "buf". (If "buf" X * was empty, this will never occur.) X */ Xint Xprget(prompter, buf) Xchar *prompter, *buf; X{ X register char *p, *q, *r; X register char c; X char lastc; X char hadprompt = buf[0]; X X curflag = CURP2; X r = buf + strlen(buf); X lastc = '\0'; X for (;;) { X p = secpr; X for (q = prompter ; *q ; q++) X *p++ = *q; X for (q = buf ; *q ; q++) { X if (p < &secpr[SECPRLEN-1] && *q >= ' ' && *q <= '~') X *p++ = *q; X } X *p = '\0'; X c = vgetc(); X if (c == '\n' || c == '\r' || c == cintr) { X break; X } X if (c == cerase || c == '\b' || c == '\177') { X if (lastc == '\\') X r[-1] = c; X else if (r > buf) X r--; X } else if (c == ckill) { X if (lastc == '\\') X r[-1] = c; X else X r = buf; X#ifdef TIOCGLTC X } else if (c == cwerase) { X if (lastc == '\\') X r[-1] = c; X else { X while (r > buf && (r[-1] == ' ' || r[-1] == '\t')) X r--; X while (r > buf && r[-1] != ' ' && r[-1] != '\t') X r--; X } X#endif X } else { X *r++ = c; X } X lastc = c; X *r = '\0'; X if ((r == buf) && hadprompt) X return 2; X } X curflag = CURHOME; X secpr[0] = '\0'; X return (c == cintr); X} X X X X/* X * Execute a shell command. X */ X Xshcmd(cmd, flags) Xchar *cmd; X{ X char *arg[4]; X X arg[0] = SHELL, arg[1] = "-c", arg[2] = cmd, arg[3] = NULL; X return prun(arg, flags); X} X X Xprun(args, flags) Xchar **args; X{ X int pid; X int i; X int (*savequit)(); X char *env[100], **envp, **oenvp; X char a[BUFLEN + 2]; X extern char **environ; X int pstatus, retval; X X if (!(flags & BKGRND)) { X botscreen(); X ttycooked(); X#ifdef SIGTSTP X (void) signal(SIGTSTP, SIG_DFL); X (void) signal(SIGTTIN, SIG_DFL); X (void) signal(SIGTTOU, SIG_DFL); X#endif X } X#if defined(BSD4_2) && !defined(sun) X while ((pid = vfork()) == -1) X#else /* !BSD4_2 */ X /* 4.1 BSD (at least) can't handle this vfork with -ljobs */ X while ((pid = fork()) == -1) X#endif /* !BSD4_2 */ X sleep(1); /* must not clear alarm */ X if (pid == 0) { X for (i = 3 ; i < 20 ; i++) X close(i); X if (flags & BKGRND) { X (void) signal(SIGINT, SIG_IGN); X (void) signal(SIGQUIT, SIG_IGN); X#ifdef SIGTSTP X (void) signal(SIGTSTP, SIG_IGN); X (void) signal(SIGTTIN, SIG_IGN); X (void) signal(SIGTTOU, SIG_IGN); X#endif X (void) close(0); X (void) close(1); X (void) open("/dev/null", 2); X (void) dup(0); X } X /* set $A */ X (void) sprintf(a, "A=%s", filename); X oenvp = environ; X env[0] = a; X for (envp = env + 1 ; *oenvp != NULL && envp < env + 98 ; oenvp++) X if ((*oenvp)[0] != 'A' || (*oenvp)[1] != '=') X *envp++ = *oenvp; X *envp = NULL; X X (void) umask(savmask); X execve(args[0], args, env); X perror(args[0]); X exit(20); X } X if (!(flags & BKGRND)) { X savequit = signal(SIGQUIT, SIG_IGN); X while ((i = wait(&pstatus)) != pid && (i != -1 || errno == EINTR)) X ; X if (i == -1) X retval = 1; X else X retval = pstatus; X if (flags & CWAIT) { X fprintf(stderr, "[Hit return to continue]"); X while ((errno = 0, i = getchar()) != '\n' X && (i != EOF || errno == EINTR)); X } X (void) signal(SIGQUIT, savequit); X ttyraw(); X clearok(curscr, 1); X#ifdef SIGTSTP X (void) signal(SIGTSTP, onstop); X (void) signal(SIGTTIN, onstop); X (void) signal(SIGTTOU, onstop); X#endif X return retval; X } else X return 0; X} X X#ifdef DIGPAGE X X X/* X * Find end of current subarticle in digest. X */ X Xfindend(l) X{ X register int i, n; X register char *p; X X for (i = l ; i < l + ARTWLEN && i < lastlin ; i++) { X tfget(linebuf, i); X for (p = linebuf ; *p == '-' ; p++) X ; X n = (int)p - (int)linebuf; X if ( (n > 23 && n < 33) || (n > 65 && n < 79)) { X tfget(linebuf, ++i); X if (linebuf[0] == '\0') X return i + 1; X } X } X return 0; X} X X#endif X X X/*** Routines for handling temporary file ***/ X X/* X * Append to temp file. X * Long lines are folded. X */ X Xtfappend(tline) Xregister char *tline; X{ X register char *nxtlin; X X do { X nxtlin = index(tline, '\n'); X if (nxtlin) X *nxtlin++ = '\0'; X X while (strlen(tline) > COLS) { X tfput(tline, lastlin++); X tline += COLS; X maxlinno++; X } X tfput(tline, lastlin++); X } while ((tline = nxtlin) != NULL); X} X X Xtfput(tline, linno) Xchar *tline; X{ X register char *p; X register FILE *rtfp; /* try to make it a little faster */ X register int i; X X p = tline, i = even(COLS); X tfseek(linno, 1); X rtfp = tfp; X while (--i >= 0) { X if (*p) X putc(*p++, rtfp); X else X putc('\0', rtfp); X } X tflinno++; X} X X Xtfget(tline, linno) Xchar *tline; X{ X tfseek(linno, 0); X fread(tline, even(COLS), 1, tfp); X tline[COLS] = '\0'; X tflinno++; X} X X Xtfseek(linno, wrflag) X{ X static int lastwrflag = 1; X X if (linno != tflinno || wrflag != lastwrflag) { X (void) fseek(tfp, (long)linno * even(COLS), 0); X tflinno = linno; X lastwrflag = wrflag; X } X} X X/* VARARGS1 */ Xmsg(s, a1, a2, a3, a4) Xchar *s; Xlong a1, a2, a3, a4; X{ X (void) sprintf(secpr, s, a1, a2, a3, a4); X} X X X/* X * Update the display. X * The display is entirely controlled by this routine, X * which means that this routine may get pretty snarled. X */ X Xstatic int savelinno = -1; /* dlinno on last call to updscr */ Xstatic int savepr; /* prflags on last call */ X#ifdef TIOCGWINSZ Xstatic int UPDATING = 0, WINCH = 0; X X/* X * called by winch() from virtterm.c -- resets state information back X * to start-up state and forces a full redraw of the screen. The X * current article is rewound to the beginning because it's would X * be very difficult to get the screen to return to the exact point X * in the file that the user left off (I know, I tried). X */ Xwinch_upd() X{ X if(UPDATING) /* concurrency. wow! */ X WINCH++; X else if((WINCH == 0) && (savelinno >= 0)) { X int saveflag = curflag; X X /* reread the article */ X FCLOSE(fp); X obit = -1; X getnextart(FALSE); X appfile(fp, dlinno + ARTWLEN + 1); X X /* fix up the screen */ X curflag = saveflag; X strcpy(prompt,"more? "); X clearok(curscr, 1); X updscr(); X } X} X#endif /* TIOCGWINSZ */ X X Xupdscr() X{ X int count; X int i; X X#ifdef TIOCGWINSZ X UPDATING++; X#endif /* TIOCGWINSZ */ X if (checkin()) X return; X if ((prflags & HELPMSG) == 0 X && (dlinno != savelinno || savepr != prflags) X && quitflg == 0) { X if (dlinno != savelinno) X prflags &=~ NOPRT; X count = ARTWLEN; X if (prflags & NOPRT) X count = 0; X if ((prflags & HDRONLY) && count > hdrend) X count = hdrend - dlinno; X#ifdef DIGPAGE X if (endsuba > 0 && count > endsuba - dlinno) X count = endsuba - dlinno; X#endif X if ((prflags & NEWART) == 0) X ushift(ARTWIN, ARTWIN+ARTWLEN-1, dlinno - savelinno); X if (count > lastlin - dlinno) X count = lastlin - dlinno; X for (i = ARTWIN ; i < ARTWIN + ARTWLEN ; i++) X clrline(i); X for (i = 0 ; i < count ; i++) { X tfget(linebuf, dlinno + i); X mvaddstr(ARTWIN + i, 0, linebuf); X } X prflags &=~ NEWART; X savepr = prflags; X savelinno = dlinno; X } X clrline(SPLINE), clrline(PRLINE); X#ifdef STATTOP X mvaddstr(PRLINE, 0, prompt); X#else X if (strlen(secpr) <= COLS) X mvaddstr(PRLINE, 0, prompt); X#endif X mvaddstr(PRLINE, 59, timestr); X mvaddstr(PRLINE, 17, groupdir); X addch(' '); addnum(bit); addch('/'); addnum(pngsize); addch(' '); X if (ismail) X mvaddstr(PRLINE, 75, ismail > 1? "MAIL" : "mail"); X mvaddstr(SPLINE, 0, secpr); X if (curflag == CURP1) X move(PRLINE, strlen(prompt)); X else if (curflag == CURHOME) X move(0, 0); X refresh(); X#ifdef TIOCGWINSZ X UPDATING=0; X if (WINCH) { /* window changed while updating screen */ X WINCH = 0; X winch_upd(); X } X#endif /* TIOCGWINSZ */ X} X Xaddnum(n) Xregister long n; X{ X if (n >= 10) X addnum(n / 10); X addch((char)(n % 10 + '0')); X} X X/* X * Called on alarm signal. X * Simply sets flag, signal processed later. X */ X Xonalarm() X{ X#ifdef SIGTSTP X int dojump = reading; X X reading = FALSE; X alflag++; X if (dojump) X longjmp(alrmjmp, 1); X#else /* !SIGTSTP */ X alflag++; X#endif X} X X/* X * Process alarm signal (or start clock) X */ Xtimer() X{ X time_t tod; X int hour; X int i; X struct tm *t; X struct stat statb; X struct tm *localtime(); X static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; X static long oldmsize = 1000000L; X static int rccount = 10; X static time_t lastismail = 0; X X alflag = 0; X (void) signal(SIGALRM, onalarm); X (void) time(&tod); X t = localtime(&tod); X i = 60 - t->tm_sec; X (void) alarm(i > 30? 30 : i); /* reset alarm */ X hour = t->tm_hour % 12; X if (hour == 0) hour = 12; X (void) sprintf(timestr, "%.3s %d %d:%02d", X months + 3 * t->tm_mon, t->tm_mday, hour, t->tm_min); X if (mailf == NULL || stat(mailf, &statb) < 0) { X statb.st_size = 0; X } X if (statb.st_size > oldmsize) { X ismail = 2; X beep(); X } else { X if (statb.st_size == 0) X ismail = 0; X /* force MAIL for at least 30 seconds */ X else if (ismail > 1 && (lastismail+30) < tod) X ismail = 1; X } X oldmsize = statb.st_size; X lastismail = tod; X if (uflag && !xflag && --rccount < 0) { X writeoutrc(); X if (secpr[0] == '\0') X (void) strcpy(secpr, ".newsrc updated"); X rccount = 10; X } X} X Xchar * Xgetmailname() X{ X static char mailname[32]; X register char *p; X X if( (p = getenv("MAIL")) != NULL) X return p; X#ifndef MMDF X if (username[0] == '\0' || strlen(username) > 15) X return NULL; X#ifdef USG X (void) sprintf(mailname, "/usr/mail/%s", username); X#else /* !USG */ X (void) sprintf(mailname, "/usr/spool/mail/%s", username); X#endif /* !USG */ X#else /* MMDF */ X (void) sprintf(mailname, "%s/mailbox", userhome); X#endif /* MMDF */ X return mailname; X} X X X X/*** Terminal I/O ***/ X X#define INBUFSIZ 8 X Xchar inbuf[INBUFSIZ]; /* input buffer */ Xchar outbuf[BUFSIZ]; /* output buffer */ Xint innleft = 0; /* # of chars in input buffer */ Xint outnleft = BUFSIZ; /* room left in output buffer */ Xchar *innext; /* next input character */ Xchar *outnext = outbuf; /* next space in output buffer */ X#ifdef USG Xint oflags; /* fcntl flags (for nodelay read) */ X#endif X X/* X * Input a character X */ X Xvgetc() X{ X register c; X#if defined(BSD4_2) || defined(BSD4_1C) X int readfds, exceptfds; X#endif X Xrecurse: X if (--innleft >= 0) { X c = *innext++; X } else { X if (alflag) X timer(); X updscr(); /* update the display */ X for (;;) { X if (innleft > 0 || alflag) X goto recurse; X intflag = 0; X#ifdef USG X if (oflags & O_NDELAY) { X oflags &=~ O_NDELAY; X fcntl(0, F_SETFL, oflags); X } X#endif X#ifdef SIGTSTP X if (setjmp(alrmjmp)) X continue; X if (setjmp(intjmp)) X return cintr; X reading = TRUE; X#endif /* SIGTSTP */ X#if defined(BSD4_2) || defined(BSD4_1C) X /* Use a select because it can be interrupted. */ X readfds = 1; exceptfds = 1; X select(1, &readfds, (int *)0, &exceptfds, (int *)0); X if (!(readfds & 1)) X break; X#endif X innleft = read(0, inbuf, INBUFSIZ); X#ifdef SIGTSTP X reading = FALSE; X#endif /* SIGTSTP */ X if (innleft > 0) X break; X if (innleft == 0) { X quitflg++; X return cintr; X } X if (errno != EINTR) X abort(); /* "Can't happen" */ X if (intflag) { X intflag--; X return cintr; X } X } X innext = inbuf + 1; X innleft--; X c = inbuf[0]; X } X#ifndef USG X#ifndef CBREAK X c &= 0177; X if (c == '\034') /* FS character */ X xxit(0); X#endif X#endif X if (c == '\f') { X clearok(curscr, 1); X prflags &=~ NOPRT; X goto recurse; X } X if (c == '\r') X c = '\n'; X return c; X} X X X/* X * Push a character back onto the input stream. X */ X Xpushback(c) X{ X if (innext <= inbuf) X abort(); X *--innext = c; X innleft++; X} X X/* X * Check for terminal input X */ X Xcheckin() X{ X#ifdef FIONREAD X int count; X#endif X#ifdef STATTOP X if (innleft > 0) X#else X if (innleft > 0 || alflag) X#endif X return 1; X#if defined(USG) || defined(FIONREAD) X if (ospeed >= B9600) X return 0; X vflush(); X if (ospeed <= B300) X ttyowait(); X#ifdef USG X if ((oflags & O_NDELAY) == 0) { X oflags |= O_NDELAY; X (void) fcntl(0, F_SETFL, oflags); X } X if ((innleft = read(0, inbuf, INBUFSIZ)) > 0) { X innext = inbuf; X return 1; X } X#endif X#ifdef FIONREAD X count = 0; /* in case FIONREAD fails */ X (void) ioctl(0, FIONREAD, (char *)&count); X if (count) X return 1; X#endif X#endif X return 0; X} X X X X/* X * flush terminal input queue. X */ X Xclearin() X{ X#ifdef USG X (void) ioctl(0, TCFLSH, (char *)0); X#else X#ifdef TIOCFLUSH X (void) ioctl(0, TIOCFLUSH, (char *)0); X#else X struct sgttyb tty; X (void) ioctl(0, TIOCGETP, &tty); X (void) ioctl(0, TIOCSETP, &tty); X#endif X#endif X innleft = 0; X} X Xvputc(c) X{ X if (--outnleft < 0) { X vflush(); X outnleft--; X } X *outnext++ = c; X} X X/* X * Flush the output buffer X */ X Xvflush() X{ X register char *p; X register int i; X#ifdef BSD4_2 X int mask; X#else X unsigned oalarm; X#endif X X#ifdef BSD4_2 X mask = sigblock(1 << (SIGALRM-1)); X#else X oalarm = alarm(0); X#endif X for (p = outbuf ; p < outnext ; p += i) { X if ((i = write(1, p, outnext - p)) < 0) { X if (errno != EINTR) X abort(); /* "Can't happen" */ X i = 0; X } X } X outnleft = BUFSIZ; X outnext = outbuf; X#ifdef BSD4_2 X sigsetmask(mask); X#else X (void) alarm(oalarm); X#endif X} X X/*** terminal modes ***/ X X#ifdef USG Xstatic struct termio oldtty, newtty; X X/* X * Save tty modes X */ X Xttysave() X{ X if (ioctl(1, TCGETA, &oldtty) < 0) X xerror("Can't get tty modes"); X newtty = oldtty; X newtty.c_iflag &=~ (INLCR|IGNCR|ICRNL); X newtty.c_oflag &=~ (OPOST); X newtty.c_lflag &=~ (ICANON|ECHO|ECHOE|ECHOK|ECHONL); X newtty.c_lflag |= (NOFLSH); X newtty.c_cc[VMIN] = 1; X newtty.c_cc[VTIME] = 0; X cerase = oldtty.c_cc[VERASE]; X ckill = oldtty.c_cc[VKILL]; X cintr = oldtty.c_cc[VINTR]; X ospeed = oldtty.c_cflag & CBAUD; X initterm(); X} X X X/* X * Set tty modes for visual processing X */ X Xttyraw() X{ X while (ioctl(1, TCSETAF, &newtty) < 0 && errno == EINTR) X ; X rawterm(); X} X Xttyowait() X{ /* wait for output queue to drain */ X while (ioctl(1, TCSETAW, &newtty) < 0 && errno == EINTR) X ; X} X X/* X * Restore tty modes X */ X Xttycooked() X{ X cookedterm(); X vflush(); X while (ioctl(1, TCSETAF, &oldtty) < 0 && errno == EINTR) X ; X oflags &=~ O_NDELAY; X (void) fcntl(0, F_SETFL, oflags) ; X} X X#else X Xstatic struct sgttyb oldtty, newtty; X#ifdef TIOCGLTC Xstatic struct ltchars oldltchars, newltchars; X#endif X X/* X * Save tty modes X */ X Xttysave() X{ X#ifdef CBREAK X struct tchars tchars; /* special characters, including interrupt */ X#endif X#ifdef SIGTSTP X int getpgrp(); X#if defined(BSD4_2) || defined(BSD4_1C) X int tpgrp; X#else /* BSD4_1 */ X short tpgrp; X#endif /* BSD4_1 */ X Xretry: X#ifdef BSD4_2 X (void) sigblock(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU)); X#else /* !BSD4_2 */ X (void) signal(SIGTSTP, SIG_HOLD); X (void) signal(SIGTTIN, SIG_HOLD); X (void) signal(SIGTTOU, SIG_HOLD); X#endif /* !BSD4_2 */ X if (ioctl(2, TIOCGPGRP, (char *)&tpgrp) < 0) X goto nottty; X if (tpgrp != getpgrp(0)) { /* not in foreground */ X (void) signal(SIGTTOU, SIG_DFL); X#ifdef BSD4_2 X (void) sigsetmask(sigblock(0) & ~sigmask(SIGTTOU)); X#endif /* BSD4_2 */ X (void) kill(0, SIGTTOU); X /* job stops here waiting for SIGCONT */ X goto retry; X } X (void) signal(SIGTTIN, SIG_DFL); X (void) signal(SIGTTOU, SIG_DFL); X (void) signal(SIGTSTP, SIG_DFL); X#ifdef BSD4_2 X (void) sigsetmask(sigblock(0) & ~(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU))); X#endif /* BSD4_2 */ X#endif /* SIGTSTP */ X if (ioctl(1, TIOCGETP, (char *)&oldtty) < 0) Xnottty: xerror("Can't get tty modes"); X newtty = oldtty; X newtty.sg_flags &=~ (CRMOD|ECHO|XTABS); X#ifdef CBREAK X newtty.sg_flags |= CBREAK; X ioctl(1, TIOCGETC, (char *)&tchars); X cintr = tchars.t_intrc; X#else /* !CBREAK */ X newtty.sg_flags |= RAW; X cintr = '\0177'; /* forcibly this on V6 systems */ X#endif /* !CBREAK */ X cerase = oldtty.sg_erase; X ckill = oldtty.sg_kill; X ospeed = oldtty.sg_ospeed; X#ifdef TIOCGLTC X if (ioctl(1, TIOCGLTC, (char *)&oldltchars) >= 0) { X newltchars = oldltchars; X newltchars.t_dsuspc = -1; X cwerase = oldltchars.t_werasc; X } X#endif X initterm(); X#ifdef SIGTSTP X (void) signal(SIGTTIN, onstop); X (void) signal(SIGTTOU, onstop); X (void) signal(SIGTSTP, onstop); X#endif /* SIGTSTP */ X} X X X/* X * Set tty modes for visual processing X */ X Xttyraw() X{ X while (ioctl(1, TIOCSETN, (char *)&newtty) < 0 && errno == EINTR) X ; X#ifdef TIOCGLTC X if (newltchars.t_dsuspc == '\377') X while (ioctl(1, TIOCSLTC, (char *)&newltchars) < 0 && errno == EINTR) X ; X#endif X rawterm(); X} X Xttyowait() X{ /* wait for output queue to drain */ X#ifdef TIOCDRAIN /* This ioctl is a local mod on linus */ X (void) ioctl(1, TIOCDRAIN, (char *)0); X#endif X} X X X/* X * Restore tty modes X */ X Xttycooked() X{ X cookedterm(); X vflush(); X while (ioctl(1, TIOCSETN, (char *)&oldtty) < 0 && errno == EINTR) X ; X#ifdef TIOCGLTC X if (newltchars.t_dsuspc == '\377') X while (ioctl(1, TIOCSLTC, (char *)&oldltchars) < 0 && errno == EINTR) X ; X#endif X} X X#endif X X X X/*** signal handlers ***/ X Xonint() { X#ifdef SIGTSTP X int dojump = reading; X X reading = FALSE; X#endif /* SIGTSTP */ X if (!news) { X ttycooked(); X xxit(1); X } X (void) signal(SIGINT, onint); X clearin(); /* flush input queue */ X#ifdef SIGTSTP X if (dojump) X longjmp(intjmp, 1); X#endif /* SIGTSTP */ X intflag++; X} X X#ifdef SIGTSTP Xonstop(signo) Xint signo; X{ X /* restore old terminal state */ X botscreen(); X vflush(); X ttycooked(); X (void) signal(signo, SIG_DFL); X#ifdef BSD4_2 X (void) sigblock(sigmask(SIGALRM)|sigmask(SIGINT)); X (void) sigsetmask(sigblock(0) & ~sigmask(signo)); X#else /* BSD4_1 */ X (void) alarm(0); X#endif /* BSD4_1 */ X (void) kill(0, signo); /* stop here until continued */ X X (void) signal(signo, onstop); X /* restore our special terminal state */ X ttyraw(); X#ifdef TIOCGWINSZ X winch(); /* get current window size and redraw screen */ X#else /* !TIOCGWINSZ */ X clearok(curscr, 1); X updscr(); X#endif /* !TIOCGWINSZ */ X#ifdef BSD4_2 X (void) sigsetmask(sigblock(0) & ~(sigmask(SIGALRM)|sigmask(SIGINT))); X#else /* BSD4_1 */ X timer(); X#endif /* BSD4_1 */ X} X#endif X X/*** stolen from rfuncs2.c and modified ***/ X Xvsave(to, flags) Xregister char *to; X{ X register FILE *ufp; X int isprogram = 0; X int isnew = 1; X long saveoff; X char temp[20]; X char *fname; X char prog[BUFLEN + 24]; X int err; X X saveoff = ftell(fp); X (void) fseek(fp, artbody, 0); X fname = to; X if (*to == PIPECHAR) { X if (strlen(to) > BUFLEN) { X msg("Command name too long"); X goto out; X } X flags |= OVWRITE; X (void) strcpy(temp, "/tmp/vnXXXXXX"); X (void) mktemp(temp); X fname = temp; X _amove(ROWS - 1, 0); X vflush(); X } X if ((flags & OVWRITE) == 0) { X ufp = fopen(fname, "r"); X if (ufp != NULL) { X (void) fclose(ufp); X isnew = 0; X } X } X (void) umask(savmask); X X if (*to == PIPECHAR) X isprogram++; X if ((ufp = fopen(fname, (flags & OVWRITE) == 0? "a" : "w")) == NULL) { X msg("Cannot open %s", fname); X goto out; X } X /* X * V7MAIL code is here to conform to V7 mail format. X * If you need a different format to be able to X * use your local mail command (such as four ^A's X * on the end of articles) substitute it here. X */ X if (flags & SVHEAD) { X#ifdef MMDF X if (!isprogram) X fprintf(ufp, "\001\001\001\001\n"); X#endif /* MMDF */ X#ifdef V7MAIL X h->subtime = cgtdate(h->subdate); X fprintf(ufp, "From %s %s", replyname(h), ctime(&h->subtime)); X#endif X hprint(h, ufp, 2); X#ifdef V7MAIL X tprint(fp, ufp, TRUE); X putc('\n', ufp); /* force blank line at end (ugh) */ X#else X tprint(fp, ufp, FALSE); X#endif X } else { X tprint(fp, ufp, FALSE); X } X X err = ferror(ufp); X X fclose(ufp); X if (isprogram) { X if (err) X msg("error in writing temp file, maybe disk full?"); X else { X (void) sprintf(prog, "(%s)<%s", to + 1, fname); X shcmd(prog, CWAIT); X prflags |= NOPRT; X } X } else { X msg("%sfile: %s %s", X err? "ERROR WHILE WRITING ": "", X to, X (flags&OVWRITE)? "written": X isnew ? "created" : "appended"); X } X X /* If we got an error, screen may be messed. E.g. 4.2BSD X * writes "disk full" messages to the user's tty. X */ X if (err) { X clearok(curscr, 1); X updscr(); X } X Xout: X if (isprogram) { X (void) unlink(fname); X } X (void) umask(N_UMASK); X (void) fseek(fp, saveoff, 0); X} X Xxxit(status) Xint status; X{ X (void) unlink(infile); X (void) unlink(outfile); X#ifdef SORTACTIVE X if (strncmp(ACTIVE,"/tmp/", 5) == 0) X (void) unlink(ACTIVE); X#endif /* SORTACTIVE */ X if (ospeed) { /* is == 0, we haven't been in raw mode yet */ X botscreen(); X vflush(); X ttycooked(); X } X exit(status); X} END_OF_FILE if test 55369 -ne `wc -c <'visual.c'`; then echo shar: \"'visual.c'\" unpacked with wrong size! fi # end of 'visual.c' fi echo shar: End of shell archive. exit 0 -- "Zeta Microcomputer Software" ACSnet: nick@ultima.cs.uts.oz UUCP: ...!uunet!munnari!ultima.cs.uts.oz!nick Fidonet: Nick Andrew on 3:713/602 (Zeta)