v23i070: Complete reposting of TRN at patchlevel 1, Part11/14

Rich Salz rsalz at bbn.com
Sat Jan 5 08:05:59 AEST 1991


Submitted-by: Wayne Davison <0004475895 at mcimail.com>
Posting-number: Volume 23, Issue 70
Archive-name: trn/part11

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# Contents:  artsrch.c head.c mt-write.c mthreads.1 ngstuff.c sw.c
# Wrapped by rsalz at litchi.bbn.com on Thu Dec 27 11:34:10 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive 11 (of 14)."'
if test -f 'artsrch.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'artsrch.c'\"
else
  echo shar: Extracting \"'artsrch.c'\" \(8042 characters\)
  sed "s/^X//" >'artsrch.c' <<'END_OF_FILE'
X/* $Header: artsrch.c,v 4.3.3.1 90/07/21 20:13:13 davison Trn $
X *
X * $Log:	artsrch.c,v $
X * Revision 4.3.3.1  90/07/21  20:13:13  davison
X * Initial Trn Release
X * 
X * Revision 4.3.2.4  89/11/27  01:30:00  sob
X * Altered NNTP code per ideas suggested by Bela Lubkin
X * <filbo at gorn.santa-cruz.ca.us>
X * 
X * Revision 4.3.2.3  89/11/26  22:54:37  sob
X * Added new patches to make rrn faster.
X * 
X * Revision 4.3.2.2  89/11/26  22:20:57  sob
X * Added better NNTP support.
X * 
X * Revision 4.3.2.1  89/11/26  22:13:10  sob
X * Added support for NNTP
X * 
X * Revision 4.3  85/05/01  11:35:47  lwall
X * Baseline for release with 4.3bsd.
X * 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "search.h"
X#include "term.h"
X#include "util.h"
X#include "intrp.h"
X#include "bits.h"
X#include "kfile.h"
X#include "head.h"
X#include "final.h"
X#include "cheat.h"
X#ifdef SERVER
X#include "server.h"
X#endif
X#include "ng.h"
X#include "artio.h"
X#ifdef USETHREADS
X#include "rthreads.h"
X#include "ngdata.h"
X#endif
X#include "INTERN.h"
X#include "artsrch.h"
X
Xvoid
Xartsrch_init()
X{
X#ifdef ARTSEARCH
X#ifdef ZEROGLOB
X    init_compex(&sub_compex);
X    init_compex(&art_compex);
X#endif
X#endif
X}
X
X/* search for an article containing some pattern */
X
X#ifdef ARTSEARCH
Xint
Xart_search(patbuf,patbufsiz,get_cmd)
Xchar *patbuf;				/* if patbuf != buf, get_cmd must */
Xint patbufsiz;
Xint get_cmd;				/*   be set to FALSE!!! */
X{
X    char *pattern;			/* unparsed pattern */
X    register char cmdchr = *patbuf;	/* what kind of search? */
X    register char *s;
X    bool backward = cmdchr == '?' || cmdchr == Ctl('p');
X					/* direction of search */
X    COMPEX *compex;			/* which compiled expression */
X    char *cmdlst = Nullch;		/* list of commands to do */
X    int normal_return = SRCH_NOTFOUND;	/* assume no commands */
X    bool saltaway = FALSE;		/* store in KILL file? */
X    char howmuch;			/* search just the subjects */
X    bool doread;			/* search read articles? */
X    bool foldcase = TRUE;		/* fold upper and lower case? */
X
X    int_count = 0;
X    if (cmdchr == '/' || cmdchr == '?') {	/* normal search? */
X	if (get_cmd && buf == patbuf)
X	    if (!finish_command(FALSE))	/* get rest of command */
X		return SRCH_ABORT;
X	compex = &art_compex;
X	if (patbuf[1]) {
X	    howmuch = 0;
X	    doread = FALSE;
X	}
X	else {
X	    howmuch = art_howmuch;
X	    doread = art_doread;
X	}
X	s = cpytill(buf,patbuf+1,cmdchr);/* ok to cpy buf+1 to buf */
X	pattern = buf;
X	if (*pattern) {
X	    if (*lastpat)
X		free(lastpat);
X	    lastpat = savestr(pattern);
X	}
X	if (*s) {			/* modifiers or commands? */
X	    for (s++; *s && index("Kharc",*s); s++) {
X		if (*s == 'h')		/* scan header */
X		    howmuch = 1;
X		else if (*s == 'a')	/* scan article */
X		    howmuch = 2;
X		else if (*s == 'r')	/* scan read articles */
X		    doread = TRUE;
X		else if (*s == 'K')	/* put into KILL file */
X		    saltaway = TRUE;
X		else if (*s == 'c')	/* make search case sensitive */
X		    foldcase = FALSE;
X	    }
X	}
X	while (isspace(*s) || *s == ':')
X	    s++;
X	if (*s) {
X	    if (*s == 'm' || *s == 'M')
X		doread = TRUE;
X	    if (*s == 'k')		/* grandfather clause */
X		*s = 'j';
X	    cmdlst = savestr(s);
X	    normal_return = SRCH_DONE;
X	}
X	art_howmuch = howmuch;
X	art_doread = doread;
X	if (srchahead)
X	    srchahead = -1;
X    }
X    else {
X	register char *h;
X
X	howmuch = 0;			/* just search subjects */
X	doread = (cmdchr == Ctl('p'));
X	if (cmdchr == Ctl('n'))
X	    normal_return = SRCH_SUBJDONE;
X	compex = &sub_compex;
X	pattern = patbuf+1;
X	strcpy(pattern,": *");
X	h = pattern + strlen(pattern);
X	interp(h,patbufsiz - (h-patbuf),"%s");	/* fetch current subject */
X	if (cmdchr == 'K') {
X	    saltaway = TRUE;
X	    cmdchr = 'k';
X	}
X	if (cmdchr == 'k') {
X	    normal_return = SRCH_DONE;
X	    cmdlst = savestr("j");
X	    mark_as_read();		/* this article has this subject */
X	    if (!*h) {
X#ifdef VERBOSE
X		IF(verbose)
X		    fputs("\nCannot delete null subject.\n",stdout) FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    fputs("\nNull subject.\n",stdout) FLUSH;
X#endif
X		return SRCH_ABORT;
X	    }
X#ifdef VERBOSE
X	    else if (verbose)
X		printf("\nMarking subject \"%s\" as read.\n",h) FLUSH;
X#endif
X	}
X	else if (!srchahead)
X	    srchahead = -1;
X	h[24] = '\0';		/* compensate for notesfiles */
X	while (*h) {
X	    if (index("/\\[.^*$'\"",*h) != Nullch)
X		*h++ = '.';
X	    else
X		h++;
X	}
X#ifdef DEBUGGING
X	if (debug) {
X	    printf("\npattern = %s\n",pattern) FLUSH;
X	}
X#endif
X    }
X    if ((s = compile(compex,pattern,TRUE,foldcase)) != Nullch) {
X					/* compile regular expression */
X	printf("\n%s\n",s) FLUSH;
X	return SRCH_ABORT;
X    }
X#ifdef KILLFILES
X    if (saltaway) {
X	char saltbuf[LBUFLEN];
X
X	s = saltbuf;
X	sprintf(s,"/%s/",pattern);
X	s += strlen(s);
X	if (doread)
X	    *s++ = 'r';
X	if (howmuch==1)
X	    *s++ = 'h';
X	else if (howmuch==2)
X	    *s++ = 'a';
X	*s++ = ':';
X	if (!cmdlst)
X	    cmdlst = savestr("j");
X	safecpy(s,cmdlst,LBUFLEN-(s-saltbuf));
X	kf_append(saltbuf);
X    }
X#endif
X    if (cmdlst && index(cmdlst,'='))
X	normal_return = SRCH_ERROR;	/* listing subjects is an error? */
X    if (get_cmd) {
X	fputs("\nSearching...\n",stdout) FLUSH;
X					/* give them something to read */
X    }
X#ifdef USETHREADS
X    if (mode == 't') {
X	if (!cmdlst)
X	    cmdlst = savestr("+");	/* thread selector's default command */
X	if (unread_selector)
X	    doread = TRUE;
X	normal_return = SRCH_DONE;
X    }
X#endif
X    if (backward) {
X	if (cmdlst && art <= lastart)
X	    art++;			/* include current article */
X	if (doread)
X	    check_first(absfirst);
X    }
X    else {
X	if (art > lastart) {
X	    art = (doread ? absfirst : firstart);
X	    check_first(art--);
X	}
X	else if (cmdlst && art >= absfirst)
X	    art--;			/* include current article */
X    }
X    if (srchahead > 0) {
X	if (!backward)
X	    art = srchahead - 1;
X	srchahead = -1;
X    }
X    assert(!cmdlst || *cmdlst);
X    perform_cnt = 0;
X    for (;;) {
X	if (backward ?
X		(--art < absfirst || (!doread && art < firstart)) :
X		(++art > lastart)
X	  ) {			/* out of articles? */
X	    if (cmdlst)
X		free(cmdlst);
X	    return normal_return;
X	}
X	if (int_count) {
X	    int_count = 0;
X	    if (cmdlst)
X		free(cmdlst);
X	    return SRCH_INTR;
X	}
X	/*NOSTRICT*/
X	if (doread || !was_read(art)) {
X	    if (wanted(compex,art,howmuch)) {
X				    /* does the shoe fit? */
X		if (cmdlst) {
X		    if (perform(cmdlst,TRUE)) {
X			if (cmdlst)
X			    free(cmdlst);
X			return SRCH_INTR;
X		    }
X		}
X		else {
X		    if (cmdlst)
X			free(cmdlst);
X		    return SRCH_FOUND;
X		}
X	    }
X	    else if (!cmdlst && ! (art%50)) {
X		printf("...%ld",(long)art);
X		fflush(stdout);
X	    }
X	}
X    }
X}
X
X/* determine if article fits pattern */
X/* returns TRUE if it exists and fits pattern, FALSE otherwise */
X
Xbool
Xwanted(compex, artnum, scope)
XCOMPEX *compex;
XART_NUM artnum;
Xchar scope;
X{
X    if (!scope) {
X	char subj_buf[266];
X	
X#ifdef USETHREADS
X	if (ThreadedGroup)
X	    find_article(art);
X	if (p_art) {
X	    if (mode != 't')
X		strcpy(subj_buf, "Subject: ");
X	    else
X		*subj_buf = '\0';
X	    if (p_art->subject != -1)
X		strcat(subj_buf,subject_ptrs[p_art->subject]);
X	}
X	else
X#endif
X	{
X	    strcpy(subj_buf, "Subject: ");
X	    strncpy(subj_buf+9,fetchsubj(artnum,FALSE,FALSE),256);
X	}
X#ifdef DEBUGGING
X	if (debug & DEB_SEARCH_AHEAD)
X	    printf("%s\n",subj_buf) FLUSH;
X#endif
X	return execute(compex,subj_buf) != Nullch;
X    }
X#ifdef CACHESUBJ
X    else
X	fetchsubj(artnum,FALSE,FALSE);/* might as well get subject handy */
X#endif
X    
X#ifdef SERVER
X    if (scope == 1){
X	if (nntpopen(artnum,GET_HEADER) == Nullfp) /* we only need the header */
X	    return FALSE;
X    }
X    else
X#endif
X    if (artopen(artnum) == Nullfp)	/* ensure that article is open */
X
X	return FALSE;			/* if not, return NO MATCH */
X    scope--;
X    while (fgets(buf,LBUFLEN,artfp) != Nullch) {
X					/* for each line of article */
X	if (!scope && index(buf,':') == Nullch && *buf != ' ' && *buf != '\t')
X					/* if headers only and out of header */
X	    return FALSE;		/* say no go */
X	if (execute(compex,buf) != Nullch) {
X					/* does pattern matcher match? */
X	    return TRUE;		/* say Eureka */
X	}
X    }
X    return FALSE;			/* out of article, so no match */
X}
X#endif
X
END_OF_FILE
  if test 8042 -ne `wc -c <'artsrch.c'`; then
    echo shar: \"'artsrch.c'\" unpacked with wrong size!
  fi
  # end of 'artsrch.c'
