* visual - visual news interface.
static char *SccsId
= "@(#)visual.c 1.38 10/15/87";
#define sigmask(m) (1<<((m)-1))
#define ARTWLEN (ROWS-2)/* number of lines used to display article */
#define even(cols) ((cols&1) ? cols + 1 : cols)
#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 */
#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 */
#define PIPECHAR '|' /* indicate save command should pipe to program */
#define CAGAIN ('e'&0x1F) /* Save-to-same-place indicator */
#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 */
#define CWAIT 0001 /* type "continue?" and wait for return */
#define BKGRND 0002 /* run process in the background */
#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 */
#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 */
#define okclear() (_junked = 1)
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 */
static char cwerase
; /* word erase character */
short ospeed
; /* terminal speed NOT STATIC */
static int intflag
; /* set if interrupt received */
static int reading
; /* to keep stupid BSD from restarting reads */
static int hasdb
; /* true if article data base exists */
static int endsuba
; /* end of sub-article in digest */
FILE *debugf
; /* file to write debugging info on */
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
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 */
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 FILE *fp
; /* current article to be printed*/
debugf
= fopen("DEBUG", "w");
setbuf(debugf
, (char *)NULL
);
if ((atime
= cgtdate(datebuf
)) == -1)
xerror("Cannot parse date string");
(void) close(creat(tfname
,0666));
if ((tfp
= fopen(tfname
, "w+")) == NULL
)
xerror("Can't create temp file");
fputs("Using article data base\n", stderr
); /*DEBUG*/
(void) signal(SIGINT
, onint
);
(void) signal(SIGQUIT
, xxit
);
/* loop reading articles. */
(void) strcpy(goodone
, filename
);
ospeed
= 0; /* to convince xxit() not to clear screen */
fprintf(stderr
, "No news.\n");
* Read and execute a command.
appfile(fp
, lastlin
+ 1);
appfile(fp
, dlinno
+ ARTWLEN
+ 1);
endsuba
= findend(dlinno
);
if (artlines
> dlinno
+ ARTWLEN
|| endsuba
> 0 && endsuba
< artlines
if (artlines
> dlinno
+ ARTWLEN
|| (prflags
& HDRONLY
) && artlines
> hdrend
) {
if (prflags
&HDRONLY
|| maxlinno
== 0)
(void) strcpy(prompt
, "more? ");
(void) sprintf(prompt
, "more(%d%%)? ",
endsuba
: (dlinno
+ ARTWLEN
)) -
hdrend
) * 100) / maxlinno
);
(void) sprintf(prompt
, "more(%d%%)? ",
((dlinno
+ ARTWLEN
- hdrend
) * 100) / maxlinno
);
(void) strcpy(prompt
, "next? ");
clear(bit
); /* article read */
p
= prompt
+ strlen(prompt
);
* 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
if (c
== cerase
|| c
== '\b' || c
== '\177') {
break; /* Use as action char */
countset
= 0; /* Erase only char of count */
count
/= 10L; /* Erase 1 char of count */
if (c
== ckill
|| c
== cwerase
) {
} else if (c
< '0' || c
> '9')
count
= (count
* 10) + (c
- '0');
(void) sprintf(p
, "%ld", count
);
if (c
== '\033') { /* escape */
(void) strcat(prompt
, "M-");
docmd(c
, count
, countset
);
if (c
!= '?' && c
!= 'H') /* UGGH */
* Process one command, which has already been typed in.
docmd(c
, count
, countset
)
/* display list of articles in current group */
list_group(groupdir
, countset
? (int) count
: 0,
(c
== 'l') ? FALSE
: TRUE
, pngsize
);
/* Show more of current article, or advance to next article */
case '\06': /* Control-F for vi compat */
else if (prflags
& HDRONLY
) {
appfile(fp
, lastlin
+ 1);
} while(STRNCMP(linebuf
, "------------------------", 24)
dlinno
= endsuba
= lastlin
;
else if ((appfile(fp
, dlinno
+ 2 * ARTWLEN
), artread
)
&& hasscroll
&& artlines
- dlinno
<= ARTWLEN
+ 2)
dlinno
= artlines
- ARTWLEN
;
dlinno
+= ARTWLEN
* count
;
/* No. Go on to next article. */
case '.': /* useful if you have a keypad */
/* Back up count pages */
case '\002': /* Control-B */
dlinno
-= ARTWLEN
* count
;
/* forward half a page */
case '\004': /* Control-D, as in vi */
dlinno
+= ARTWLEN
/2 * count
;
/* backward half a page */
case '\025': /* Control-U */
dlinno
-= ARTWLEN
/2 * count
;
/* forward count lines */
case '\016': /* Control-N */
case '\005': /* Control-E */
/* backwards count lines */
case '\020': /* Control-P */
case '\031': /* Control-Y */
/* Turn displaying of article back on */
* Unsubscribe to the newsgroup and go on to next group
if (actdirect
== BACKWARD
)
quitflg
= 1; /* probably unnecessary */
if (c
!= cintr
&& c
!= ckill
)
/* Print the current version of news */
msg("News version: %s", news_version
);
/* Decrypt joke. Always does rot 13 */
for (i
= hdrend
; i
< artlines
; 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';
prflags
&=~ (HDRONLY
|NOPRT
);
/* write out the article someplace */
/* w writes out without the header */
/* | defaults to pipeing */
static char savebuf
[BUFLEN
];
/* We loop back to here each time user types ^U to prompt */
/* Prompt based on command char */
msg( (c
==PIPECHAR
)? "|": "file: ");
while ((wflags
= vgetc()) == ' ');
if ((wflags
& 0x1F) == wflags
) { /* control char */
/* don't let them pipe to a saved filename */
if (c
== PIPECHAR
&& savebuf
[0] != PIPECHAR
) {
wflags
= prget( (savebuf
[0] == PIPECHAR
) ? "" : "file: ",
if (wflags
) break; /* Interrupted out */
if (c
== PIPECHAR
) c
= 's';
bptr
++; /* strip leading spaces */
if (*bptr
!= PIPECHAR
&& *bptr
!= '/') {
(void) strcpy(hetyped
, bptr
);
if (hetyped
[0] == '~' && hetyped
[1] == '/') {
} else if (boxptr
= getenv("NEWSBOX")) {
if (index(boxptr
, '%')) {
sprintf(bptr
, boxptr
, groupdir
);
if (stat(bptr
,&stbf
) < 0) {
if (mkdir(bptr
, 0777) < 0) {
msg("Cannot create directory %s", bptr
);
} else if ((stbf
.st_mode
&S_IFMT
) != S_IFDIR
) {
msg("%s not a directory", bptr
);
(void) strcat(bptr
, "/");
(void) strcat(bptr
, hetyped
);
(void) strcat(bptr
, "Articles");
/* handle ~/ for pipes */
bptr
++; /* skip PIPECHAR */
bptr
++; /* strip leading spaces */
if (bptr
[0] == '~' && bptr
[1] == '/') {
strcpy(fullname
,userhome
);
/* we know PIPECHAR is in *savebuf */
strcpy(savebuf
+1,fullname
);
(void) strcpy(bfr
, filename
);
(void) strcpy(filename
, ofilename1
);
(void) strcpy(ofilename1
, bfr
);
if (STRCMP(groupdir
, ogroupdir
)) {
(void) strcpy(bfr
, groupdir
);
selectng(ogroupdir
, FALSE
, FALSE
);
(void) strcpy(groupdir
, ogroupdir
);
(void) strcpy(ogroupdir
, bfr
);
caseplus
: if (count
== 0)
for (i
= 0; i
< count
; i
++) {
if ((bit
> pngsize
) || (rflag
&& bit
< 1))
/* exit - time updated to that of most recently read article */
/* cancel the article. */
strcpy(prompt
, "cancel [n]? ");
msg("Article not cancelled");
(void) strcpy(linebuf
, SHELL
);
while (p
> linebuf
&& p
[-1] == ' ')
} else if (*p
== PIPECHAR
) {
(void) sprintf(bfr
, "(%s)%cmail '%s'", linebuf
, PIPECHAR
, username
);
(void) strcpy(linebuf
, bfr
);
/* mark the rest of the articles in this group as read */
while (bit
<= ngsize
&& bit
>= minartno
) {
/* Print the full header */
msg("No current article");
for (i
= 0; i
< ARTWLEN
; i
++) {
if (fgets(linebuf
, COLS
, fp
) == NULL
)
(void) fseek(fp
, Hoffset
, 0);
prflags
|= HELPMSG
|NEWART
;
case 'b': /* backup 1 article */
case 'A': /* specific number */
msg("not that many articles");
/* display parent article */
if (hasdb
&& (ptr3
= findparent(h
->ident
, &nart
)) != NULL
) {
msg("parent: %s/%ld", ptr3
, nart
); /*DEBUG*/
if (h
->followid
[0] == '\0') {
msg("no references line");
ptr1
= h
->followid
+ strlen(h
->followid
);
ptr1
= rindex(h
->followid
, ' ');
ptr1
= rindex(h
->followid
, ' ');
} while (ptr1
!= NULL
&& --count
> 0);
(void) strncpy(linebuf
, ptr1
, ptr2
- ptr1
);
linebuf
[ptr2
- ptr1
] = '\0';
updscr(); /* may take this out later */
/* specific message ID. */
linebuf
[0] = '<'; linebuf
[1] = 0;
if (prget("", linebuf
)) {
searchid
: secpr
[0] = '\0';
if (index(linebuf
, '@') == NULL
&& index(linebuf
, '>') == NULL
) {
(void) sprintf(bfr
, "<%s@%s.UUCP>", ptr2
, ptr1
);
(void) strcpy(linebuf
, bfr
);
if (index(linebuf
, '>') == NULL
)
(void) strcat(linebuf
, ">");
ptr1
= findhist(linebuf
);
msg("%s not found", linebuf
);
ptr2
= index(ptr1
, '\t');
ptr3
= index(++ptr2
, '\t');
ptr2
= index(++ptr3
, ' ');
if (STRCMP(ptr3
, "cancelled") == 0)
msg("%s has been cancelled", linebuf
);
msg("%s has expired", linebuf
);
(void) sscanf(ptr2
, "%ld", &nart
);
* Go to a given article. Ptr3 specifies the newsgroup
* and nart specifies the article number.
(void) strcpy(ogroupdir
, ptr3
);
if (STRCMP(groupdir
, ogroupdir
)) {
(void) strcpy(bfr
, groupdir
);
selectng(ogroupdir
, TRUE
, PERHAPS
);
(void) strcpy(groupdir
, ogroupdir
);
(void) strcpy(ogroupdir
, bfr
);
if (bit
!= nart
|| STRCMP(groupdir
, ptr3
) != 0) {
msg("can't read %s/%ld", ptr3
, nart
);
if (STRCMP(h
->followto
, "poster") == 0) {
(void) sprintf(bfr
, "%s/%s", BIN
, "postnews");
(void) sprintf(bfr
, "%s/%s %s", BIN
, "postnews", goodone
);
/* erase - pretend we haven't seen this article. */
goto caseplus
; /* skip this article for now */
msg("Article %ld of %ld", rfq
? oobit
: bit
, pngsize
);
(void) sprintf(linebuf
, "%s/vnews.help", LIB
);
if ((helpf
= fopen(linebuf
, "r")) == NULL
) {
msg("Can't open help file");
while (fgets(linebuf
, LBUFLEN
, helpf
) != NULL
)
prflags
|= HELPMSG
|NEWART
;
if (c
!= ckill
&& c
!= cintr
&& c
!= cerase
)
register char *poster
, *r
;
/* only compare up to '.' or ' ' */
notauthor
= STRCMP(username
, poster
);
if (uid
!= ROOTID
&& uid
&& notauthor
) {
msg("Can't cancel what you didn't write.");
if (!cancel(stderr
, h
, notauthor
)) {
/* Put the user in the editor to create the body of the reply. */
if (ed
== NULL
|| *ed
== '\0')
msg("You don't have an editor");
(void) close(creat(tf
,0600));
if ((rfp
= fopen(tf
, "w")) == NULL
) {
msg("Can't create %s", tf
) ;
(void) strcpy(subj
, h
->title
);
if (!PREFIX(subj
, "Re:")){
(void) strcpy(bfr
, subj
);
(void) sprintf(subj
, "Re: %s", bfr
);
fprintf(rfp
, "To: %s\n", p
);
fprintf(rfp
, "Subject: %s\n", subj
);
fprintf(rfp
, "In-reply-to: your article %s\n", h
->ident
);
fprintf(rfp
, "News-Path: %s\n", h
->path
);
(void) sprintf(rcbuf
, "%s -t < %s; rm -f %s", MAILPARSER
, tf
, tf
);
of
= xart_open(goodone
, "r");
while (fgets(buf
, sizeof buf
, of
) != NULL
)
while (fgets(buf
, sizeof buf
, of
) != NULL
)
fprintf(rfp
, "> %s", buf
);
(void) fstat(fileno(rfp
), &statb
);
(void) sprintf(edcmdbuf
, "exec %s %s", ed
, tf
);
msg("Couldn't run editor");
if (access(tf
, 4) || stat(tf
, &statb
)) {
msg("No input file - mail not sent");
if (statb
.st_mtime
== creatm
|| statb
.st_size
< 5) {
msg("File unchanged - no message posted");
extern char *replyname();
if (index("\"\\$", *p
) != 0)
if ((MAILER
= getenv("MAILER")) == NULL
)
sprintf(rcbuf
, MAILER
, hptr
->title
);
sprintf(bfr
, "%s %s", rcbuf
, address
);
msg("Couldn't run mailer");
if (prget("group? ", linebuf
))
if (!*bptr
|| *bptr
== '-') {
if (actdirect
== BACKWARD
)
selectng(bptr
, TRUE
, TRUE
);
* Find the next article we want to consider, if we're done with
* the last one, and show the header.
register struct direct
*dir
;
goto nextart2
; /* Kludge for "-" command. */
if (bit
== obit
) /* Return if still on same article as last time */
/* If done with this newsgroup, find the next one. */
while (ngsize
<= 0 || (!rflag
&& ((long) bit
> ngsize
)) || (rflag
&& bit
< minartno
)) {
if (actdirect
== BACKWARD
) {
else /* if (rfq++ || pflag || cflag) */
/* speed things up by not searching for article -1 */
rcreadok
= 2; /* have seen >= 1 article */
if ((fp
= getarticle(groupdir
, bit
, "ARTICLE")) == NULL
)
strcpy(filename
, article_name());
(void) sprintf(filename
, "%s/%ld", dirname(groupdir
), bit
);
if (rfq
&& goodone
[0]) /* ??? */
strcpy(filename
, goodone
);
/* Decide if we want to show this article. */
if ((fp
= art_open(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 (*groupdir
== ' ' || *groupdir
== '\0' ||
set_group(groupdir
) == NULL
)
dirp
= opendir(dirname(groupdir
));
msg("Can't open %s", dirname(groupdir
));
nextnum
= rflag
? minartno
- 1 : ngsize
+ 1;
(void) sprintf(bfr
,"STAT %ld",tnum
);
(void) get_server(workspace
,sizeof(workspace
));
if (*workspace
!= CHAR_OK
) {
while ((dir
= readdir(dirp
)) != NULL
) {
tnum
= atol(dir
->d_name
);
if (rflag
? (tnum
> nextnum
&& tnum
< bit
)
: (tnum
< nextnum
&& tnum
> bit
))
break; /* not exactly right */
if (rflag
? (nextnum
>= bit
) : (nextnum
<= bit
))
} while (rflag
? (nextnum
< bit
) : (nextnum
> bit
));
if (hread(h
, fp
, TRUE
) == NULL
|| (!rfq
&& !aselect(h
, aabs
))) {
if (! cflag
&& hdrend
< ARTWLEN
&& !cflag
)
maxlinno
= NLINES(h
, fp
);
* Print out whatever the appropriate header is
static FILE *ngfd
= NULL
;
static int triedopen
= 0;
char pbuf
[BUFLEN
], *printbuffer
= groupdir
;
(void) sprintf(pbuf
,"%s/newsgroups", LIB
);
while (fgets(ibuf
, BUFLEN
, ngfd
) != NULL
) {
if (STRCMP(ibuf
, groupdir
) == 0) {
(void) sprintf(pbuf
,"%s (%s)",
(void) sprintf(linebuf
, "Newsgroup %s", printbuffer
);
(void) sprintf(linebuf
, "Article %s %s",
h
->ident
, briefdate(h
->subdate
));
vhprint(h
, pflag
? 1 : 0);
(void) sprintf(linebuf
, "(%d lines)", NLINES(h
, fp
)); tfappend(linebuf
);
* 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 ..)
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
);
xtabf(p
->xref
, sizeof p
->xref
);
if (index(str
, '\t') == NULL
)
for (p
= buf
; c
= *str
++; i
++) {
strncpy(s
, buf
, size
- 1);
* Print the file header to the temp file.
register struct hbuf
*hp
;
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
, '(');
(void) strcpy(fname
, p1
+1);
(void) sprintf(linebuf
, "Subject: %s", hp
->title
);
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
);
(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
);
*--p1
= '\0'; /* bump over the '(' */
* 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
);
(void) sprintf(linebuf
, "Path: %s", tailpath(hp
));
if (fname
[0] || (hp
->organization
[0] && !hflag
)) {
(void) strcat(linebuf
, " (");
(void) strcpy(fname
, hp
->from
);
(void) strcat(linebuf
, fname
);
if (hp
->organization
[0] && !hflag
) {
(void) strcat(linebuf
, " @ ");
(void) strcat(linebuf
, hp
->organization
);
(void) strcat(linebuf
, ")");
(void) sprintf(linebuf
, "Control: %s", hp
->ctlmsg
);
(void) sprintf(linebuf
, "Newsgroups: %s", hp
->nbuf
); tfappend(linebuf
);
(void) sprintf(linebuf
, "Date: %s", hp
->subdate
); tfappend(linebuf
);
(void) sprintf(linebuf
, "Sender: %s", hp
->sender
);
(void) sprintf(linebuf
, "Reply-To: %s", hp
->replyto
);
(void) sprintf(linebuf
, "Followup-To: %s", hp
->followto
);
else if (STRCMP(hp
->nbuf
, groupdir
) != 0) {
(void) sprintf(linebuf
, "Newsgroups: %s", hp
->nbuf
);
if (lookart(id
, &a
) == DNULL
)
*num
= a
.groups
[0].artno
;
return ngname(a
.groups
[0].newsgroup
);
* 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.
register char *icol
; /* &linebuf[0] <= icol <= maxcol */
if (artread
|| artlines
>= nlines
|| iop
== NULL
)
while ((c
= getc(iop
)) != EOF
) {
if (icol
== maxcol
&& icol
< linebuf
+ LBUFLEN
- 1) {
icol
= (icol
- linebuf
&~ 07) + 8 + linebuf
;
if (icol
> linebuf
) --icol
;
outline(); outline(); outline();
else if (icol
>= linebuf
+ LBUFLEN
- 1)
else if (icol
== maxcol
) {
if (maxcol
!= linebuf
) /* file not terminated with newline */
while (maxcol
< col
&& maxcol
< linebuf
+ LBUFLEN
- 1)
if (STRNCMP(linebuf
, ">From ", 6) == 0) {
for (p
= linebuf
; (*p
= p
[1]) != '\0' ; p
++);
* Prompt the user and get a line.
* "prompter" is the prompt. "buf" contains a string which
* will be used as the initial user response (which may be edited
* by the user with backspace, ^U, etc). The resulting line is
* returned in "buf". The result of prget() is:
* 0 if the line was terminated by NL or CR
* 1 if it was terminated by the interrupt character.
* 2 if it was terminated by erasing all the characters, including
* one or more that were prompted initially in "buf". (If "buf"
* was empty, this will never occur.)
register char *p
, *q
, *r
;
for (q
= prompter
; *q
; q
++)
for (q
= buf
; *q
; q
++) {
if (p
< &secpr
[SECPRLEN
-1] && *q
>= ' ' && *q
<= '~')
if (c
== '\n' || c
== '\r' || c
== cintr
) {
if (c
== cerase
|| c
== '\b' || c
== '\177') {
} else if (c
== cwerase
) {
while (r
> buf
&& (r
[-1] == ' ' || r
[-1] == '\t'))
while (r
> buf
&& r
[-1] != ' ' && r
[-1] != '\t')
if ((r
== buf
) && hadprompt
)
* Execute a shell command.
arg
[0] = SHELL
, arg
[1] = "-c", arg
[2] = cmd
, arg
[3] = NULL
;
char *env
[100], **envp
, **oenvp
;
(void) signal(SIGTSTP
, SIG_DFL
);
(void) signal(SIGTTIN
, SIG_DFL
);
(void) signal(SIGTTOU
, SIG_DFL
);
#if defined(BSD4_2) && !defined(sun)
while ((pid
= vfork()) == -1)
/* 4.1 BSD (at least) can't handle this vfork with -ljobs */
while ((pid
= fork()) == -1)
sleep(1); /* must not clear alarm */
for (i
= 3 ; i
< 20 ; i
++)
(void) signal(SIGINT
, SIG_IGN
);
(void) signal(SIGQUIT
, SIG_IGN
);
(void) signal(SIGTSTP
, SIG_IGN
);
(void) signal(SIGTTIN
, SIG_IGN
);
(void) signal(SIGTTOU
, SIG_IGN
);
(void) open("/dev/null", 2);
(void) sprintf(a
, "A=%s", filename
);
for (envp
= env
+ 1 ; *oenvp
!= NULL
&& envp
< env
+ 98 ; oenvp
++)
if ((*oenvp
)[0] != 'A' || (*oenvp
)[1] != '=')
execve(args
[0], args
, env
);
savequit
= signal(SIGQUIT
, SIG_IGN
);
while ((i
= wait(&pstatus
)) != pid
&& (i
!= -1 || errno
== EINTR
))
fprintf(stderr
, "[Hit return to continue]");
while ((errno
= 0, i
= getchar()) != '\n'
&& (i
!= EOF
|| errno
== EINTR
));
(void) signal(SIGQUIT
, savequit
);
(void) signal(SIGTSTP
, onstop
);
(void) signal(SIGTTIN
, onstop
);
(void) signal(SIGTTOU
, onstop
);
* Find end of current subarticle in digest.
for (i
= l
; i
< l
+ ARTWLEN
&& i
< lastlin
; i
++) {
for (p
= linebuf
; *p
== '-' ; p
++)
if ( (n
> 23 && n
< 33) || (n
> 65 && n
< 79)) {
/*** Routines for handling temporary file ***/
nxtlin
= index(tline
, '\n');
while (strlen(tline
) > COLS
) {
} while ((tline
= nxtlin
) != NULL
);
register FILE *rtfp
; /* try to make it a little faster */
p
= tline
, i
= even(COLS
);
fread(tline
, even(COLS
), 1, tfp
);
static int lastwrflag
= 1;
if (linno
!= tflinno
|| wrflag
!= lastwrflag
) {
(void) fseek(tfp
, (long)linno
* even(COLS
), 0);
(void) sprintf(secpr
, s
, a1
, a2
, a3
, a4
);
* 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 */
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).
if(UPDATING
) /* concurrency. wow! */
else if((WINCH
== 0) && (savelinno
>= 0)) {
appfile(fp
, dlinno
+ ARTWLEN
+ 1);
if ((prflags
& HELPMSG
) == 0
&& (dlinno
!= savelinno
|| savepr
!= prflags
)
if ((prflags
& HDRONLY
) && count
> hdrend
)
if (endsuba
> 0 && count
> endsuba
- dlinno
)
count
= endsuba
- dlinno
;
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
++)
for (i
= 0 ; i
< count
; i
++) {
tfget(linebuf
, dlinno
+ i
);
mvaddstr(ARTWIN
+ i
, 0, linebuf
);
clrline(SPLINE
), clrline(PRLINE
);
mvaddstr(PRLINE
, 0, prompt
);
if (strlen(secpr
) <= COLS
)
mvaddstr(PRLINE
, 0, prompt
);
mvaddstr(PRLINE
, 59, timestr
);
mvaddstr(PRLINE
, 17, groupdir
);
addch(' '); addnum(bit
); addch('/'); addnum(pngsize
); addch(' ');
mvaddstr(PRLINE
, 75, ismail
> 1? "MAIL" : "mail");
mvaddstr(SPLINE
, 0, secpr
);
move(PRLINE
, strlen(prompt
));
else if (curflag
== CURHOME
)
if (WINCH
) { /* window changed while updating screen */
addch((char)(n
% 10 + '0'));
* Called on alarm signal.
* Simply sets flag, signal processed later.
* Process alarm signal (or start clock)
static char months
[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
static long oldmsize
= 1000000L;
static time_t lastismail
= 0;
(void) signal(SIGALRM
, onalarm
);
(void) alarm(i
> 30? 30 : i
); /* reset alarm */
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) {
if (statb
.st_size
> oldmsize
) {
/* force MAIL for at least 30 seconds */
else if (ismail
> 1 && (lastismail
+30) < tod
)
oldmsize
= statb
.st_size
;
if (uflag
&& !xflag
&& --rccount
< 0) {
(void) strcpy(secpr
, ".newsrc updated");
static char mailname
[32];
if( (p
= getenv("MAIL")) != NULL
)
if (username
[0] == '\0' || strlen(username
) > 15)
(void) sprintf(mailname
, "/usr/mail/%s", username
);
(void) sprintf(mailname
, "/usr/spool/mail/%s", username
);
(void) sprintf(mailname
, "%s/mailbox", userhome
);
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 */
int oflags
; /* fcntl flags (for nodelay read) */
#if defined(BSD4_2) || defined(BSD4_1C)
updscr(); /* update the display */
if (innleft
> 0 || alflag
)
fcntl(0, F_SETFL
, oflags
);
#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);
innleft
= read(0, inbuf
, INBUFSIZ
);
abort(); /* "Can't happen" */
if (c
== '\034') /* FS character */
* Push a character back onto the input stream.
* Check for terminal input
if (innleft
> 0 || alflag
)
#if defined(USG) || defined(FIONREAD)
if ((oflags
& O_NDELAY
) == 0) {
(void) fcntl(0, F_SETFL
, oflags
);
if ((innleft
= read(0, inbuf
, INBUFSIZ
)) > 0) {
count
= 0; /* in case FIONREAD fails */
(void) ioctl(0, FIONREAD
, (char *)&count
);
* flush terminal input queue.
(void) ioctl(0, TCFLSH
, (char *)0);
(void) ioctl(0, TIOCFLUSH
, (char *)0);
(void) ioctl(0, TIOCGETP
, &tty
);
(void) ioctl(0, TIOCSETP
, &tty
);
* Flush the output buffer
mask
= sigblock(1 << (SIGALRM
-1));
for (p
= outbuf
; p
< outnext
; p
+= i
) {
if ((i
= write(1, p
, outnext
- p
)) < 0) {
abort(); /* "Can't happen" */
static struct termio oldtty
, newtty
;
if (ioctl(1, TCGETA
, &oldtty
) < 0)
xerror("Can't get tty modes");
newtty
.c_iflag
&=~ (INLCR
|IGNCR
|ICRNL
);
newtty
.c_oflag
&=~ (OPOST
);
newtty
.c_lflag
&=~ (ICANON
|ECHO
|ECHOE
|ECHOK
|ECHONL
);
newtty
.c_lflag
|= (NOFLSH
);
cerase
= oldtty
.c_cc
[VERASE
];
ckill
= oldtty
.c_cc
[VKILL
];
cintr
= oldtty
.c_cc
[VINTR
];
ospeed
= oldtty
.c_cflag
& CBAUD
;
* Set tty modes for visual processing
while (ioctl(1, TCSETAF
, &newtty
) < 0 && errno
== EINTR
)
{ /* wait for output queue to drain */
while (ioctl(1, TCSETAW
, &newtty
) < 0 && errno
== EINTR
)
while (ioctl(1, TCSETAF
, &oldtty
) < 0 && errno
== EINTR
)
(void) fcntl(0, F_SETFL
, oflags
) ;
static struct sgttyb oldtty
, newtty
;
static struct ltchars oldltchars
, newltchars
;
struct tchars tchars
; /* special characters, including interrupt */
#if defined(BSD4_2) || defined(BSD4_1C)
(void) sigblock(sigmask(SIGTSTP
)|sigmask(SIGTTIN
)|sigmask(SIGTTOU
));
(void) signal(SIGTSTP
, SIG_HOLD
);
(void) signal(SIGTTIN
, SIG_HOLD
);
(void) signal(SIGTTOU
, SIG_HOLD
);
if (ioctl(2, TIOCGPGRP
, (char *)&tpgrp
) < 0)
if (tpgrp
!= getpgrp(0)) { /* not in foreground */
(void) signal(SIGTTOU
, SIG_DFL
);
(void) sigsetmask(sigblock(0) & ~sigmask(SIGTTOU
));
/* job stops here waiting for SIGCONT */
(void) signal(SIGTTIN
, SIG_DFL
);
(void) signal(SIGTTOU
, SIG_DFL
);
(void) signal(SIGTSTP
, SIG_DFL
);
(void) sigsetmask(sigblock(0) & ~(sigmask(SIGTSTP
)|sigmask(SIGTTIN
)|sigmask(SIGTTOU
)));
if (ioctl(1, TIOCGETP
, (char *)&oldtty
) < 0)
nottty
: xerror("Can't get tty modes");
newtty
.sg_flags
&=~ (CRMOD
|ECHO
|XTABS
);
newtty
.sg_flags
|= CBREAK
;
ioctl(1, TIOCGETC
, (char *)&tchars
);
cintr
= '\0177'; /* forcibly this on V6 systems */
cerase
= oldtty
.sg_erase
;
ospeed
= oldtty
.sg_ospeed
;
if (ioctl(1, TIOCGLTC
, (char *)&oldltchars
) >= 0) {
newltchars
.t_dsuspc
= -1;
cwerase
= oldltchars
.t_werasc
;
(void) signal(SIGTTIN
, onstop
);
(void) signal(SIGTTOU
, onstop
);
(void) signal(SIGTSTP
, onstop
);
* Set tty modes for visual processing
while (ioctl(1, TIOCSETN
, (char *)&newtty
) < 0 && errno
== EINTR
)
if (newltchars
.t_dsuspc
== '\377')
while (ioctl(1, TIOCSLTC
, (char *)&newltchars
) < 0 && errno
== EINTR
)
{ /* wait for output queue to drain */
#ifdef TIOCDRAIN /* This ioctl is a local mod on linus */
(void) ioctl(1, TIOCDRAIN
, (char *)0);
while (ioctl(1, TIOCSETN
, (char *)&oldtty
) < 0 && errno
== EINTR
)
if (newltchars
.t_dsuspc
== '\377')
while (ioctl(1, TIOCSLTC
, (char *)&oldltchars
) < 0 && errno
== EINTR
)
/*** signal handlers ***/
(void) signal(SIGINT
, onint
);
clearin(); /* flush input queue */
/* restore old terminal state */
(void) signal(signo
, SIG_DFL
);
(void) sigblock(sigmask(SIGALRM
)|sigmask(SIGINT
));
(void) sigsetmask(sigblock(0) & ~sigmask(signo
));
(void) kill(0, signo
); /* stop here until continued */
(void) signal(signo
, onstop
);
/* restore our special terminal state */
winch(); /* get current window size and redraw screen */
(void) sigsetmask(sigblock(0) & ~(sigmask(SIGALRM
)|sigmask(SIGINT
)));
/*** stolen from rfuncs2.c and modified ***/
(void) fseek(fp
, artbody
, 0);
if (strlen(to
) > BUFLEN
) {
msg("Command name too long");
(void) strcpy(temp
, "/tmp/vnXXXXXX");
if ((flags
& OVWRITE
) == 0) {
if ((ufp
= fopen(fname
, (flags
& OVWRITE
) == 0? "a" : "w")) == NULL
) {
msg("Cannot open %s", fname
);
* 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.
fprintf(ufp
, "\001\001\001\001\n");
h
->subtime
= cgtdate(h
->subdate
);
fprintf(ufp
, "From %s %s", replyname(h
), ctime(&h
->subtime
));
putc('\n', ufp
); /* force blank line at end (ugh) */
msg("error in writing temp file, maybe disk full?");
(void) sprintf(prog
, "(%s)<%s", to
+ 1, fname
);
err
? "ERROR WHILE WRITING ": "",
(flags
&OVWRITE
)? "written":
isnew
? "created" : "appended");
/* If we got an error, screen may be messed. E.g. 4.2BSD
* writes "disk full" messages to the user's tty.
(void) fseek(fp
, saveoff
, 0);
if (STRNCMP(ACTIVE
,"/tmp/", 5) == 0)
(void) unlink(active_name());
if (ospeed
) { /* is == 0, we haven't been in raw mode yet */