vn 12/86 (part 3 of 4) - reader.c / newsrc.c

Bob Mcqueer bobm at rtech.UUCP
Fri Jan 2 05:48:32 AEST 1987


Bob McQueer
{amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm

cut here
-----------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	reader.c
#	newsrc.c
# This archive created: Thu Jan  1 11:15:26 1987
export PATH; PATH=/bin:$PATH
echo shar: extracting "'reader.c'" '(18610 characters)'
if test -f 'reader.c'
then
	echo shar: will not over-write existing file "'reader.c'"
else
cat << \SHAR_EOF > 'reader.c'
/*
** vn news reader.
**
** reader.c - article reading interface - "more" like.
**
** see copyright disclaimer / history in vn.c source file
*/

#include <stdio.h>
#include <sys/types.h>
#include "tty.h"
#include "config.h"
#include "vn.h"
#include "head.h"
#include "reader.h"

#define PERTAB 8	/* tab expansion factor */
#define BACKTRACK 24

extern char *Printer,*Editor,*Mailer,*Poster,*Orgdir,*Savefile,*Savedir,*Ccfile;
extern int L_allow;
extern int C_allow;
extern int Rot;
extern int Headflag;
extern int Digest;
extern char *No_msg;
extern char *Roton_msg;
extern char *Rotoff_msg;
extern char *Hdon_msg;
extern char *Hdoff_msg;

extern char *T_head, *FT_head, *N_head, *L_head, *RT_head, *DIS_head;
extern char *TO_head, *F_head, *P_head, *M_head, *R_head;

extern char Cxrtoi[], Cxitor[];

static FILE *Fpread;
static char *Fname;
static char *Lookahead;
static int Rlines;
static int Hlines;

#ifdef ADDRMUNGE
static int Newaddr;
#endif

/*
	readstr routine is the "funnel" to the reading state,
	and controls signal setting.  Some "session" context is passed
	along to allow jumping back to a different display

	WARNING:

	NOTHING below readstr should call strtok()
*/

readstr (s,crec,highrec,count)
char *s;
int *crec, *highrec;
int count;
{
	char *fnext, *strtok();
	int pc;
	Fname = strtok(s,LIST_SEP);
	if (Fname != NULL)
	{
		term_set (ERASE);
		sig_set (BRK_READ,&Fpread);
		fnext = strtok(NULL,LIST_SEP);
		while (Fname != NULL && readfile(fnext,&pc) >= 0)
		{
			if (Digest)
				unlink (Fname);
			Fname = fnext;
			fnext = strtok (NULL,LIST_SEP);
		}
		if (Digest && Fname != NULL)
			unlink (Fname);
		if (pc != 0)
			forward (pc, crec, highrec);
		else
		{
			*crec += count;
			if (*crec >= *highrec)
				*crec = *highrec - 1;
		}
		sig_set (BRK_RFIN);
		show ();
		term_set (MOVE, 0, *crec);
	}
	else
	{
		preinfo ("%s",No_msg);
		term_set (MOVE, 0, *crec);
	}
}

/*
	readfile presents article:
		sn - name of next article, NULL if last.
		pages - pages to advance on return, if applicable
	returns 0 for "continue", <0 for "quit"
*/
static readfile (sn,pages)
char *sn;
int *pages;
{
	FILE *fopen();
	int lines,percent,artlin;
	long rew_pos, ftell();
	char c,  buf[RECLEN], mid[RECLEN], ngrp[RECLEN], dist[RECLEN];
 	char from[RECLEN], title[RECLEN], flto[RECLEN], reply[RECLEN];
 	char pstr[24], dgname[48], getpgch(), *index(), *digest_extract();
	char *tgetstr();

	*pages = 0;

	term_set(ERASE);

	if (Digest)
	{
		lines = atoi(Fname);
		if ((Fname = digest_extract(dgname,lines)) == NULL)
		{
			printf ("couldn't extract article %d",lines);
			return (0);
		}
	}

	if ((Fpread = fopen(Fname,"r")) == NULL)
	{
		printf ("couldn't open article %s",Fname);
		return (0);
	}

	Hlines = gethead (mid, from, title, ngrp, flto, reply, dist, &artlin);
	printf (ANFORM,Fname,Cxrtoi[PG_HELP]);
	lines = 1;
	rew_pos = ftell(Fpread);
	if (Headflag)
	{
		rewind(Fpread);
		Rlines = 0;
	}
	else
	{
		/* use do_out to guard against control sequences */
		Rlines = Hlines;
		sprintf (buf,"%s%s\n",T_head,title);
		lines += do_out(buf,1);
		sprintf (buf,"%s%s\n",F_head,from);
		lines += do_out(buf,1);
		if (index(ngrp,',') != NULL)
		{
			sprintf (buf,"%s%s\n",N_head,ngrp);
			lines += do_out(buf,1);
		}
		if (*flto != '\0')
		{
			sprintf (buf,"%s%s\n",FT_head,flto);
			lines += do_out(buf,1);
		}
		printf ("%s%d\n",L_head,artlin);	/* no controls */
		++lines;
	}

	/* will return out of outer while loop */
	Lookahead = NULL;
	while (1)
	{
		/*
		** lines counts folded lines from do_out.
		** globals Hlines and Rlines refer to records.
		** If Lookahead is null after this loop, we've
		** hit EOF.
		*/
		lines += do_out(Lookahead,L_allow-lines);
		while (1)
		{
			if (Lookahead == NULL)
			{
				if (fgets(buf,RECLEN-1,Fpread) == NULL)
					break;
				Lookahead = buf;
				if (Rot != 0 && Rlines >= Hlines)
					rot_line(buf);
				++Rlines;
			}
			if (lines >= L_allow)
				break;
			lines += do_out(buf,L_allow-lines);
		}

		if (Lookahead != NULL)
		{
			/*
			** calculation is truncated rather than rounded,
			** so we shouldn't get "100%".  Subtract 2 for
			** 1 line lookahead and empty line at beginning
			** of article.
			*/
			if (Headflag)
				percent = ((Rlines-2)*100)/(artlin+Hlines);
			else
				percent = ((Rlines-Hlines-2)*100)/artlin;
			sprintf (pstr,PAGE_MID,percent);
		}
		else
		{
			if (sn == NULL)
				strcpy (pstr,PAGE_END);
			else
			strcpy (pstr,PAGE_NEXT);
		}
		c = getpgch(pstr,mid,from,reply,title,ngrp,flto,dist);

		/*
			handle user input:
			CAUTION!!  return cases must close Fpread.
		*/
		switch (c)
		{
		case PG_NEXT:
			fclose (Fpread);
			return (0);
		case PG_FLIP:
			*pages = 1;	/* fall through */
		case PG_QUIT:
			fclose (Fpread);
			return (-1);
		case PG_REWIND:
			if (Headflag)
			{
				Rlines = 0;
				rewind (Fpread);
			}
			else
			{
				fseek (Fpread,rew_pos,0);
				Rlines = Hlines;
			}
			Lookahead = NULL;
			lines = 2 - RECBIAS;
			break;
		case PG_SEARCH:
			searcher(buf);
			lines = 2 - RECBIAS;
			lines += do_out(buf,L_allow-lines);
			break;
		case PG_WIND:
			fseek (Fpread,0L,2);
			lines = 2 - RECBIAS;
			Lookahead = NULL;
			break;
		case PG_STEP:
			if (Lookahead == NULL)
			{
				fclose (Fpread);
				return (0);
			}
			lines = L_allow - 1;
			break;
		default:
			if (Lookahead == NULL)
			{
				fclose (Fpread);
				return (0);
			}
			lines = 2 - RECBIAS;
			break;
		}
	}
}

/*
	gethead obtains subject, reply, message id, from, lines, newsgroup and
	followup-to lines of article for later use in mailing replies and
	posting followups, does not rewind, but leaves file at end of header
	lines.  Returns number of header lines.
*/
static gethead (mid, from, title, ngrp, flto, reply, dist, lin)
char *mid, *from, *title, *ngrp, *flto, *reply, *dist;
int *lin;
{
	int count;
	char buf [RECLEN], *index();
	long pos,ftell();

#ifdef ADDRMUNGE
	Newaddr = 1;
#endif

	*lin = 0;
	*dist = *mid = *from = *title = *ngrp = *flto = *reply = '\0';

	/* for conditional is abnormal - expected exit is break */
	for (count = 0; count < HDR_LINES && fgets(buf,RECLEN-1,Fpread) != NULL; ++count)
	{

		/* reset position and bail out at first non-header line */
		if (index(buf,':') == NULL)
		{
			pos = ftell(Fpread);
			pos -= strlen(buf);
			fseek (Fpread,pos,0);
			break;
		}

#ifdef MAILSMART
		if (strncmp(buf,RT_head,RTHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			strcpy (reply,buf+RTHDLEN);
			continue;
		}
#else
		if (strncmp(buf,P_head,PHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			strcpy (reply,buf+PHDLEN);
			continue;
		}
#endif
		if (strncmp(buf,DIS_head,DISHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			strcpy (dist,buf+DISHDLEN);
			continue;
		}
		if (strncmp(buf,M_head,MHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			strcpy (mid,buf+MHDLEN);
			continue;
		}
		if (strncmp(buf,F_head,FHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			strcpy (from,buf+FHDLEN);
			continue;
		}
		if (strncmp(buf,T_head,THDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			strcpy (title,buf+THDLEN);
			continue;
		}
		if (strncmp(buf,N_head,NHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			strcpy (ngrp,buf+NHDLEN);
			continue;
		}
		if (strncmp(buf,FT_head,FTHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			strcpy (flto,buf+FTHDLEN);
			continue;
		}
		if (strncmp(buf,L_head,LHDLEN) == 0)
		{
			buf [strlen(buf)-1] = '\0';
			*lin = atoi(buf+LHDLEN);
			continue;
		}
	}
#ifdef MAILSMART
	if (*reply == '\0')
		strcpy(reply,from);
#endif
	return (count);
}

/*
	getpgch prints prompt and gets command from user
	handles "mail", "save" and "followup" internally
	as well as flag resets.
*/
static char getpgch(prompt,mid,from,reply,title,ngrp,flto,dist)
char *prompt, *mid, *from, *reply, *title, *ngrp, *flto, *dist;
{
	char c;
	int ic;
	term_set (ONREVERSE);
	printf("%s\015",prompt);
	term_set (OFFREVERSE);
	while ((ic=getnoctl()) != EOF)
	{
		switch (c = Cxitor[ic])
		{
		case SETROT:
			term_set (ZAP,0,PPR_MAX);
			if (Rot == 0)
			{
				Rot = 13;
				printf ("%s\n",Roton_msg);
			}
			else
			{
				Rot = 0;
				printf ("%s\n",Rotoff_msg);
			}
			if (Lookahead != NULL && Rlines > Hlines)
				rot_line(Lookahead);
			break;
		case HEADTOG:
			term_set (ZAP,0,PPR_MAX);
			if (Headflag)
			{
				Headflag = FALSE;
				printf ("%s\n",Hdoff_msg);
			}
			else
			{
				Headflag = TRUE;
				printf ("%s\n",Hdon_msg);
			}
			break;
		case PG_HELP:
			term_set (ZAP,0,PPR_MAX);
			help_pg ();
			break;
		case PG_REPLY:
			mail (reply,title,from);
			break;
		case PG_FOLLOW:
			followup (mid,title,ngrp,flto,from,dist);
			break;
		case SAVE:
			saver ();
			break;
		case PRINT:
			printr ();
			break;
		default:
			term_set (ZAP,0,PPR_MAX);
			return (c);
		}

		term_set (ONREVERSE);
		printf("%s\015",prompt);
		term_set (OFFREVERSE);
	}
	term_set (ZAP,0,PPR_MAX);
	return (c);
}

/*
	save article
	Like the savestr routine, it "loses" some storage every time
	the user specifies a new file, but this should not be significant
*/
static saver ()
{
	char *fn,cmd[RECLEN],*str_store(),*rprompt();

	tty_set (SAVEMODE);
	sprintf (cmd,SAVFORM,Savefile);
	fn = rprompt(cmd,cmd);
	if (fn != NULL)
		Savefile = str_store(fn);
	if (*Savefile != '/' && *Savefile != '$')
		sprintf (cmd,"cat %s >>%s/%s",Fname,Savedir,Savefile);
	else
		sprintf (cmd,"cat %s >>%s",Fname,Savefile);
	system (cmd);
	tty_set (RESTORE);
}

/*
	invoke editor on new temp file, mail using reply line,
	possibly first allowing user to overide the reply (not INLETTER)
*/
static mail (p, t, f)
char *p, *t, *f;
{
	char *new, fn[L_tmpnam], cmd [RECLEN+60], *rprompt ();
	FILE *fp, *fopen();

	tmpnam (fn);
	if ((fp = fopen(fn,"w")) == NULL)
		printex ("can't open %s\n",fn);

	if ((new = index(p, '(')) != NULL)
		*new = '\0';	/* a poor way of deleting comments */

#ifdef ADDRMUNGE
	if (Newaddr)
	{
		Newaddr = 0;
		ADDRMUNGE(p);
	}
#endif

	if (strncmp(t, FPFIX, FPFLEN) == 0)
		t += FPFLEN;	/* don't add multiple Re:s */
#ifdef INLETTER
 	fprintf (fp,"%s%s\n%s%s%s\n\n%s:\n", TO_head, p, T_head, FPFIX, t, f);
#else
	fprintf (fp,"%s%s%s\n\n%s:\n", T_head, FPFIX, t, f);
#endif

	edcopy (fp);
	fclose (fp);
	tty_set (SAVEMODE);

#ifndef INLETTER
	sprintf (cmd,"ADDRESS: %s\nreturn to accept, or input new address: ",p);
	if ((new = rprompt(cmd,cmd)) != NULL)
		strcpy (p,new);
#endif

	sprintf (cmd,"%s %s", Editor, fn);
	chdir (Orgdir);
	system (cmd);
	cd_group ();
	new = rprompt ("still want to mail it ? ",cmd);
	if (new != NULL && (*new == 'y' || *new == 'Y'))
	{
#ifndef INLETTER
		sprintf (cmd,"%s '%s' <%s", Mailer, p, fn);
#else
		sprintf (cmd,"%s <%s", Mailer, fn);
#endif
		system (cmd);
		printf ("given to mailer\n");
	}
	else
		printf ("not mailed\n");
	unlink (fn);
	tty_set (RESTORE);
	term_set (RESTART);
}

/*
	post a followup article, invoking editor for user after creating
	new temp file.  remove after posting.  Hack in ".followup" if posting
	newsgroup ends in ".general" - similar hack for preventing mod &
	announce groups - should really be more thorough and parse the
	whole string.  User can change, anyway.
*/
static followup (m,t,n,ft,oa,dist)
char *m, *t, *n, *ft, *oa, *dist;
{
	char fn[L_tmpnam], *new, cmd [RECLEN], *rprompt();
	FILE *f, *fopen();
	char *rindex();

	if (*ft != '\0')
		strcpy (cmd,ft);
	else
		strcpy (cmd,n);
	new = rindex(cmd,'.');
	if (new != NULL && strcmp(new,".general") == 0)
		strcpy (new,".followup");
	if ( strncmp(cmd, "mod.", 4) == 0 || strcmp(new, ".announce") == 0)
	{
		term_set (ONREVERSE);
		printf("Cannot post a follow-up to \"%s\", reply with mail to moderator\007\n",
			cmd);
		term_set (OFFREVERSE);
		return;
	}

	tmpnam (fn);
	if ((f = fopen(fn,"w")) == NULL)
		printex ("can't open %s\n",fn);

	if (strncmp(t, FPFIX, FPFLEN) == 0)
		t += FPFLEN;	/* don't add multiple Re:s */
	fprintf (f,"%s%s%s\n%s%s\n%s%s\n",T_head,FPFIX,t,N_head,cmd,R_head,m);
	if (*dist != '\0')
		fprintf(f,"%s%s\n",DIS_head,dist);
	fprintf (f,"\nin article %s, %s says:\n",m,oa);
	edcopy (f);
	fclose (f);
	tty_set (SAVEMODE);
	sprintf (cmd,"%s %s", Editor, fn);
	chdir (Orgdir);
	system (cmd);
	cd_group ();
	new = rprompt("still want to post it ? ",cmd);
	if (new != NULL && (*new == 'y' || *new == 'Y'))
	{
		sprintf (cmd,"%s <%s", Poster, fn);
		system (cmd);
		printf ("given to posting program\n");
		save_article (fn);
	}
	else
		printf ("not posted\n");
	unlink (fn);
	tty_set (RESTORE);
	term_set (RESTART);
}

/*
	get user buffer, return whitespace delimited token
	without using strtok().  buffer is allowed to overwrite
	prompt string.
*/
static char *
rprompt(s,buf)
char *s,*buf;
{
	printf("%s",s);
	fgets (buf,RECLEN-1,stdin);
	while (*buf == ' ' || *buf == '\t')
		++buf;
	if (*buf == '\n' || *buf == '\0')
		return (NULL);
	for (s = buf; *s != ' ' && *s != '\t' && *s != '\n' && *s != '\0'; ++s)
		;
	*s = '\0';
	return (buf);
}

/*
	edcopy copies article to file which user is editting for
	a reply or followup, so it may be referenced.  It places
	ED_MARK in the left hand margin.
*/
edcopy(fp)
FILE *fp;
{
	long current;
	char buf[RECLEN];
	int i;

	/* save position, rewind and skip over header lines */
	current = ftell(Fpread);
	rewind (Fpread);
	for (i=0; i < HDR_LINES; ++i)
	{
		if (fgets(buf,RECLEN-1,Fpread) == NULL)
			break;
		if (strncmp(buf,L_head,LHDLEN) == 0)
			break;
	}

	/* if line already begins with ED_MARK, forget about the space */
	while (fgets(buf,RECLEN-1,Fpread) != NULL)
	{
		if (buf[0] == ED_MARK)
			fprintf(fp,"%c%s",ED_MARK,buf);
		else
			fprintf(fp,"%c %s",ED_MARK,buf);
	}

	/* restore position */
	fseek(Fpread,current,0);
}

/*
	help menus
*/
static help_pg()
{
	h_print (Cxrtoi[PG_NEXT],HPG_NEXT);
	h_print (Cxrtoi[PG_QUIT],HPG_QUIT);
	h_print (Cxrtoi[PG_FLIP],HPG_FLIP);
	h_print (Cxrtoi[PG_REWIND],HPG_REWIND);
	h_print (Cxrtoi[PG_WIND],HPG_WIND);
	h_print (Cxrtoi[PG_SEARCH],HPG_SEARCH);
	h_print (Cxrtoi[PG_STEP],HPG_STEP);
	h_print (Cxrtoi[PG_REPLY],HPG_REPLY);
	h_print (Cxrtoi[PG_FOLLOW],HPG_FOLLOW);
	h_print (Cxrtoi[SAVE],HPG_SAVE);
	h_print (Cxrtoi[PRINT],HPG_PRINT);
	h_print (Cxrtoi[SETROT],HPG_ROT);
	h_print (Cxrtoi[HEADTOG],HPG_HEAD);
	h_print (Cxrtoi[PG_HELP],HPG_HELP);
	printf ("%s\n",HPG_DEF);
}

rot_line (s)
unsigned char *s;
{
	for ( ; *s != '\0'; ++s)
	{
		if (*s >= 'A' && *s <= 'Z')
		{
			*s += Rot;
			if (*s > 'Z')
				*s -= 26;
			continue;
		}
		if (*s >= 'a' && *s <= 'z')
		{
			*s += Rot;
			if (*s > 'z')
				*s -= 26;
		}
	}
}

/*
** output record.  folds record to terminal width on word boundaries,
** returning number of lines output.  If limit is reached, remainder
** of buffer waiting to be output is returned.  Processes several
** special characters:
**	form-feed - return "lim" lines so we stop on that line
**	tabs - counts "expanded" width
**	backspace - assumes they work, -1 width unless in first col.
**	bell - pass through with zero width
**	newline - end of record.
**	del - turns into '_'
**	other control - 'A' - 1 added ('01' = ctl-A).  Makes escape = "[".
**		(prevents "letter bombs" containing inappropriate control
**			sequences for the terminal).
**
** Sets Lookahead pointer to remainder of line or NULL.
*/
static do_out(s,lim)
char *s;
int lim;
{
	int len,i;
	char cs,*word,*start;

	Lookahead = NULL;
	if (s == NULL)
		return(0);
	len = 0;
	start = word = s;

	/*
	** NOTE: "normal" return is buried inside switch, at newline
	** ending record
	*/
	for (i=0; i < lim; ++i)
	{
		for ( ; len < C_allow; ++s)
		{
			switch (*s)
			{
			case '\n':
				*s = '\0';	/* fall through */
			case '\0':
				printf("%s\n",start);
				return(i+1);
			case '\t':
				len = ((len/PERTAB)+1)*PERTAB;
				word = s;
				break;
			case '\b':
				if (len > 0)
					--len;
				break;
			case '\014':
				*s = ' ';
				i = lim-1;	/* fall through */
			case ' ':
				word = s+1;
				++len;
				break;
			case '\177':
				*s = '_';
				++len;
				break;
			default:
				if (*s < ' ')
					*s += 'A' - 1;
				++len;		/* fall through */
			case '\07':
				break;
			}
		}
		cs = *s;
		*s = '\0';
		if ((len = strlen(word)) < BACKTRACK)
		{
			*s = cs;
			s = word;
			cs = *s;
			*s = '\0';
		}
		else
			len = 0;
		printf("%s\n",start);
		start = s;
		*s = cs;
	}
	Lookahead = start;
	return(lim);
}

save_article(tempfname)
char *tempfname;
{
	FILE *in, *out;
	int c;
	time_t timenow, time();
	char *today, *ctime();


	if ((in = fopen(tempfname, "r")) == NULL)
		return;
	if ((out = fopen(Ccfile, "a")) == NULL)
	{
	    fclose(in);
	    return;
	}
	timenow = time((time_t)0);
	today = ctime(&timenow);
	fprintf(out,"From vn %s",today);
	while ((c=getc(in)) != EOF)
		putc(c, out);
	putc('\n', out);
	fclose(in);
	fclose(out);
	printf ("a copy has been saved in %s\n", Ccfile);
}

/*
	send article to printer
*/
static printr ()
{
	char cmd[RECLEN];

	tty_set (SAVEMODE);
	printf("Sent to printer\n");
	sprintf (cmd,"%s %s 2>/dev/null",Printer,Fname);
	system (cmd);
	tty_set (RESTORE);
}

/*
	search article for specified search pattern, returning the line on which
		it is found in buf, a null buffer otherwise. The input file will
		be positioned either after the line on which the pattern is
		found, or unaaltered if match fails.
*/
searcher (buf)
char	*buf;
{
	static char	searchstr[RECLEN] = "";
	char	lasave[RECLEN];
	char	*s, *reg, *rprompt(), *regcmp(), *regex();
	long	current;
	int	orlines;

	/* save position, then request search pattern */
	current = ftell(Fpread);
	orlines = Rlines;

 	tty_set (SAVEMODE);
 	sprintf (lasave,SEARCHFORM,searchstr);
	s = rprompt(lasave,lasave);
	tty_set (RESTORE);
	if (s != NULL)
		strcpy(searchstr, lasave);
 
	/* Now compile the search string */
	if(( reg = regcmp(searchstr, (char *)0)) == NULL) {
		printf("Invalid search string \"%s\"\n", searchstr);
		*buf = '\0';
		return;
	}

	/* try lookahead buffer first */
	if (Lookahead != NULL && regex(reg,Lookahead) != NULL)
	{
		strcpy(buf,Lookahead);
		regfree(reg);
		return;
	}

	/* Lookahead can point into buf */
	if (Lookahead != NULL)
		strcpy(lasave,Lookahead);

	/* now start reading lines, rotating if necessary and do search */
	while (fgets(buf,RECLEN-1,Fpread) != NULL)
	{
		if (Rot != 0 && Rlines >= Hlines)
			rot_line(buf);
		++Rlines;
		if( regex(reg, buf) != NULL ){	/* Got it */
			term_set (ONREVERSE);
			printf("\n\tSkipping ....\n\n");
			term_set (OFFREVERSE);
			regfree(reg);
			return;
		}
	}

	/* no dice, so restore position */
	regfree(reg);
	term_set (ONREVERSE);
	printf("Cannot find string \"%s\" in remainder of article\007\n",
		searchstr);
	term_set (OFFREVERSE);
	fseek(Fpread,current,0);
	Rlines = orlines;
	if (Lookahead != NULL)
		strcpy(buf,lasave);
	else
		*buf = '\0';
	return(0.0);
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'newsrc.c'" '(11576 characters)'
if test -f 'newsrc.c'
then
	echo shar: will not over-write existing file "'newsrc.c'"
else
cat << \SHAR_EOF > 'newsrc.c'
/*
** vn news reader.
**
** newsrc.c - routines to deal with the newsrc file
**
** see copyright disclaimer / history in vn.c source file
*/

#include <stdio.h>
#include <ctype.h>
#include "config.h"
#include "tty.h"
#include "vn.h"

extern NODE **Newsorder;
extern char *Onews, *Newsrc;
extern int Ncount, Lrec, C_allow;
extern int Ntopt, Nntopt, Nwopt, Nnwopt;
extern char *Topt[], *Negtopt[], *Wopt[], *Negwopt[];
extern int Nounsub, Listfirst;

/*
	global flags signifying options set
*/
#define GF_ALL 1	/* -x option - scan everything */
#define GF_SPEC 2	/* -n option(s) - user specified groups */
#define GF_OVER 4	/* command line specification - overide marks */

static char *Options[OPTLINES];
static int New_idx, Max_name, Optlines;
static unsigned Gflags = 0;

/*
	routines for dealing with the .newsrc file and options
*/

/*
	command name argument is already omitted from argv argc in this
	routine.  Only the option arguments are present.  We process
	options before we scan the rest of .newsrc, which redoes Newsorder,
	ie. we don't clobber Ncount until options are processed.
*/
scan_newsrc (argc,argv)
int argc;
char **argv;
{
	FILE *fp, *fopen();
	static char marks[] =
	{ 
		NEWS_ON, NEWS_OFF, '\0' 
	};
	char *str_store ();
	int line, len, num;
	char buf [RECLEN], trail, optpflag, submark, *fret, *ptr, *strpbrk(), *strtok();


	/* initialize hash table, open temp file, fill table with active articles */
	hashinit ();
	fill_active ();
	temp_open();

	if (argc > 0)
	{
		Gflags |= GF_OVER;
		arg_opt(argc,argv);
		optpflag = 'y';
	}
	else
		optpflag = 'n';

	if ((fp = fopen (Newsrc,"r")) == NULL)
		printex ("can't open %s for reading",Newsrc);

	Optlines = 0;

	for (line = 1; (fret = fgets(buf,RECLEN-1,fp)) != NULL && emptyline(buf) == 1; ++line)
		;
	if (fret != NULL && strncmp (buf,"options",7) == 0)
	{
		Options[0] = str_store(buf);
		Optlines = 1;
		trail = buf [strlen(buf)-2];
		for ( ; (fret = fgets(buf,RECLEN-1,fp)) != NULL; ++line)
		{
			if (trail != '\\' && buf[0] != ' ' && buf[0] != '\t')
				break;
			if (Optlines >= OPTLINES)
				printex ("%s - too many option lines (%d allowed)",Newsrc,OPTLINES);
			Options[Optlines] = str_store(buf);
			++Optlines;
			if ((len = strlen(buf)) >= 2 && buf[len-2] != '\\')
				trail = buf[len-2];
			else
				trail = '\0';
		}
	}

	/* do the options from the newsrc file if there weren't command line args */
	if (Optlines > 0 && optpflag == 'n')
		newsrc_opt ();

	Ncount = 0;

	for ( ; fret != NULL; ++line, fret = fgets(buf,RECLEN-1,fp))
	{
		if (emptyline(buf) == 1)
			continue;
		if ((ptr = strpbrk(buf,marks)) == NULL)
		{
			fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n",
			line,Newsrc,buf);
			continue;
		}
		submark = *ptr;
		*ptr = '\0';
		++ptr;
		num = 0;
		for (ptr = strtok(ptr," ,-\n"); ptr != NULL; ptr = strtok(NULL," ,-\n"))
		{
			len = atoi (ptr);
			for ( ; *ptr >= '0' && *ptr <= '9'; ++ptr)
				;
			if (*ptr != '\0' || len < num)
			{
				num = -1;
				fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n",
				line,Newsrc,buf);
				break;
			}
			num = len;
		}
		if (num < 0)
			continue;
		chkgroup (buf,submark,num);
	}
	fclose (fp);

	/* now take care of groups not specified in .newsrc */
	art_active();

	/* free up the option string storage */
	for (num=0; num < Ntopt; ++num)
		regfree (Topt[num]);
	for (num=0; num < Nwopt; ++num)
		regfree (Wopt[num]);
	for (num=0; num < Nntopt; ++num)
		regfree (Negtopt[num]);
	for (num=0; num < Nnwopt; ++num)
		regfree (Negwopt[num]);
	Ntopt = Nwopt = Nntopt = Nnwopt = 0;
}

static emptyline(s)
char *s;
{
	while (isspace(*s))
		++s;
	if (*s == '\0')
		return (1);
	return (0);
}

/*
	fill hash table from active news group list
	temporarily makes "Newsorder" active list order.
	This is needed to be able to process options
	before scanning user order.
*/
static fill_active ()
{
	FILE *f,*fopen ();
	char *nread, act_rec[RECLEN], *strtok();
	int num,lownum;

	Max_name = 0;
	if ((f = fopen (ACTFILE,"r")) == NULL)
		printex ("couldn't open %s\n",ACTFILE);
	while (fgets(act_rec, RECLEN-1, f) != NULL)
	{
		if (strtok (act_rec," \n") == NULL)
			continue;
		nread = strtok (NULL, " \n");
		if (nread != NULL)
			num = atoi(nread);
		else
			num = 0;
		nread = strtok (NULL, " \n");
		if (nread != NULL)
			lownum = atoi(nread);
		else
			lownum = 0;
		if (lownum > 0)
			--lownum;
		if (strlen(act_rec) > Max_name)
			Max_name = strlen(act_rec);
		hashenter (act_rec, num, lownum);
	}

	/* construct initial Newsorder */
	entry_order();

	fclose (f);
}

/*
	check active newsgroups not mentioned in NEWSRC file
	(FLG_SCAN not set)
*/
static art_active ()
{
	char act_rec[RECLEN], *strtok();
	NODE *ptr,*hashfind();
	FILE *f,*fopen();
	if ((f = fopen (ACTFILE,"r")) == NULL)
		printex ("couldn't open %s\n",ACTFILE);
	New_idx = Ncount;
	while (fgets(act_rec, RECLEN-1, f) != NULL)
	{
		if (strtok (act_rec," \n") == NULL)
			continue;
		if ((ptr = hashfind (act_rec)) == NULL)
			printex("%s - unexpected hash table failure",act_rec);
		if ((ptr->state & FLG_SCAN) == 0)
			chkgroup (ptr->nd_name, NEWS_ON, 0);
	}
}

/*
	check group for new articles:
	s - group
	c - subscription indicator from NEWSRC
	n - number read
*/
static chkgroup (s,c,n)
char *s,c;
int n;
{
	NODE *ptr, *hashfind();
	int lold,lowart;
	lold = Lrec;
	if ((ptr = hashfind(s)) != NULL && (ptr->state & FLG_SCAN) == 0)
	{
		Newsorder [Ncount] = ptr;
		++Ncount;
		ptr->pages = 0;
		ptr->state |= FLG_SCAN;
		if (c == NEWS_ON)
			ptr->state |= FLG_SUB;
		/* if "read" more than exist reset to zero */
		if (n > ptr->art)
			n = 0;
		lowart = ptr->rdnum;
		if (n < ptr->rdnum)
			n = ptr->rdnum;
		ptr->orgrd = ptr->pgrd = ptr->rdnum = n;
		ptr->pgshwn = 0L;

		/*
		** scan decision is rather complex, since GF_ALL setting
		** overides "n" value, GF_SPEC indicates FLG_SPEC flag used.
		** if GF_OVER set, FLG_SPEC overides subscription mark, else
		** FLG_SPEC AND subscribed is neccesary.
		*/
		if ((Gflags & GF_SPEC) != 0)
		{
			if ((ptr->state & FLG_SPEC) == 0)
				c = NEWS_OFF;
			else
			{
				if ((Gflags & GF_OVER) != 0)
					c = NEWS_ON;
			}
		}
		if ((Gflags & GF_ALL) != 0)
			n = lowart;
		if (c == NEWS_ON && ptr->art > n)
		{
			outgroup (s,n,ptr->art);
			if (lold != Lrec)
			{
				ptr->pnum = lold+1;
				ptr->pages = Lrec - lold;
				ptr->state |= FLG_PAGE;
			}
		}
	}
}

/*
	wr_newsrc writes the .newsrc file
*/
wr_newsrc ()
{
	FILE *fp,*fopen();
	NODE *p;
	char c;
	int i,rc;

	if (link(Newsrc,Onews) < 0)
		printex ("can't backup %s to %s before writing",Newsrc,Onews);

	if (unlink(Newsrc) < 0 || (fp = fopen(Newsrc,"w")) == NULL)
		printex ("can't open %s for writing (backed up in %s)",Newsrc,Onews);
	else
	{
		clearerr(fp);
		for (i=0; (rc = ferror(fp)) == 0 && i < Optlines; ++i)
			fprintf (fp,"%s",Options[i]);
		for (i=0; rc == 0 && i < Ncount; ++i)
		{
			p = Newsorder[i];
			if ((p->state & FLG_SUB) == 0)
				c = NEWS_OFF;
			else
				c = NEWS_ON;
#ifdef OLDRC
			fprintf (fp,"%s%c %d\n",p->nd_name,c,p->rdnum);
#else
			if (p->rdnum > 0)
				fprintf (fp,"%s%c 1-%d\n",p->nd_name,c,p->rdnum);
			else
				fprintf (fp,"%s%c 0\n",p->nd_name,c);
#endif
			rc = ferror(fp);
		}
		fclose (fp);
		if (rc != 0)
			printex ("write of %s failed, old copy stored in %s",Newsrc,Onews);
		else
			unlink (Onews);
	}
}

new_groups ()
{
	int i,wrem,w;
	char fs[24],c_end;
	if (New_idx >= Ncount || C_allow < (w = Max_name+1))
		return (0);
	term_set (ERASE);
	printf (NEWGFORM,Newsrc);
	sprintf (fs,"%%-%ds%%c",Max_name);
	wrem = C_allow;
	for (i=New_idx; i < Ncount; ++i)
	{
		if ((wrem -= w) < w)
		{
			wrem = C_allow;
			c_end = '\n';
		}
		else
			c_end = ' ';
		printf (fs,(Newsorder[i])->nd_name,c_end);
	}
	if ((++wrem) < C_allow)
		putchar ('\n');
	return (i-New_idx);
}

/*
	arg_opt must be called prior to option scanning, since
	it uses the options array.  This is a bit of a kludge,
	but it saves a bunch of work.  NOTE - no command name argument
*/
static arg_opt (argc,argv)
int argc;
char **argv;
{
	if (argc > OPTLINES)
		printex ("too many command line options (%d allowed)\n",OPTLINES);
	for (Optlines=0; Optlines < argc; ++Optlines)
	{
		Options[Optlines] = *argv;
		++argv;
	}
	newsrc_opt();
}

/*
	option setting routine:
	sets global flags: GF_ALL for -x option GF_SPEC for -n.
	sets up filter array for article scanning
*/
static newsrc_opt()
{
	int i;
	char curopt,tmp[RECLEN],*tok,*strtok(),*index();

	Nounsub = Listfirst = 0;
	Ntopt = Nwopt = Nnwopt = Nntopt = 0;
	curopt = '\0';
	for (i=0; i < Optlines; ++i)
	{
		strcpy(tmp,Options[i]);
		for (tok = strtok(tmp,",\\ \t\n"); tok != NULL; tok = strtok(NULL,",\\ \t\n"))
		{
			if (*tok != '-')
				do_opt (curopt,tok);
			else
			{
				for (++tok; index("nwt",*tok) == NULL; ++tok)
				{
					/* options with no strings */
					switch(*tok)
					{
					case 'S':
						Gflags &= ~GF_OVER;
						break;
					case '%':
						Listfirst = 1;
						break;
					case 'U':
						Nounsub = 1;
						break;
#ifdef OLDRC
					case 'i':
					/* Treat "-i" as synonym for "-x" */
#endif
					case 'x':
						Gflags |= GF_ALL;
					default:
						break;
					}
				}
				curopt = *tok;
				if (*(++tok) != '\0')
					do_opt (curopt,tok);
			}
		}
	}
}

/* do_opt is for options with strings attached */
static do_opt (opt,str)
char opt, *str;
{
	switch (opt)
	{
	case 'n':
		Gflags |= GF_SPEC;
		specmark(str);
		break;
	case 'w':
		specfilter (FIL_AUTHOR,str);
		break;
	case 't':
		specfilter (FIL_TITLE,str);
		break;
	default:
#ifdef OLDRC
		Gflags |= GF_SPEC;	/* Assume anything else is newsgroup */
		specmark(str);
#endif
		break;
	}
}

static specfilter (comp,str)
char comp,*str;
{
	char *regcmp();
	int *count;
	char **rex;

	/*
	** we may set rex one past end of array.  we will error before
	** referencing it if that's the case, however.
	*/
	if (*str == '!')
	{
		if (comp == FIL_TITLE)
		{
			count = &Nntopt;
			rex = Negtopt + *count;
		}
		else
		{
			count = &Nnwopt;
			rex = Negwopt + *count;
		}
		++str;
	}
	else
	{
		if (comp == FIL_TITLE)
		{
			count = &Ntopt;
			rex = Topt + *count;
		}
		else
		{
			count = &Nwopt;
			rex = Wopt + *count;
		}
	}
	if (*count >= NUMFILTER)
		printex ("too many %c options, %d allowed",comp,NUMFILTER);
	if ((*rex = regcmp(str,(char *) 0)) == NULL)
		printex ("%c option regular expression syntax: %s",comp,str);
	++(*count);
}

/*
	handle the newsgroup specification string.
	("all" convention - braack!!!)
*/
static specmark (s)
char *s;
{
	unsigned ormask,andmask;
	int i,len;
	char *ptr,*re,pattern[RECLEN],*regex(),*regcmp();

	if (*s == '!')
	{
		++s;
		ormask = 0;
		andmask = ~FLG_SPEC;
		if (*s == '\0')
			return;
	}
	else
	{
		ormask = FLG_SPEC;
		andmask = 0xffff;
	}

	/* convert "all" not bounded by alphanumerics to ".*". ".all" becomes ".*" */
	for (ptr = s; (len = findall(ptr)) >= 0; ptr += len+1)
	{
		if (len > 0 && isalnum (s[len-1]))
			continue;
		if (isalnum (s[len+3]))
			continue;
		if (len > 0 && s[len-1] == '.')
		{
			--len;
			strcpy (s+len,s+len+1);
		}
		s[len] = '.';
		s[len+1] = '*';
		strcpy (s+len+2,s+len+3);
	}

	/* now use regular expressions */
	sprintf (pattern,"^%s$",s);
	if ((re = regcmp(pattern,(char *) 0)) == NULL)
		printex ("n option regular expression syntax: %s",s);
	for (i=0; i < Ncount; ++i)
	{
		if (regex(re,(Newsorder[i])->nd_name) != NULL)
		{
			(Newsorder[i])->state |= ormask;
			(Newsorder[i])->state &= andmask;
		}
	}
	regfree (re);
}

static findall (s)
char *s;
{
	int len;
	for (len=0; *s != '\0'; ++s,++len)
	{
		if (*s == 'a' && strncmp(s,"all",3) == 0)
			return (len);
	}
	return (-1);
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0



More information about the Comp.sources.unix mailing list