fi
if test -f 'head.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'head.c'\"
else
  echo shar: Extracting \"'head.c'\" \(8722 characters\)
  sed "s/^X//" >'head.c' <<'END_OF_FILE'
X/* $Header: head.c,v 4.3.3.1 90/07/21 20:19:26 davison Trn $
X *
X * $Log:	head.c,v $
X * Revision 4.3.3.1  90/07/21  20:19:26  davison
X * Initial Trn Release
X * 
X * Revision 4.3.2.5  90/03/22  23:04:22  sob
X * Fixes provided by Wayne Davison <drivax!davison>
X * 
X * Revision 4.3.2.4  89/11/27  01:30:35  sob
X * Altered NNTP code per ideas suggested by Bela Lubkin
X * <filbo at gorn.santa-cruz.ca.us>
X * 
X * Revision 4.3.2.3  89/11/26  22:53:52  sob
X * Add new patches to make RRN be faster.
X * 
X * Revision 4.3.2.2  89/11/08  01:17:46  sob
X * Added changes to insure that this will compile for RN or RRN with no
X * changes to the source code.
X * 
X * Revision 4.3.2.1  89/11/06  00:37:18  sob
X * Added RRN support from NNTP 1.5
X * 
X * Revision 4.3.1.2  85/05/10  13:47:25  lwall
X * Added debugging stuff.
X * 
X * Revision 4.3.1.1  85/05/10  11:32:30  lwall
X * Branch for patches.
X * 
X * Revision 4.3  85/05/01  11:38:21  lwall
X * Baseline for release with 4.3bsd.
X * 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "artio.h"
X#include "bits.h"
X#ifdef SERVER
X#include "server.h"
X#endif
X#include "util.h"
X#include "INTERN.h"
X#include "head.h"
X
Xbool first_one;		/* is this the 1st occurance of this header line? */
X
Xstatic char htypeix[26] =
X    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
X
Xvoid
Xhead_init()
X{
X    register int i;
X
X    for (i=HEAD_FIRST+1; i<HEAD_LAST; i++)
X	htypeix[*htype[i].ht_name - 'a'] = i;
X}
X
X#ifdef DEBUGGING
Xdumpheader(where)
Xchar *where;
X{
X    register int i;
X
X    printf("header: %d %s", parsed_art, where);
X
X    for (i=0; i<HEAD_LAST; i++) {
X	printf("%15s %4d %4d %03o\n",htype[i].ht_name,
X	    htype[i].ht_minpos,
X	    htype[i].ht_maxpos,
X	    htype[i].ht_flags) FLUSH;
X    }
X}
X#endif
X
Xint
Xset_line_type(bufptr,colon)
Xchar *bufptr;
Xregister char *colon;
X{
X    char lc[LONGKEY+3];
X    register char *t, *f;
X    register int i, len;
X
X    if (colon-bufptr > LONGKEY+2)
X	return SOME_LINE;
X
X    for (t=lc,f=bufptr; f<colon; f++, t++) {
X	if (isspace(*f))
X	/* guard against space before : */
X	    break;
X	*t = isupper(*f) ? tolower(*f) : *f;
X    }
X    *t = '\0';
X    f = lc;				/* get lc into register */
X    len = t - f;
X
X    /* now scan the headtype table, backwards so we don't have to supply an
X     * extra terminating value, using first letter as index, and length as
X     * optimization to avoid calling subroutine strEQ unnecessarily.  Hauls.
X     */
X    
X    if (islower(*f)) {
X	for (i = htypeix[*f - 'a']; *htype[i].ht_name == *f; --i) {
X	    if (len == htype[i].ht_length && strEQ(f, htype[i].ht_name)) {
X		return i;
X	    }
X	}
X    }
X    return SOME_LINE;
X}
X
Xvoid
Xstart_header(artnum)
XART_NUM artnum;
X{
X    register int i;
X
X#ifdef DEBUGGING
X    if (debug & 4)
X	dumpheader("start_header\n");
X#endif
X    for (i=0; i<HEAD_LAST; i++) {
X	htype[i].ht_minpos = -1;
X	htype[i].ht_maxpos = 0;
X    }
X    in_header = SOME_LINE;
X    first_one = FALSE;
X#ifdef ASYNC_PARSE
X    parsed_art = artnum;
X#endif
X}
X
Xbool
Xparseline(art_buf,newhide,oldhide)
Xchar *art_buf;
Xint newhide, oldhide;
X{
X    if (*art_buf == ' ' || *art_buf == '\t')
X					/* header continuation line? */
X	return oldhide;
X    else {				/* maybe another header line */
X	char *s;
X
X	if (first_one) {		/* did we just pass 1st occurance? */
X	    first_one = FALSE;
X	    htype[in_header].ht_maxpos = artpos;
X					/* remember where line left off */
X	}
X	s = index(art_buf,':');
X	if (s == Nullch) {
X			    /* is it the end of the header? */
X	    htype[PAST_HEADER].ht_minpos =
X		(*art_buf == '\n') ? ftell(artfp) : artpos;
X			    /* remember where body starts */
X	    in_header = PAST_HEADER;
X	}
X	else {	/* it is a new header line */
X	    in_header = set_line_type(art_buf,s);
X	    first_one = (htype[in_header].ht_minpos < 0);
X	    if (first_one)
X		htype[in_header].ht_minpos = artpos;
X#ifdef DEBUGGING
X	    if (debug & 4)
X		dumpheader(art_buf);
X#endif
X	    if (htype[in_header].ht_flags & HT_HIDE)
X		return newhide;
X	}
X    }
X    return FALSE;			/* don't hide this line */
X}
X
X#ifdef ASYNC_PARSE
Xint
Xparse_maybe(artnum)
XART_NUM artnum;
X{
X    char tmpbuf[LBUFLEN];
X
X    if (parsed_art == artnum)
X	return 0;
X    /* no maybe about it now */
X#ifdef SERVER
X    if (nntpopen(artnum,GET_HEADER) == Nullfp) {
X#else
X    if (artopen(artnum) == Nullfp) {
X#endif
X	return -1;
X    }
X    start_header(artnum);
X    while (in_header) {
X	artpos = ftell(artfp);
X	if (fgets(tmpbuf,LBUFLEN,artfp) == Nullch)
X	    break;
X	parseline(tmpbuf,FALSE,FALSE);
X    }
X    in_header = PAST_HEADER;
X    return 0;
X}
X#endif
X
X/* get the subject line for an article */
X
Xchar *
Xfetchsubj(artnum,current_subject,copy)
XART_NUM artnum;				/* article to get subject from */
Xbool current_subject;			/* is it in a parsed header? */
Xbool copy;				/* do you want it savestr()ed? */
X{
X    char *s = Nullch, *t;
X#ifdef SERVER
X    static int xhdr = 1;		/* Can we use xhdr command? */
X    int eoo;				/* End of server output */
X    char ser_line[256];
X#endif /* SERVER */
X
X#ifdef CACHESUBJ
X    if (!subj_list) {
X	register ART_NUM i;
X	
X
X#ifndef lint
X	subj_list =
X	  (char**)safemalloc((MEM_SIZE)((OFFSET(lastart)+2)*sizeof(char *)));
X#endif /* lint */
X	for (i=0; i<=OFFSET(lastart); i++)
X	    subj_list[i] = Nullch;
X    }
X    if (!artnum || artnum > lastart)
X	s = nullstr;
X    else
X	s = subj_list[OFFSET(artnum)];
X#endif
X    if (s == Nullch) {
X	if (current_subject) {
X	    s = fetchlines(artnum,SUBJ_LINE);
X#ifdef CACHESUBJ
X	    subj_list[OFFSET(artnum)] = s;
X#endif
X	}
X	else {
X	    s = safemalloc((MEM_SIZE)256);
X	    *s = '\0';
X#ifdef SERVER
X	    if (xhdr) {
X	    	sprintf(ser_line, "XHDR subject %ld", artnum);
X	    	put_server(ser_line);
X		if (get_server(ser_line, sizeof (ser_line)) >= 0) {
X			if (ser_line[0] == CHAR_FATAL) {
X			    xhdr = 0;
X			} else {
X			    while (get_server(ser_line, sizeof (ser_line)) >= 0) {
X				if (ser_line[0] == '.')
X				    break;
X				else {
X				    t = index(ser_line, ' ');
X				    if (t++) {
X					strcpy(s, t);
X					if (t = index(s, '\r'))
X						*t = '\0';
X				    }
X				}
X			    }
X			}
X		} else {
X		    fprintf(stderr,
X			"rrn: Unexpected close of server socket.\n");
X		    finalize(1);
X		}
X	    }
X
X	    if (!xhdr) {
X		sprintf(ser_line, "HEAD %ld", artnum);
X		put_server(ser_line);
X		eoo = 0;
X		if (get_server(ser_line, 256) >= 0 && ser_line[0] == CHAR_OK) {
X		    do {
X			if (get_server(s, 256) < 0 || (*s == '.')) {
X			strcpy(s, "Title: \n");
X			eoo = 1;
X		        }
X		    } while (strnNE(s,"Title:",6) && strnNE(s,"Subject:",8));
X
X		    if (!eoo)
X			while (get_server(ser_line, sizeof (ser_line)) >= 0 &&
X				ser_line[0] != '.');
X		    t = index(s,':')+1;
X		    while (*t == ' ') t++;
X		    strcpy(s, t);
X	        }
X	    }
X#else /* not SERVER */
X	    if (artopen(artnum) != Nullfp) {
X		do {
X		    if (fgets(s,256,artfp) == Nullch)
X			strcpy(s, "Title: \n");
X		} while (strnNE(s,"Title:",6) && strnNE(s,"Subject:",8));
X
X		s[strlen(s)-1] = '\0';
X		t = index(s,':')+1;
X		while (*t == ' ') t++;
X		strcpy(s, t);
X	    }
X#endif
X	    s = saferealloc(s, (MEM_SIZE)strlen(s)+1);
X#ifdef CACHESUBJ
X	    subj_list[OFFSET(artnum)] = s;
X#endif 
X	}
X    }
X#ifdef CACHESUBJ
X    if (copy) {
X	t = savestr(s);
X	return t;
X    }
X    else
X	return s;
X#else
X    if (copy)
X	return s;
X    else {
X	safecpy(cmd_buf,s,CBUFLEN);	/* hope this is okay--we're */
X	free(s);
X	return cmd_buf;			/* really scraping for space here */
X    }
X#endif
X}
X
X/* get header lines from an article */
X
Xchar *
Xfetchlines(artnum,which_line)
XART_NUM artnum;				/* article to get line from */
Xint which_line;				/* type of line desired */
X{
X    char *newbuf, *t, tmp_buf[LBUFLEN];
X    register ART_POS curpos;
X    int size;
X    register ART_POS firstpos;
X    register ART_POS lastpos;
X    
X#ifdef ASYNC_PARSE
X    if (parse_maybe(artnum))
X	artnum = 0;
X#endif
X    firstpos = htype[which_line].ht_minpos;
X    lastpos = htype[which_line].ht_maxpos;
X#ifdef SERVER
X    if (!artnum || firstpos < 0 || nntpopen(artnum,GET_HEADER) == Nullfp) {
X#else
X    if (!artnum || firstpos < 0 || artopen(artnum) == Nullfp) {
X#endif
X	newbuf = safemalloc((unsigned int)1);
X	*newbuf = '\0';
X	return newbuf;
X    }
X#ifndef lint
X    size = lastpos - firstpos + 1;
X#else
X    size = Null(int);
X#endif /* lint */
X#ifdef DEBUGGING
X    if (debug && (size < 1 || size > 1000)) {
X	printf("Firstpos = %ld, lastpos = %ld\n",(long)firstpos,(long)lastpos);
X	gets(tmp_buf);
X    }
X#endif
X    newbuf = safemalloc((unsigned int)size);
X    *newbuf = '\0';
X    fseek(artfp,firstpos,0);
X    for (curpos = firstpos; curpos < lastpos; curpos = ftell(artfp)) {
X	if (fgets(tmp_buf,LBUFLEN,artfp) == Nullch)
X	    break;
X	if (*tmp_buf == ' ' || *tmp_buf == '\t')
X	    t = tmp_buf;
X	else
X	    t = index(tmp_buf,':')+1;
X	if (t == Nullch)
X	    break;
X	else {
X	    while (*t == ' ' || *t == '\t') t++;
X	    safecat(newbuf,t,size);
X	}
X    }
X    return newbuf;
X}
X
END_OF_FILE
  if test 8722 -ne `wc -c <'head.c'`; then
    echo shar: \"'head.c'\" unpacked with wrong size!
  fi
  # end of 'head.c'
fi
if test -f 'mt-write.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mt-write.c'\"
else
  echo shar: Extracting \"'mt-write.c'\" \(9389 characters\)
  sed "s/^X//" >'mt-write.c' <<'END_OF_FILE'
X/* $Header: mt-write.c,v 4.3.3.1 90/07/24 23:51:18 davison Trn $
X**
X** $Log:	mt-write.c,v $
X** Revision 4.3.3.1  90/07/24  23:51:18  davison
X** Initial Trn Release
X** 
X*/
X
X#include "EXTERN.h"
X#include "common.h"
X#include "mthreads.h"
X
Xstatic FILE *fp_out;
Xstatic int seq;
Xstatic int article_seq;
X
Xstatic int failure;
X
Xvoid write_subjects(), write_authors(), write_roots(), write_ids();
Xvoid write_articles(), write_thread(), write_item();
Xvoid enumerate_articles(), enumerate_thread();
Xvoid free_leftovers();
X
X/* Write out all the data in a packed format that is easy for our newsreader
X** to use.  We free things as we go, when we don't need them any longer.  If
X** we encounter any write errors, the write_item routine sets a failure flag
X** to halt our writing of the file, but we keep on plugging away to free
X** everything up.
X*/
Xint
Xwrite_data( filename )
Xchar *filename;
X{
X    if( filename == Nullch ) {
X	failure = 2;	/* A NULL filename indicates just free the data */
X    } else if( !ensure_path( filename ) ) {
X	log_error( "Unable to create path: `%s'.\n", filename );
X	failure = 2;
X    } else if( (fp_out = fopen( filename, "w" )) == Nullfp ) {
X	log_error( "Unable to create file: `%s'.\n", filename );
X	failure = 2;
X    } else {
X	failure = 0;
X    }
X    write_item( &total, sizeof (TOTAL) );
X
X    enumerate_articles();
X
X    write_authors();
X    write_subjects();
X    write_roots();
X    write_articles();
X    write_ids();
X    free_leftovers();
X
X    if( failure != 2 ) {
X	fclose( fp_out );
X    }
X    if( failure == 1 ) {
X	log_error( "Write failed!  Removing `%s'.\n", filename );
X	unlink( filename );
X    }
X    return !failure;
X}
X
X/* Recursively descend the article tree, enumerating the articles as we go.
X** This way we can output the article sequence numbers into the data file.
X*/
Xvoid
Xenumerate_articles()
X{
X    register ROOT *root;
X
X    seq = article_seq = 0;
X
X    for( root = root_root; root; root = root->link ) {
X	root->seq = seq++;
X	if( !root->articles ) {
X	    log_error( "** No articles on this root??\n" );
X	    continue;
X	}
X	enumerate_thread( root->articles );
X    }
X    if( seq != total.root ) {
X	log_error( "** Wrote %d roots instead of %d **\n", seq, total.root );
X    }
X    if( article_seq != total.article ) {
X	log_error( "** Wrote %d articles instead of %d **\n", article_seq, total.article );
X    }
X}
X
X/* Recursive routine for above-mentioned enumeration. */
Xvoid
Xenumerate_thread( article )
XARTICLE *article;
X{
X    while( article ) {
X	article->seq = article_seq++;
X	if( article->children ) {
X	    enumerate_thread( article->children );
X	}
X	article = article->siblings;
X    }
X}
X
X#define write_and_free( str_ptr )	/* Comment for makedepend to	 \
X					** ignore the backslash above */ \
X{\
X    register int len = strlen( str_ptr ) + 1;\
X    write_item( str_ptr, len );\
X    free( str_ptr );\
X    string_offset += len;\
X}
X
XMEM_SIZE string_offset;
X
X/* Write out the author information:  first the use-counts, then the
X** name strings all packed together.
X*/
Xvoid
Xwrite_authors()
X{
X    register AUTHOR *author;
X
X    seq = 0;
X    for( author = author_root; author; author = author->link ) {
X	write_item( &author->count, sizeof (WORD) );
X	author->seq = seq++;
X    }
X    if( seq != total.author ) {
X	log_error( "** Wrote %d authors instead of %d **\n",
X		seq, total.author );
X    }
X
X    string_offset = 0;
X
X    for( author = author_root; author; author = author->link ) {
X	write_and_free( author->name );
X    }
X}
X
X/* Write out the subject information: first the packed string data, then
X** the use-counts.  The order is important -- it is the order required
X** by the roots for their subject structures.
X*/
Xvoid
Xwrite_subjects()
X{
X    register ROOT *root;
X    register SUBJECT *subject;
X
X    for( root = root_root; root; root = root->link ) {
X	for( subject = root->subjects; subject; subject = subject->link ) {
X	    write_and_free( subject->str );
X	}
X    }
X    if( string_offset != total.string1 ) {
X	log_error( "** Author/subject strings were %ld bytes instead of %ld **\n",
X		string_offset, total.string1 );
X    }
X
X    seq = 0;
X    for( root = root_root; root; root = root->link ) {
X	for( subject = root->subjects; subject; subject = subject->link ) {
X	    write_item( &subject->count, sizeof (WORD) );
X	    subject->seq = seq++;
X	}
X    }
X    if( seq != total.subject ) {
X	log_error( "** Wrote %d subjects instead of %d **\n",
X		seq, total.subject );
X    }
X}
X
X/* Write the roots in a packed format.  Interpret the pointers into
X** sequence numbers as we go.
X*/
Xvoid
Xwrite_roots()
X{
X    register ROOT *root;
X
X    for( root = root_root; root; root = root->link ) {
X	p_root.articles = root->articles->seq;
X	p_root.root_num = root->root_num;
X	p_root.thread_cnt = root->thread_cnt;
X	p_root.subject_cnt = root->subject_cnt;
X	write_item( &p_root, sizeof (PACKED_ROOT) );
X    }
X}
X
X#define rel_article( article, rseq )	((article)? (article)->seq - (rseq) : 0)
X#define valid_seq( ptr )		((ptr)? (ptr)->seq : -1)
X
X/* Write all the articles in the same order that we sequenced them. */
Xvoid
Xwrite_articles()
X{
X    register ROOT *root;
X
X    for( root = root_root; root; root = root->link ) {
X	write_thread( root->articles );
X    }
X}
X
X/* Recursive routine to write the article in thread order.  We depend on
X** the fact that our first child is the very next article written (if we
X** have children).
X*/
Xvoid
Xwrite_thread( article )
Xregister ARTICLE *article;
X{
X    while( article ) {
X	p_article.num = article->num;
X	p_article.date = article->date;
X	p_article.subject = valid_seq( article->subject );
X	p_article.author = valid_seq( article->author );
X	p_article.flags = (article->flags & ~NEW_ARTICLE);
X	p_article.child_cnt = article->child_cnt;
X	p_article.parent = rel_article( article->parent, article->seq );
X	p_article.siblings = rel_article( article->siblings, article->seq );
X	p_article.root = article->root->seq;
X	write_item( &p_article, sizeof (PACKED_ARTICLE) );
X	if( article->children ) {
X	    write_thread( article->children );
X	}
X	article = article->siblings;
X    }
X}
X
XWORD minus_one = -1;
X
X/* Write the message-id strings:  each domain name (not including the
X** ".unknown." domain) followed by all of its associated unique ids.
X** Then output the article sequence numbers they belong to.  This stuff
X** is last because the newsreader doesn't need to read it.
X*/
Xvoid
Xwrite_ids()
X{
X    register DOMAIN *domain;
X    register ARTICLE *id;
X    register DOMAIN *next_domain;
X    register ARTICLE *next_id;
X
X    string_offset = 0;
X
X    for( domain = &unk_domain; domain; domain = domain->link ) {
X	if( domain != &unk_domain ) {
X	    write_and_free( domain->name );
X	    if( !domain->ids ) {
X		log_error( "** Empty domain name!! **\n" );
X	    }
X	}
X	for( id = domain->ids; id; id = id->id_link ) {
X	    write_and_free( id->id );
X	}
X    }
X    if( string_offset != total.string2 ) {
X	log_error( "** Message-id strings were %ld bytes (%ld) **\n",
X		string_offset, total.string2 );
X    }
X    for( domain = &unk_domain; domain; domain = next_domain ) {
X	next_domain = domain->link;
X	for( id = domain->ids; id; id = next_id ) {
X	    next_id = id->id_link;
X	    write_item( &id->seq, sizeof (WORD) );
X	    free( id );
X	}
X	write_item( &minus_one, sizeof (WORD) );
X	if( domain != &unk_domain ) {
X	    free( domain );
X	}
X    }
X    unk_domain.ids = Nullart;
X    unk_domain.link = Null(DOMAIN*);
X}
X
X/* Free everything that's left to free.
X*/
Xvoid
Xfree_leftovers()
X{
X    register ROOT *root, *next_root;
X    register SUBJECT *subj, *next_subj;
X    register AUTHOR *author, *next_author;
X
X    for( root = root_root; root; root = next_root ) {
X	next_root = root->link;
X	for( subj = root->subjects; subj; subj = next_subj ) {
X	    next_subj = subj->link;
X	    free( subj );
X	}
X	free( root );
X    }
X    for( author = author_root; author; author = next_author ) {
X	next_author = author->link;
X	free( author );
X    }
X    root_root = Null(ROOT*);
X    author_root = Null(AUTHOR*);
X}
X
X/* This routine will check to be sure that the required path exists for
X** the data file, and if not it will attempt to create it.
X*/
Xint
Xensure_path( filename )
Xregister char *filename;
X{
X    int status, pid, w;
X    char tmpbuf[1024];
X#ifdef MAKEDIR
X    register char *cp, *last;
X    register char *tbptr = tmpbuf+5;
X
X    if( !(last = rindex( filename, '/' )) ) {	/* find filename portion */
X	return 1;				/* no path, we're fine */
X    }
X    *last = '\0';				/* truncate path at filename */
X    strcpy( tmpbuf, "mkdir" );
X
X    for( cp = last;; ) {
X	if( stat( filename, &filestat ) >= 0 && (filestat.st_mode & S_IFDIR) ) {
X	    *cp = '/';
X	    break;
X	}
X	if( !(cp = rindex( filename, '/' )) ) {/* find something that exists */
X	    break;
X	}
X	*cp = '\0';
X    }
X    
X    for( cp = filename; cp <= last; cp++ ) {
X	if( !*cp ) {
X	    sprintf( tbptr, " %s", filename );
X	    tbptr += strlen( tbptr );		/* set up for mkdir call */
X	    *cp = '/';
X	}
X    }
X    if( tbptr == tmpbuf+5 ) {
X	return 1;
X    }
X#else
X    sprintf(tmpbuf,"%s %s %d", filexp(DIRMAKER), filename, 1);
X#endif
X
X    if ((pid = vfork()) == 0) {
X	execl(SH, SH, "-c", tmpbuf, Nullch);
X	_exit(127);
X    }
X    while ((w = wait(&status)) != pid && w != -1)
X	;
X    if (w == -1)
X	status = -1;
X    return !status;
X}
X
X/* A simple routine to output some data only if we haven't failed any
X** previous writes.
X*/
Xvoid
Xwrite_item( buff, len )
Xchar *buff;
Xint len;
X{
X    if( !failure ) {
X	if( fwrite( buff, 1, len, fp_out ) < len ) {
X	    failure = 1;
X	}
X    }
X}
END_OF_FILE
  if test 9389 -ne `wc -c <'mt-write.c'`; then
    echo shar: \"'mt-write.c'\" unpacked with wrong size!
  fi
  # end of 'mt-write.c'
fi
if test -f 'mthreads.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mthreads.1'\"
else
  echo shar: Extracting \"'mthreads.1'\" \(8949 characters\)
  sed "s/^X//" >'mthreads.1' <<'END_OF_FILE'
X''' $Header: mthreads.1,v 4.3.3.2 90/08/20 16:42:32 davison Trn $
X''' 
X''' $Log:	mthreads.1,v $
X''' Revision 4.3.3.2  90/08/20  16:42:32  davison
X''' Document new command-line interface.
X''' 
X''' Revision 4.3.3.1  90/07/21  20:03:37  davison
X''' Initial Trn Release
X''' 
X''' 
X.de Sh
X.br
X.ne 5
X.PP
X\fB\\$1\fR
X.PP
X..
X.de Sp
X.if t .sp .5v
X.if n .sp
X..
X.de Ip
X.br
X.ie \\n.$>=3 .ne \\$3
X.el .ne 3
X.IP "\\$1" \\$2
X..
X'''
X'''     Set up \*(-- to give an unbreakable dash;
X'''     string Tr holds user defined translation string.
X'''     Bell System Logo is used as a dummy character.
X'''
X.tr \(bs-|\(bv\*(Tr
X.ie n \{\
X.ds -- \(bs-
X.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
X.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
X.ds L" ""
X.ds R" ""
X.ds L' '
X.ds R' '
X'br\}
X.el\{\
X.ds -- \(em\|
X.tr \*(Tr
X.ds L" ``
X.ds R" ''
X.ds L' `
X.ds R' '
X'br\}
X.TH MTHREADS 1 LOCAL
X.UC 6
X.SH NAME
Xmthreads - threaded database manager for trn
X.SH SYNOPSIS
X.B mthreads [-d[MM]] [-e[HHMM]] [-aDfknv] [hierarchy_list]
X.SH DESCRIPTION
X.I Mthreads
Xmanages the thread files that are used by the
X.IR trn (1)
Xnewsreader.
X\*(L"Thread files\*(R" are used to store information about the news
Xarticles and how they are all related to one another.
X.PP
X.I Mthreads
Xneeds to be run
Xperiodically \*(-- either in single-pass mode out of cron,
Xor in daemon mode out of the boot script \*(-- to update the thread
Xinformation whenever new news is received.
XA site that gets its news feed during the night and doesn't need local
Xpostings processed throughout the day can run
X.I mthreads
Xin single-pass mode once a day.
XIf more processing is needed, either run
X.I mthreads
Xmore often or run it in daemon mode.
XIn daemon mode, a background process is forked off that wakes up every 10
Xminutes (by default) to check if the active file has been updated.
X.SH INSTALLATION
X.I Mthreads
Xin installed in the RNLIB directory chosen during configuration.
XWhen it is run for the first time, it will automatically create a file called
X.I active2
Xin the same directory.
XThis file is essentially a copy of the active file that keeps the newsgroup
Xtotals from the last run in one place.
XIt is also used to choose which groups are to be processed into thread files.
XAll groups start out as \*(L"unthreaded\*(R" unless they are turned on with
Xa command like:
X.IP
Xmthreads all
X.PP
Xwhich would create thread file for all the groups.
XFor testing purposes it is a good idea to start out small with a command
Xlike:
X.IP
Xmthreads news
X.PP
Xwhich would thread only the news hierarchy.
XThread processing can be turned on or off for individual groups or entire
Xhierarchies by specifying the groups in a syntax very similar to that used
Xin the sys file.
XFor example, to turn on all of soc and talk except for talk.politics, and
Xto turn off news.lists, use the following command once:
X.IP
Xmthreads soc,talk,!talk.politics,!news.lists
X.PP
XIf mthreads complains that another mthreads process is already running,
Xit can be killed with the command:
X.IP
Xmthreads -k
X.PP
Xand then repeat the prior command after giving it a little time to die.
X.PP
XOnce all the desired groups are turned on, it is not necessary to
Xspecify a hierarchy list for the normal functioning of mthreads.
XIt can be used, however, to customize which new groups get turned on
Xas they are created.
X.SH LOGGING
XAs mthreads executes, some status information (including error messages) 
Xis placed in
Xthe file mt.log in the RNLIB directory.
XThis file will grow without bounds, and should be scanned periodically for
Xerrors, and trimmed in size when it grows too large.
XSee the shell script
X.I mt.check
Xfor an mt.log maintainer that will send mail if it finds database errors.
X.SH OPTIONS
X.TP 5
X.B \-a
Xis used to automatically turn on thread processing for new news groups as
Xthey are created.
XThe default is to leave new groups unthreaded.
X.TP 5
X.B \-D
Xspecifies a debugging mode where only the groups mentioned on the
Xcommand-line are processed \*(-- all other groups are left unchanged.
X.TP 5
X.B \-d
Xis used to specify the daemon mode, where
X.I mthreads
Xforks a background task that periodically wakes up and checks for an updated
Xactive file.
XThe number of minutes to wait after the completion of the last pass can
Xbe specified after the '-d' option (e.g. -d20), otherwise it will default to
X10 minutes.
X.TP 5
X.B \-e
Xtells
X.I mthreads
Xto run an enhanced expiration check on the database.
XWithout this option, only articles below the minimum field in the active
Xfile are expired.
XWith this option, all unexpired articles in the database are stat()'ed to
Xsee if they actually exist.
XIn single-pass mode the
X.B -e
Xoption always affects the current pass \*(-- use it
Xonce a day after expire has run.
XIn daemon mode, the
X.B -e
Xoption will cause one pass a day to be the enhanced expire pass.
XBy default, this is the first time mthreads wakes up after 12:30 am.
XIf a different time is desired, it can be specified in the form HHMM 
X(e.g. -e2359).
X.TP 5
X.B -f
Xis used to force
X.I mthreads
Xto open each and every thread file to see which ones really need to be
Xupdated, not just the ones that differ in the active/active2 comparison.
XIt will also remove any extraneous thread files from unthreaded groups
X(which should only occur if you manually change the active2 file).
XThis option should only be used when manipulating the thread files in
Xunorthodox ways.
X.TP 5
X.B -k
Xcan be used to terminate the currently running mthreads, just as if it
Xhad received a terminate signal.
XWhen this option is specified, no other activity is performed.
X.TP 5
X.B -n
Xtells
X.I mthreads
Xthat no actual processing of thread files is to be performed.
XThis can be used to just adjust which groups are to be processed, without
Xactually doing any of the processing right away.
X.TP 5
X.B -v
Xselects additional levels of verbosity in the log file.
XThe default (without -v) is to log mthread's startup, the totals for each
Xpass, the inclusion of the enhanced expire option, and major database errors.
XAdd one
X.B -v
Xto get extra reference line problems logged into the file.
XAdd a second and a third for even more (useless?) information.
X.TP 5
X.B hierarchy_list
XThe hierarchy list is used to turn thread processing on or off for the listed
Xgroups.
XThe groups are specified in a manner very similar to the news software's
Xsys file:  \*(L"news\*(R" matches all groups in news; \*(L"!news\*(R" excludes
Xall groups in news; \*(L"comp.all.ibm.pc,!comp.all.ibm.pc.all\*(L" matches both
Xcomp.sys.ibm.pc and comp.binaries.ibm.pc, but not comp.binaries.ibm.pc.d.
X.Sp
XThe hierarchy_list is also used in conjunction with the debug flag to only
Xprocess one or more groups for testing purposes.
X.SH OUTPUT
XWhen
X.I mthreads
Xis run in single-pass mode it generates a stream a status characters on
Xstdout that present a visual display of what is happening.  If 
Xsingle-pass mode is used for regular processing, this output can be
Xredirected to /dev/null.
X.Sp
XThe output definitions:
X.br
X	\&'.' = group's entry is up-to-date
X.br
X	\&':' = group processed -- no change
X.br
X	\&'#' = group processed
X.br
X	\&'-' = group processed -- is now empty
X.br
X	\&'x' = group excluded in active
X.br
X	\&'X' = group excluded in active2
X.br
X	\&'*' = chdir failed (might be ok)
X.br
X	\&'!' = write failed (is NOT ok)
X.br
X	\&'e' = informational error
X.br
X	\&'E' = database-affecting error -- please report!
X.SH CONFIGURATION
XDuring the configuration of
X.I trn
Xa choice was made about where to place the thread data files.
XThey either exist as a .thread file in each group's spool directory, or
Xthey are a group.th file in a one-off directory structure on another drive.
XSee the THREAD_DIR definition in config.h to review or change this definition.
X.SH REBUILDING
XIf the thread files are ever removed, also remove the file db.init in
Xthe RNLIB directory.
XThis file contains the byte-order of the machine that generated the database,
Xand needs to be removed to truly start from scratch.
XAn easy way to get
X.I mthreads
Xto remove all the files except for db.init is to specify the command:
X.IP
Xmthreads !all
X.PP
XThis also turns off thread processing for all groups.
X.SH "ERROR HANDLING"
XIf the active2 file is removed or corrupted, it will
Xbe automatically rebuilt in the normal course of operation.
XThe record of which groups should be threaded will be lost, however.
XMissing/corrupted thread files are automatically re-built.
X.SH EXAMPLES
XRecommended commands to run on a regular basis are:
X.IP
Xmthreads -adve0630
X.PP
Xto start up an mthreads daemon in verbose logging mode that automatically
Xthreads new groups and performs an extended expire at 6:30 am, or:
X.IP
Xmthreads -e >/dev/null
X.PP
Xto run an mthreads single-pass with extended expire that leaves new groups
Xunthreaded.
X.SH FILES
X/usr/lib/news/active
X.br
X$RNLIB/active2
X.br
X$RNLIB/mt.log
X.br
X$RNLIB/db.init
X.br
X$RNLIB/LOCKmthreads
X.br
XLots of thread data files.
X.SH AUTHOR
XWayne Davison <davison at dri.com> <uunet!drivax!davison>
END_OF_FILE
  if test 8949 -ne `wc -c <'mthreads.1'`; then
    echo shar: \"'mthreads.1'\" unpacked with wrong size!
  fi
  # end of 'mthreads.1'
fi
if test -f 'ngstuff.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ngstuff.c'\"
else
  echo shar: Extracting \"'ngstuff.c'\" \(10760 characters\)
  sed "s/^X//" >'ngstuff.c' <<'END_OF_FILE'
X/* $Header: ngstuff.c,v 4.3.3.2 90/08/20 18:29:08 davison Trn $
X *
X * $Log:	ngstuff.c,v $
X * Revision 4.3.3.2  90/08/20  18:29:08  davison
X * Expanded path arrays for consistancy.
X * 
X * Revision 4.3.3.1  90/07/21  20:29:12  davison
X * Initial Trn Release
X * 
X * Revision 4.3.2.2  90/04/14  19:40:02  sob
X * Fixed small syntax problem that generates errors with particular C
X * preprocessors.
X *
X * Revision 4.3.1.2  85/05/10  14:31:52  lwall
X * Prevented "Junked" or "Marked unread" when no state change.
X * 
X * Revision 4.3.1.1  85/05/10  11:36:45  lwall
X * Branch for patches.
X * 
X * Revision 4.3  85/05/01  11:45:03  lwall
X * Baseline for release with 4.3bsd.
X * 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "term.h"
X#include "util.h"
X#include "ng.h"
X#include "bits.h"
X#include "intrp.h"
X#include "cheat.h"
X#include "head.h"
X#include "final.h"
X#include "sw.h"
X#ifdef USETHREADS
X#include "rthreads.h"
X#include "rn.h"
X#include "rcstuff.h"
X#endif
X#include "uudecode.h"
X#include "INTERN.h"
X#include "ngstuff.h"
X
Xvoid
Xngstuff_init()
X{
X    ;
X}
X
X/* do a shell escape */
X
Xint
Xescapade()
X{
X    register char *s;
X    bool interactive = (buf[1] == FINISHCMD);
X    bool docd;
X    char whereiam[512];
X
X    if (!finish_command(interactive))	/* get remainder of command */
X	return -1;
X    s = buf+1;
X    docd = *s != '!';
X    if (!docd) {
X	s++;
X    }
X    else {
X	getwd(whereiam);
X	if (chdir(cwd)) {
X	    printf(nocd,cwd) FLUSH;
X	    sig_catcher(0);
X	}
X    }
X    while (*s == ' ') s++;
X					/* skip leading spaces */
X    interp(cmd_buf, (sizeof cmd_buf), s);/* interpret any % escapes */
X    resetty();				/* make sure tty is friendly */
X    doshell(Nullch,cmd_buf);	/* invoke the shell */
X    noecho();				/* and make terminal */
X    crmode();				/*   unfriendly again */
X    if (docd) {
X	if (chdir(whereiam)) {
X	    printf(nocd,whereiam) FLUSH;
X	    sig_catcher(0);
X	}
X    }
X#ifdef MAILCALL
X    mailcount = 0;			/* force recheck */
X#endif
X    return 0;
X}
X
X/* process & command */
X
Xint
Xswitcheroo()
X{
X    if (!finish_command(TRUE)) /* get rest of command */
X	return -1;	/* if rubbed out, try something else */
X    if (!buf[1])
X	pr_switches();
X#ifdef PUSHBACK
X    else if (buf[1] == '&') {
X	if (!buf[2]) {
X	    page_init();
X	    show_macros();
X	}
X	else {
X	    char tmpbuf[LBUFLEN];
X	    register char *s;
X
X	    for (s=buf+2; isspace(*s); s++);
X	    mac_line(s,tmpbuf,(sizeof tmpbuf));
X	}
X    }
X#endif
X    else {
X	bool docd = (instr(buf,"-d") != Nullch);
X 	char whereami[512];
X 
X	if (docd)
X	    getwd(whereami);
X	sw_list(buf+1);
X	if (docd) {
X	    cwd_check();
X	    if (chdir(whereami)) {		/* -d does chdirs */
X		printf(nocd,whereami) FLUSH;
X		sig_catcher(0);
X	    }
X	}
X    }
X    return 0;
X}
X
X/* process range commands */
X
Xint
Xnumnum()
X{
X    ART_NUM min, max;
X    char *cmdlst = Nullch;
X    register char *s, *c;
X    ART_NUM oldart = art;
X    char tmpbuf[LBUFLEN];
X    bool justone = TRUE;		/* assume only one article */
X
X    perform_cnt = 0;
X    if (!finish_command(TRUE))	/* get rest of command */
X	return NN_INP;
X	if (lastart < 1) {
X	    fputs("\nNo articles\n",stdout) FLUSH;
X	    return NN_ASK;
X	}
X#ifdef ARTSRCH
X    if (srchahead)
X	srchahead = -1;
X#endif
X    for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++)
X	if (!isdigit(*s))
X	    justone = FALSE;
X    if (*s) {
X	cmdlst = savestr(s);
X	justone = FALSE;
X    }
X    else if (!justone)
X	cmdlst = savestr("m");
X    *s++ = ',';
X    *s = '\0';
X    safecpy(tmpbuf,buf,LBUFLEN);
X    for (s = tmpbuf; c = index(s,','); s = ++c) {
X	*c = '\0';
X	if (*s == '.')
X	    min = oldart;
X	else
X	    min = atol(s);
X#ifdef USETHREADS
X	if (min<absfirst && justone) {
X	    int r;
X
X	    /* Check if this is a root number */
X	    for (r = 0; r < total.root; r++) {
X		if (p_roots[r].root_num == min) {
X		    p_art = p_articles + p_roots[r].articles;
X		    art = p_art->num;
X		    if (p_art->subject == -1) {
X			follow_thread('N');
X		    }
X		    return NN_REREAD;
X		}
X	    }
X	}
X#endif
X	if (min<absfirst) {		/* make sure it is reasonable */
X	    min = absfirst;
X	    printf("(First article is %ld)\n",(long)absfirst) FLUSH;
X	    pad(just_a_sec/3);
X	}
X	if ((s=index(s,'-')) != Nullch) {
X	    s++;
X	    if (*s == '$')
X		max = lastart;
X	    else if (*s == '.')
X		max = oldart;
X	    else
X		max = atol(s);
X	}
X	else
X	    max = min;
X	if (max>lastart) {
X	    max = lastart;
X	    if (min > max)
X		min = max;
X	    printf("(Last article is %ld)\n",(long)lastart) FLUSH;
X	    pad(just_a_sec/3);
X	}
X	if (max < min) {
X	    fputs("\nBad range\n",stdout) FLUSH;
X	    if (cmdlst)
X		free(cmdlst);
X	    return NN_ASK;
X	}
X	if (justone) {
X	    art = min;
X	    return NN_REREAD;
X	}
X	check_first(min);
X	for (art=min; art<=max; art++) {
X	    if (perform(cmdlst,TRUE)) {
X#ifdef VERBOSE
X		IF(verbose)
X		    printf("\n(Interrupted at article %ld)\n",(long)art)
X		      FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    printf("\n(Intr at %ld)\n",(long)art) FLUSH;
X#endif
X		if (cmdlst)
X		    free(cmdlst);
X		return NN_ASK;
X	    }
X	}
X    }
X    art = oldart;
X    if (cmdlst)
X	free(cmdlst);
X    return NN_NORM;
X}
X
X#ifdef USETHREADS
Xint
Xuse_selected()
X{
X    PACKED_ARTICLE *root_limit;
X    register char *s, ch;
X    register int r;
X    char *cmdstr;
X    int ret = 1, orig_root_cnt = selected_root_cnt;
X
X    if (!finish_command(TRUE))	/* get rest of command */
X	return 0;
X    if (!(ch = buf[1]))
X	return -1;
X    cmdstr = savestr(buf+1);
X
X    perform_cnt = 0;
X    page_line = 1;
X
X    /* Multiple commands and commands that operate on individual articles
X    ** use the article loop.
X    */
X    if (strlen(cmdstr) > 1 || index("ejmMsSwW|=", ch)) {
X	bool want_unread = (unread_selector || ch == 'm');
X
X	for (r = 0; r < total.root; r++) {
X	    if (scan_all_roots
X	     || (!orig_root_cnt&&root_article_cnts[r]&&!(selected_roots[r]&4))
X	     || (selected_roots[r] & (unread_selector+1))) {
X		p_art = p_articles + p_roots[r].articles;
X		root_limit = upper_limit( p_art, 0 );
X		for (; p_art < root_limit; p_art++) {
X		    art = p_art->num;
X		    if (p_art->subject != -1
X		     && (!was_read(art) ^ want_unread)) {
X			if (perform(cmdstr, TRUE)) {
X			    fputs("\nInterrupted\n", stdout) FLUSH;
X			    goto break_out;
X			}
X		    }
X		    if (p_art == Nullart)
X			break;
X		}/* for all articles */
X	    }/* if selected */
X	}/* for all threads */
X    }				/* other commands get the root loop */
X    else if (ch == '+' || ch == '-' || ch == 'J' || ch == 'T' || ch == 't') {
X	for (r = 0; r < total.root; r++) {
X	    if (scan_all_roots
X	     || (!orig_root_cnt&&root_article_cnts[r]&&!(selected_roots[r]&4))
X	     || (selected_roots[r] & (unread_selector+1))) {
X		if (mode != 't' && ch != 't') {
X		    printf("T%-5ld ", (long)p_roots[r].root_num);
X		}
X		p_art = p_articles + p_roots[r].articles;
X		art = p_art->num;
X		if (perform(cmdstr, FALSE)) {
X		    fputs("\nInterrupted\n", stdout) FLUSH;
X		    goto break_out;
X		}
X#ifdef VERBOSE
X		IF(verbose)
X		    if (mode != 't' && ch != 't' && ch != 'T')
X			putchar('\n') FLUSH;
X#endif
X	    }
X	}
X    }
X    else if (ch == 'E') {	/* one command needs no looping at all */
X	if (uu_out != Nullfp) {
X	    uud_end();
X	} else {
X	    ret = 2;
X	}
X    }
X    else {
X	printf("???%s\n",cmdstr);
X	ret = -1;
X    }
X  break_out:
X    free(cmdstr);
X    return ret;
X}
X#endif
X
Xint
Xperform(cmdlst,toplevel)
Xregister char *cmdlst;
Xint toplevel;
X{
X    register int ch;
X    
X    if (toplevel) {
X	printf("%-6ld ",art);
X	fflush(stdout);
X    }
X    perform_cnt++;
X    for (; ch = *cmdlst; cmdlst++) {
X	if (isspace(ch) || ch == ':')
X	    continue;
X	if (ch == 'j') {
X	    if (!was_read(art)) {
X		mark_as_read();
X#ifdef VERBOSE
X		IF(verbose)
X		    fputs("\tJunked",stdout);
X#endif
X	    }
X	}
X#ifdef USETHREADS
X	else if (ch == '+') {
X	    find_article(art);
X	    if (p_art && !(selected_roots[p_art->root] & (unread_selector+1))) {
X		selected_roots[p_art->root] |= (unread_selector+1);
X		selected_root_cnt++;
X		if (mode == 't') {
X		    selected_count += root_article_cnts[p_art->root];
X		} else {
X		    selected_count += count_one_root(p_art->root);
X#ifdef VERBOSE
X		    IF(verbose)
X			fputs("\tSelected",stdout);
X#endif
X		}
X	    }
X	}
X	else if (ch == '-') {
X	    find_article(art);
X	    if (p_art && selected_root_cnt
X	     && (selected_roots[p_art->root] & (unread_selector+1))) {
X		selected_roots[p_art->root] &= ~(unread_selector+1);
X		selected_root_cnt--;
X		if (mode == 't') {
X		    selected_count -= root_article_cnts[p_art->root];
X		} else {
X		    selected_count -= count_one_root(p_art->root);
X#ifdef VERBOSE
X		    IF(verbose)
X			fputs("\tDeselected",stdout);
X#endif
X		}
X	    }
X	}
X	else if (ch == 't') {
X	    find_article(art);
X	    entire_tree();
X	}
X	else if (ch == 'J' || ch == 'T') {
X	    char tmpbuf[128];
X	    ART_NUM oldart = art;
X
X	    find_article(art);
X	    if (p_art) {
X		if (ch == 'T') {
X		    sprintf(tmpbuf,"T%ld\t# %s",
X			(long)p_roots[p_art->root].root_num,
X			subject_ptrs[p_art->subject]);
X		    fputs(tmpbuf,stdout);
X		    kf_append(tmpbuf);
X		}
X		follow_thread('J');
X		art = oldart;
X	    }
X	}
X#endif
X	else if (ch == 'm') {
X	    if (was_read(art)) {
X		unmark_as_read();
X#ifdef VERBOSE
X		IF(verbose)
X		    fputs("\tMarked unread",stdout);
X#endif
X	    }
X	}
X	else if (ch == 'M') {
X#ifdef DELAYMARK
X	    delay_unmark(art);
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\tWill return",stdout);
X#endif
X#else
X	    notincl("M");
X	    return -1;
X#endif
X	}
X	else if (ch == '=') {
X	    printf("\t%s",fetchsubj(art,FALSE,FALSE));
X#ifdef VERBOSE
X	    IF(verbose)
X		;
X	    ELSE
X#endif
X		putchar('\n') FLUSH;		/* ghad! */
X	}
X	else if (ch == 'C') {
X#ifdef ASYNC_PARSE
X	    printf("\t%sancelled",(cancel_article() ? "Not c" : "C"));
X#else
X	    notincl("C");
X	    return -1;
X#endif
X	}
X	else if (ch == '%') {
X#ifdef ASYNC_PARSE
X	    char tmpbuf[512];
X
X	    if (one_command)
X		interp(tmpbuf, (sizeof tmpbuf), cmdlst);
X	    else
X		cmdlst = dointerp(tmpbuf, (sizeof tmpbuf), cmdlst, ":") - 1;
X	    perform_cnt--;
X	    if (perform(tmpbuf,FALSE))
X		return -1;
X#else
X	    notincl("%");
X	    return -1;
X#endif
X	}
X	else if (index("!&sSwWe|",ch)) {
X	    if (one_command)
X		strcpy(buf,cmdlst);
X	    else
X		cmdlst = cpytill(buf,cmdlst,':') - 1;
X	    /* we now have the command in buf */
X	    if (ch == '!') {
X		escapade();
X#ifdef VERBOSE
X		IF(verbose)
X		    fputs("\tShell escaped",stdout);
X#endif
X	    }
X	    else if (ch == '&') {
X		switcheroo();
X#ifdef VERBOSE
X		IF(verbose)
X		    if (buf[1] && buf[1] != '&')
X			fputs("\tSwitched",stdout);
X#endif
X	    }
X	    else {
X		putchar('\t');
X		save_article();
X#ifdef VERBOSE
X		IF(verbose)
X		    ;
X		ELSE
X#endif
X		    putchar('\n') FLUSH;
X	    }
X	}
X	else {
X	    printf("\t???%s\n",cmdlst);
X	    return -1;
X	}
X#ifdef VERBOSE
X	fflush(stdout);
X#endif
X	if (one_command)
X	    break;
X    }
X    if (toplevel) {
X#ifdef VERBOSE
X	IF(verbose)
X	    putchar('\n') FLUSH;
X#endif
X    }
X    if( int_count ) {
X	int_count = 0;
X	return -1;
X    }
X    return 0;
X}
END_OF_FILE
  if test 10760 -ne `wc -c <'ngstuff.c'`; then
    echo shar: \"'ngstuff.c'\" unpacked with wrong size!
  fi
  # end of 'ngstuff.c'
fi
if test -f 'sw.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sw.c'\"
else
  echo shar: Extracting \"'sw.c'\" \(11603 characters\)
  sed "s/^X//" >'sw.c' <<'END_OF_FILE'
X/* $Header: sw.c,v 4.3.3.1 90/06/20 22:40:11 davison Trn $
X *
X * $Log:	sw.c,v $
X * Revision 4.3.3.1  90/06/20  22:40:11  davison
X * Initial Trn Release
X * 
X * Revision 4.3.2.3  90/05/08  22:06:00  sob
X * Added quick startup (-q) flag.
X * 
X * Revision 4.3.2.2  90/03/22  23:05:34  sob
X * Fixes provided by Wayne Davison <drivax!davison>
X * 
X * Revision 4.3.2.1  89/12/09  00:52:40  sob
X * Now handles SIGWINCH correctly.
X * 
X * Revision 4.3.1.2  85/05/21  13:36:23  lwall
X * Sped up "rn -c" by not doing unnecessary initialization.
X * 
X * Revision 4.3.1.1  85/05/10  11:40:38  lwall
X * Branch for patches.
X * 
X * Revision 4.3  85/05/01  11:50:54  lwall
X * Baseline for release with 4.3bsd.
X * 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "head.h"
X#include "only.h"
X#include "term.h"
X#include "ng.h"
X#include "intrp.h"
X#include "INTERN.h"
X#include "sw.h"
X
Xvoid
Xsw_init(argc,argv,tcbufptr)
Xint argc;
Xchar *argv[];
Xchar **tcbufptr;
X{
X    register int i;
X
X    if (argc >= 2 && strEQ(argv[1],"-c"))
X	checkflag=TRUE;			/* so we can optimize for -c */
X    interp(*tcbufptr,1024,GLOBINIT);
X    sw_file(tcbufptr,FALSE);
X    safecpy(*tcbufptr,getenv("RNINIT"),1024);
X    if (**tcbufptr) {
X	if (**tcbufptr == '/') {
X	    sw_file(tcbufptr,TRUE);
X	}
X	else
X	    sw_list(*tcbufptr);
X    }
X
X    for (i = 1; i < argc; i++)
X	decode_switch(argv[i]);
X}
X
Xvoid
Xsw_file(tcbufptr,bleat)
Xchar **tcbufptr;
Xbool bleat;
X{
X    int initfd = open(*tcbufptr,0);
X	
X    if (initfd >= 0) {
X	fstat(initfd,&filestat);
X	if (filestat.st_size > 1024)
X	    *tcbufptr = saferealloc(*tcbufptr,(MEM_SIZE)filestat.st_size);
X	if (filestat.st_size) {
X	    read(initfd,*tcbufptr,(int)filestat.st_size);
X	    (*tcbufptr)[filestat.st_size-1] = '\0';
X				/* wipe out last newline */
X	    sw_list(*tcbufptr);
X	}
X	else
X	    **tcbufptr = '\0';
X	close(initfd);
X    }
X    else {
X	if (bleat)
X	    printf(cantopen,*tcbufptr) FLUSH;
X	**tcbufptr = '\0';
X    }
X}
X
X/* decode a list of space separated switches */
X
Xvoid
Xsw_list(swlist)
Xchar *swlist;
X{
X    char *tmplist = safemalloc((MEM_SIZE) strlen(swlist) + 2);
X					/* semi-automatic string */
X    register char *p, inquote = 0;
X
X    strcpy(tmplist,swlist);
X    for (p=tmplist; isspace(*p); p++) ;	/* skip any initial spaces */
X    while (*p) {			/* "String, or nothing" */
X	if (!inquote && isspace(*p)) {	/* word delimiter? */
X	    *p++ = '\0';		/* chop here */
X	    while (isspace(*p))		/* these will be ignored later */
X		p++;
X	}
X	else if (inquote == *p) {
X	    strcpy(p,p+1);		/* delete trailing quote */
X	    inquote = 0;		/* no longer quoting */
X	}
X	else if (!inquote && *p == '"' || *p == '\'') {
X					/* OK, I know when I am not wanted */
X	    inquote = *p;		/* remember single or double */
X	    strcpy(p,p+1);		/* delete the quote */
X	}				/* (crude, but effective) */
X	else if (*p == '\\') {		/* quoted something? */
X	    if (p[1] == '\n')		/* newline? */
X		strcpy(p,p+2);		/* "I didn't see anything" */
X	    else {
X		strcpy(p,p+1);		/* delete the backwhack */
X		p++;			/* leave the whatever alone */
X	    }
X	}
X	else
X	    p++;			/* normal char, leave it alone */
X    }
X    *++p = '\0';			/* put an extra null on the end */
X    if (inquote)
X	printf("Unmatched %c in switch\n",inquote) FLUSH;
X    for (p = tmplist; *p; /* p += strlen(p)+1 */ ) {
X	decode_switch(p);
X	while (*p++) ;			/* point at null + 1 */
X    }
X    free(tmplist);			/* this oughta be in Ada */
X}
X
X/* decode a single switch */
X
Xvoid
Xdecode_switch(s)
Xregister char *s;
X{
X    while (isspace(*s))			/* ignore leading spaces */
X	s++;
X#ifdef DEBUGGING
X    if (debug)
X	printf("Switch: %s\n",s) FLUSH;
X#endif
X    if (*s != '-' && *s != '+') {	/* newsgroup pattern */
X	setngtodo(s);
X    }
X    else {				/* normal switch */
X	bool upordown = *s == '-' ? TRUE : FALSE;
X	char tmpbuf[LBUFLEN];
X
X	s++;
X	switch (*s) {
X#ifdef TERMMOD
X	case '=': {
X	    char *beg = s+1;
X
X	    while (*s && *s != '-' && *s != '+') s++;
X	    cpytill(tmpbuf,beg,*s);
X	    if (upordown ? strEQ(getenv("TERM"),tmpbuf)
X	    		 : strNE(getenv("TERM"),tmpbuf) ) {
X		decode_switch(s);
X	    }
X	    break;
X	}
X#endif
X#ifdef BAUDMOD
X	case '0': case '1': case '2': case '3': case '4':
X	case '5': case '6': case '7': case '8': case '9':
X	    if (upordown ? (just_a_sec*10 <= atoi(s))
X	    		 : (just_a_sec*10 >= atoi(s)) ) {
X		while (isdigit(*s)) s++;
X		decode_switch(s);
X	    }
X	    break;
X#endif
X	case '/':
X	    if (checkflag)
X		break;
X#ifdef SETENV
X	    setenv("SAVEDIR",  upordown ? "%p/%c" : "%p" );
X	    setenv("SAVENAME", upordown ? "%a"    : "%^C");
X#else
X	    notincl("-/");
X#endif
X	    break;
X	case 'c':
X	    checkflag = upordown;
X	    break;
X	case 'C':
X	    s++;
X	    if (*s == '=') s++;
X	    docheckwhen = atoi(s);
X	    break;
X	case 'd': {
X	    if (checkflag)
X		break;
X	    s++;
X	    if (*s == '=') s++;
X	    if (cwd) {
X		chdir(cwd);
X		free(cwd);
X	    }
X	    cwd = savestr(s);
X	    break;
X	}
X#ifdef DEBUGGING
X	case 'D':
X	    s++;
X	    if (*s == '=') s++;
X	    if (*s)
X		if (upordown)
X		    debug |= atoi(s);
X		else
X		    debug &= ~atoi(s);
X	    else
X		if (upordown)
X		    debug |= 1;
X		else
X		    debug = 0;
X	    break;
X#endif
X	case 'e':
X	    erase_screen = upordown;
X	    break;
X	case 'E':
X#ifdef SETENV
X	    s++;
X	    if (*s == '=')
X		s++;
X	    strcpy(tmpbuf,s);
X	    s = index(tmpbuf,'=');
X	    if (s) {
X		*s++ = '\0';
X		setenv(tmpbuf,s);
X	    }
X	    else
X		setenv(tmpbuf,nullstr);
X#else
X	    notincl("-E");
X#endif
X	    break;
X	case 'F':
X	    s++;
X	    indstr = savestr(s);
X	    break;
X#ifdef INNERSEARCH
X	case 'g':
X	    gline = atoi(s+1)-1;
X	    break;
X#endif
X	case 'H':
X	case 'h': {
X	    register int len, i;
X	    char *t;
X	    int flag = (*s == 'h' ? HT_HIDE : HT_MAGIC);
X	    
X	    if (checkflag)
X		break;
X	    s++;
X	    len = strlen(s);
X	    for (t=s; *t; t++)
X		if (isupper(*t))
X		   *t = tolower(*t);
X	    for (i=HEAD_FIRST; i<HEAD_LAST; i++)
X		if (!len || strnEQ(s,htype[i].ht_name,len))
X		    if (upordown)
X			htype[i].ht_flags |= flag;
X		    else
X			htype[i].ht_flags &= ~flag;
X	    break;
X	}
X	case 'i':
X	    s++;
X	    if (*s == '=') s++;
X	    initlines = atoi(s);
X	    initlines_specified = TRUE;
X	    break;
X	case 'l':
X	    muck_up_clear = upordown;
X	    break;
X	case 'L':
X#ifdef CLEAREOL
X	    can_home_clear = upordown;
X#else
X	    notincl("-L");
X#endif
X	    break;
X	case 'M':
X	    mbox_always = upordown;
X	    break;
X	case 'm':
X	    s++;
X	    if (*s == '=') s++;
X	    if (!upordown)
X		marking = NOMARKING;
X	    else if (*s == 'u')
X		marking = UNDERLINE;
X	    else {
X		marking = STANDOUT;
X	    }
X	    break;
X	case 'N':
X	    norm_always = upordown;
X	    break;
X#ifdef VERBOSE
X	case 'n':
X	    fputs("This isn't readnews.  Don't use -n.\n\n",stdout) FLUSH;
X	    break;
X#endif
X	case 'r':
X	    findlast = upordown;
X	    break;
X	case 's':
X	    s++;
X	    if (*s == '=') s++;
X	    if (*s) {
X		countdown = atoi(s);
X		suppress_cn = FALSE;
X	    }
X	    else {
X		if (!upordown)
X		    countdown = 5;
X		suppress_cn = upordown;
X	    }
X	    break;
X	case 'S':
X#ifdef ARTSEARCH
X	    s++;
X	    if (*s == '=') s++;
X	    if (*s)
X		scanon = atoi(s);
X	    else
X		scanon = upordown*3;
X#else
X	    notincl("-S");
X#endif
X	    break;
X	case 't':
X#ifdef VERBOSE
X#ifdef TERSE
X	    verbose = !upordown;
X#else
X	    notincl("+t");
X#endif
X#else
X	    notincl("+t");
X#endif
X	    break;
X	case 'T':
X	    typeahead = upordown;
X	    break;
X	case 'v':
X#ifdef VERIFY
X	    verify = upordown;
X#else
X	    notincl("-v");
X#endif
X	    break;
X	case 'x':
X#ifdef USETHREADS
X	    s++;
X	    if (*s == '=') s++;
X	    if (*s <= '9' && *s >= '0') {
X		if ((max_tree_lines = atoi(s)) > 11)
X		    max_tree_lines = 11;
X		do {
X		    s++;
X		} while (*s <= '9' && *s >= '0');
X	    } else
X		max_tree_lines = upordown*6;
X	    if (*s)
X		strncpy(select_order, s, 3);
X	    if (mode == 'i')
X		use_threads = upordown;
X	    else if (use_threads != upordown)
X		printf("You must exit and restart to turn threaded operation %s.\n\n",
X		    upordown ? "on" : "off");
X#else
X	    notincl("-x");
X#endif
X	    break;
X	case 'X':
X#ifdef USETHREADS
X	    s++;
X	    if (*s == '=') s++;
X	    if (*s <= '9' && *s >= '0') {
X		select_on = atoi(s);
X		do {
X		    s++;
X		} while (*s <= '9' && *s >= '0');
X	    } else
X		select_on = upordown;
X	    if (*s)
X		end_select = *s++;
X	    if (*s)
X		page_select = *s;
X#else
X	    notincl("-X");
X#endif
X	    break;
X	/*
X	 * People want a way to avoid checking for new newsgroups on startup.
X	 */
X	case 'q':
X		quickstart = upordown;
X		break;
X	default:
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\nIgnoring unrecognized switch: -%c\n", *s) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\nIgnoring -%c\n", *s) FLUSH;
X#endif
X	    break;
X	}
X    }
X}
X
X/* print current switch values */
X
Xvoid
Xpr_switches()
X{
X    static char mp[2] = {'+','-'};
X    register int i;
X    
X    fputs("\nCurrent switch settings:\n",stdout);
X    printf("%c/ ", mp[strEQ(getval("SAVEDIR",SAVEDIR),"%p/%c")]);
X    printf("%cc ", mp[checkflag]);
X    printf("-C%d ", docheckwhen);
X    printf("-d%s ", cwd);
X#ifdef DEBUGGING
X    if (debug)
X	printf("-D%d ", debug);
X#endif
X    printf("%ce ", mp[erase_screen]);
X    printf("-F\"%s\" ", indstr);
X#ifdef INNERSEARCH
X    printf("-g%d", gline);
X#endif
X    putchar('\n');
X#ifdef VERBOSE
X    if (verbose) {
X	for (i=HEAD_FIRST; i<HEAD_LAST; i++)
X	    printf("%ch%s%c",
X		mp[htype[i].ht_flags & HT_HIDE], htype[i].ht_name,
X		(! (i % 5) ? '\n' : ' ') );
X    }
X#endif
X    printf("-i%d ", initlines);
X    printf("%cl ", mp[muck_up_clear]);
X#ifdef CLEAREOL
X    printf("%cL ", mp[can_home_clear]);
X#endif /* CLEAREOL */
X    if (marking)
X	printf("-m%c ",marking==UNDERLINE?'u':'s');
X    else
X	printf("+m ");
X    printf("%cM ", mp[mbox_always]);
X    printf("%cN ", mp[norm_always]);
X    printf("%cr ", mp[findlast]);
X    if (countdown)
X	printf("-s%d ", countdown);
X    else
X	printf("%cs ", mp[suppress_cn]);
X#ifdef ARTSEARCH
X    if (scanon)
X	printf("-S%d ",scanon);
X    else
X	printf("+S ");
X#endif
X#ifdef VERBOSE
X#ifdef TERSE
X    printf("%ct ", mp[!verbose]);
X#endif
X#endif
X    printf("%cT ", mp[typeahead]);
X#ifdef VERIFY
X    printf("%cv ", mp[verify]);
X#endif
X#ifdef USETHREADS
X    if (use_threads)
X	printf("-x%d%s ",max_tree_lines,select_order);
X    else
X	printf("+x ");
X    if (select_on)
X	printf("-X%d%c%c ",select_on,end_select,page_select);
X    else
X	printf("+X ");
X#endif
X    fputs("\n\n",stdout) FLUSH;
X#ifdef ONLY
X    if (maxngtodo) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("Current restriction:",stdout);
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("Only:",stdout);
X#endif
X	for (i=0; i<maxngtodo; i++)
X	    printf(" %s",ngtodo[i]);
X	fputs("\n\n",stdout) FLUSH;
X    }
X#ifdef VERBOSE
X    else if (verbose)
X	fputs("No restriction.\n\n",stdout) FLUSH;
X#endif
X#endif
X}
X
Xvoid
Xcwd_check()
X{
X    char tmpbuf[LBUFLEN];
X
X    if (!cwd)
X	cwd = savestr(filexp("~/News"));
X    strcpy(tmpbuf,cwd);
X    if (chdir(cwd)) {
X	safecpy(tmpbuf,filexp(cwd),sizeof tmpbuf);
X	if (makedir(tmpbuf,MD_DIR) < 0 || chdir(tmpbuf) < 0) {
X	    interp(cmd_buf, (sizeof cmd_buf), "%~/News");
X	    if (makedir(cmd_buf,MD_DIR) < 0)
X		strcpy(tmpbuf,homedir);
X	    else
X		strcpy(tmpbuf,cmd_buf);
X	    chdir(tmpbuf);
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\
XCannot make directory %s--\n\
X	articles will be saved to %s\n\
X\n\
X",cwd,tmpbuf) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\
XCan't make %s--\n\
X	using %s\n\
X\n\
X",cwd,tmpbuf) FLUSH;
X#endif
X	}
X    }
X    free(cwd);
X    getwd(tmpbuf);
X    if (eaccess(tmpbuf,2)) {
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\
XCurrent directory %s is not writeable--\n\
X	articles will be saved to home directory\n\n\
X",tmpbuf) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("%s not writeable--using ~\n\n",tmpbuf) FLUSH;
X#endif
X	strcpy(tmpbuf,homedir);
X    }
X    cwd = savestr(tmpbuf);
X}
END_OF_FILE
  if test 11603 -ne `wc -c <'sw.c'`; then
    echo shar: \"'sw.c'\" unpacked with wrong size!
  fi
  # end of 'sw.c'
fi
echo shar: End of archive 11 \(of 14\).
cp /dev/null ark11isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 14 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still must unpack the following archives:
    echo "        " ${MISSING}
fi
exit 0
exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list