🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: de.comp.sources.os9
1 messages
1 total messages Started by Frank Kaefer Thu, 24 Dec 1992 13:05
Tass for OS-9 Part01/03
#13
Author: Frank Kaefer
Date: Thu, 24 Dec 1992 13:05
2372 lines
51129 bytes
Submitted-by: Ulrich Dessauer <ud@Nightmare.ddt.sub.org>
Archive-name: tass/part01

: ----- Cut here ----- Cut here ----- Cut here ----- Cut here -----
: Use  sh filename  to extract shell archive
: This shell archive contains following files:
:     'MANIFEST                                      660 bytes'
:     'COPYRIGHT                                     356 bytes'
:     'Makefile                                      679 bytes'
:     'art.c                                       16520 bytes'
:     'curses.c                                     8952 bytes'
:     'group.c                                     16732 bytes'
: 
if test -f 'MANIFEST' ; then
  echo 'File MANIFEST already exists, overwriting it'
  del 'MANIFEST'
fi
echo Extracting \"'MANIFEST'\"
sed "s/^X//" >'MANIFEST' <<'__END__OF__THIS__FILE__'
XFilename                  Part   Description
X----------------------------------------------------
XMANIFEST                  1      This shipping list
XCOPYRIGHT                 1      
XMakefile                  1      
Xart.c                     1      
Xcurses.c                  1      
Xgroup.c                   1      
Xmail.c                    2      
Xmain.c                    2      
Xmisc.c                    2      
Xpage.c                    2      
Xprompt.c                  3      
Xscreen.c                  3      
Xselect.c                  3      
Xsigs.c                    3      
Xtass.h                    3      
Xtime.c                    3      
__END__OF__THIS__FILE__
if test -f 'COPYRIGHT' ; then
  echo 'File COPYRIGHT already exists, overwriting it'
  del 'COPYRIGHT'
fi
echo Extracting \"'COPYRIGHT'\"
sed "s/^X//" >'COPYRIGHT' <<'__END__OF__THIS__FILE__'
X/*
X *  Tass, a visual Usenet news reader
X *  (c) Copyright 1990 by Rich Skrenta
X *
X *  Distribution agreement:
X *
X *	You may freely copy or redistribute this software, so long
X *	as there is no profit made from its use, sale, trade or
X *	reproduction.  You may not change this copyright notice,
X *	and it must be included prominently in any copy made.
X */
__END__OF__THIS__FILE__
if test -f 'Makefile' ; then
  echo 'File Makefile already exists, overwriting it'
  del 'Makefile'
fi
echo Extracting \"'Makefile'\"
sed "s/^X//" >'Makefile' <<'__END__OF__THIS__FILE__'
XG=	-g
XCC=	cc
XCFLAGS=	-qt=/dd/TMP $G -DREGEXP -DAPPEND_SIG -DMNEWS -DINCSTR \
X	-DLOCK_INDEX -DRAND_SIG
XLIBS=	-l=/dd/LIB/8bit.l -l=/dd/LIB/termlib.l -l=/dd/LIB/os9lib.l
X
X
XOBJECTS	=	curses.r art.r group.r mail.r main.r misc.r page.r \
X		prompt.r screen.r select.r time.r sigs.r
X
X/h0/ETC/CMDS/tass: $(OBJECTS)
X	$(CC) $(CFLAGS) -m=20 -f=$@ $(OBJECTS) $(LIBS)
X
Xshar:
X	-mv -f ../tass.shar ../tass.shar-
X	shar -v [A-Z]* *.[ch] > ../tass.shar
X
X
Xart.r:		art.c tass.h
Xcurses.r:	curses.c
Xgroup.r:	group.c tass.h
Xmail.r:		mail.c
Xmain.r:		main.c tass.h
Xmisc.r:		misc.c tass.h
Xpage.r:		page.c tass.h
Xprompt.r:	prompt.c tass.h
Xscreen.r:	screen.c tass.h
Xselect.r:	select.c tass.h
Xtime.r:		time.c
__END__OF__THIS__FILE__
if test -f 'art.c' ; then
  echo 'File art.c already exists, overwriting it'
  del 'art.c'
fi
echo Extracting \"'art.c'\"
sed "s/^X//" >'art.c' <<'__END__OF__THIS__FILE__'
X
X
X#include	<stdio.h>
X#include	<time.h>
X#include	<signal.h>
X#include	<errno.h>
X#include	"tass.h"
X
X
X/* Hopefully one of these is right for you. */
X
X#ifdef BSD
X#	include <sys/types.h>
X#	include <sys/dir.h>
X#	define		DIR_BUF		struct direct
X#	define		D_LENGTH	d_namlen
X#endif
X#ifdef M_XENIX
X#	include <sys/ndir.h>
X#	define		DIR_BUF		struct direct
X#	define		D_LENGTH	d_namlen
X#endif
X#ifdef	OSK
X#	include <ctype.h>
X#	include <modes.h>
X#	include <dir.h>
X#	define		DIR_BUF		struct direct
X#	define		D_LENGTH	d_namlen
X#	undef		SPOOLDIR
X#	define		SPOOLDIR	spooldir
X#endif	/* OSK */
X#ifndef DIR_BUF
X#	include	<sys/types.h>
X#	include	<dirent.h>
X#	define		DIR_BUF		struct dirent
X#	define		D_LENGTH	d_reclen
X#endif
X
Xextern char	*index ();
Xextern char	*realloc ();
X
Xchar index_file[LEN+1];
Xchar *glob_art_group;
X
Xstatic int	complete_count;
Xstatic int	complete_expire;
X
X#ifdef SIGTSTP
Xvoid
Xart_susp(i)
Xint i;
X{
X
X	Raw(FALSE);
X	putchar('\n');
X	signal(SIGTSTP, SIG_DFL);
X	kill(0, SIGTSTP);
X
X	signal(SIGTSTP, art_susp);
X	Raw(TRUE);
X
X	mail_setup();
X	ClearScreen();
X	MoveCursor(LINES, 0);
X	printf("Group %s...    ", glob_art_group);
X	fflush(stdout);
X}
X#endif
X
X
X/*
X *  Convert a string to a long, only look at first n characters
X */
X
Xmy_atol(s, n)
Xchar *s;
Xint n;
X{
X	long ret = 0;
X
X	while (*s && n--) {
X		if (*s >= '0' && *s <= '9')
X			ret = ret * 10 + (*s - '0');
X		else
X			return -1;
X		s++;
X	}
X
X	return ret;
X}
X
X# ifdef		LOCK_INDEX
X/*
X *	Lock the index when modifieng it (only on global indicies)
X */
Xstatic char *
Xlock_filename (fle)
Xchar *fle;
X{
X#ifndef	OSK
X	static int	siz = -1;
X	static char	*fname = NULL;
X	int		len;
X
X	if ((len = strlen (fle)) > siz) {
X		siz = len;
X		if (! (fname = realloc (fname, siz + 16)))
X			return (NULL);
X	}
X	sprintf (fname, "%s_lock", fle);
X	return (fname);
X#else
X	static char	fname[64];
X	unsigned long	n;
X	char		*ptr;
X
X	for (ptr = fle, n = 0; *ptr; n += *ptr++ & 0xff)
X		;
X	sprintf (fname, "/dd/TMP/%u_lock", n);
X	return (fname);
X#endif
X}
X
X# ifdef		OSK
Xstatic void
Xstop_signals ()
X{
X	sigmask (1);
X}
X
Xstatic void
Xstart_signals ()
X{
X	sigmask (-1);
X}
X# else		/* OSK */
Xtypedef int	SIGTYPE;
X
Xstatic SIGTYPE	(*signals)()[NSIG];
X
Xstatic void
Xstop_signals ()
X{
X	int	t;
X
X	for (t = 0; t < NSIG; ++t)
X		signals[t] = signal (t, SIG_IGN);
X}
X
Xstatic void
Xstart_signals ()
X{
X	int	t;
X
X	for (t = 0; t < NSIG; ++t)
X		signal (t, signals[t]);
X}
X# endif		/* OSK */
X
Xstatic int	lock_fd = -1;
X
Xstatic int
Xlock_index (fle)
Xchar *fle;
X{
X	int		tout;
X	char		*fn;
X
X	if (lock_fd != -1)
X		close (lock_fd);
X	if (! (fn = lock_filename (fle)))
X		return (-1);
X	for (tout = 20; tout > 0; --tout) {
X# ifndef	OSK
X		if ((lock_fd = creat (fn, 0444)) == -1)
X# else		/* OSK */
X		unlink (fn);
X		if (((lock_fd = create (fn, S_IWRITE, 033)) == -1) && (errno == E_CEF))
X# endif		/* OSK */
X			sleep (1);
X		else
X			break;
X	}
X	if (lock_fd != -1)
X		stop_signals ();
X	return (lock_fd == -1 ? -1 : 0);
X}
X
Xstatic void
Xunlock_index (fle)
Xchar *fle;
X{
X	char	*fn;
X
X	if (lock_fd != -1) {
X		close (lock_fd);
X		lock_fd = -1;
X		if (fn = lock_filename (fle)) {
X# ifndef	OSK
X			chmod (fn, 0666);
X# endif		/* OSK */
X			unlink (fn);
X		}
X		start_signals ();
X	}
X}
X# endif		/* LOCK_INDEX */
X
X/*
X *  Construct the pointers to the basenotes of each thread
X *  arts[] contains every article in the group.  inthread is
X *  set on each article that is after the first article in the
X *  thread.  Articles which have been expired have their thread
X *  set to -2.
X */
X
Xfind_base() {
X	int i;
X
X	top_base = 0;
X
X	for (i = 0; i < top; i++)
X		if (!arts[i].inthread && arts[i].thread != -2) {
X			if (top_base >= max_art)
X				expand_art();
X			base[top_base++] = i;
X		}
X}
X
X
X/* 
X *  Count the number of non-expired articles in arts[]
X */
X
Xnum_arts() {
X	int sum = 0;
X	int i;
X
X	for (i = 0; i < top; i++)
X		if (arts[i].thread != -2)
X			sum++;
X
X	return sum;
X}
X
X
X/*
X *  Do we have an entry for article art?
X */
X
Xvalid_artnum(art)
Xlong art;
X{
X	int i;
X
X	for (i = 0; i < top; i++)
X		if (arts[i].artnum == art)
X			return i;
X
X	return -1;
X}
X
X
X/*
X *  Return TRUE if arts[] contains any expired articles
X *  (articles we have an entry for which don't have a corresponding
X *   article file in the spool directory)
X */
X
Xpurge_needed() {
X	int i;
X
X	for (i = 0; i < top; i++)
X		if (arts[i].thread == -2)
X			return TRUE;
X
X	return FALSE;
X}
X
X
X/*
X *  Main group indexing routine.  Group should be the name of the
X *  newsgroup, i.e. "comp.unix.amiga".  group_path should be the
X *  same but with the .'s turned into /'s: "comp/unix/amiga"
X *
X *  Will read any existing index, create or incrementally update
X *  the index by looking at the articles in the spool directory,
X *  and attempt to write a new index if necessary.
X */
X
Xindex_group(group, group_path)
Xchar *group;
Xchar *group_path;
X{
X	int modified;
X
X	glob_art_group = group;
X
X#ifdef SIGTSTP
X	signal(SIGTSTP, art_susp);
X#endif
X
X	if (!update) {
X		clear_message();
X		MoveCursor(LINES, 0);
X		printf("Group %s...    ", group);
X		fflush(stdout);
X	}
X
X	if (local_index)
X		find_local_index(group);
X	else
X		sprintf(index_file, "%s/%s/.tindex", SPOOLDIR, group_path);
X
X#ifdef	LOCK_INDEX
X	if (! local_index)
X		if (lock_index (index_file) < 0)
X			return -1;
X#endif	/* LOCK_INDEX */
X
X	load_index();
X	modified = read_group(group_path);
X	make_threads();
X	if (modified || purge_needed()) {
X#ifdef	USE_UID
X		if (local_index) {	/* writing index in home directory */
X			setuid(real_uid);	/* so become them */
X			setgid(real_gid);
X		}
X#endif	/* USE_UID */
X		dump_index(group);
X#ifdef	USE_UID
X		if (local_index) {
X			setuid(tass_uid);
X			setgid(tass_gid);
X		}
X#endif	/* USE_UID */
X	}
X
X#ifdef	LOCK_INDEX
X	if (! local_index)
X		unlock_index (index_file);
X#endif	/* LOCK_INDEX */
X
X	find_base();
X
X	if (modified && !update)
X		clear_message();
X}
X
X
X/*
X *  Longword comparison routine for the qsort()
X */
X
Xbase_comp(a, b)
Xlong *a;
Xlong *b;
X{
X
X	if (*a < *b)
X		return -1;
X	if (*a > *b)
X		return 1;
X	return 0;
X}
X
X
X/*
X *  Read the article numbers existing in a group's spool directory
X *  into base[] and sort them.  base_top is one past top.
X */
X
Xscan_dir(group)
Xchar *group;
X{
X	DIR *d;
X	DIR_BUF *e;
X	long art;
X	char buf[200];
X
X	top_base = 0;
X
X	sprintf(buf, "%s/%s", SPOOLDIR, group);
X
X	d = opendir(buf);
X	if (d != NULL) {
X		while ((e = readdir(d)) != NULL) {
X#ifdef		OSK
X			e->D_LENGTH = strlen (e->d_name);
X#endif		/* OSK */
X			art = my_atol(e->d_name, e->D_LENGTH);
X			if (art >= 0) {
X				if (top_base >= max_art)
X					expand_art();
X				base[top_base++] = art;
X			}
X		}
X		closedir(d);
X	}
X
X	qsort(base, top_base, sizeof(long), base_comp);
X}
X
X
X/*
X *  Index a group.  Assumes any existing index has already been
X *  loaded.
X */
X
Xread_group(group)
Xchar *group;
X{
X	char buf[200];
X	int fd;
X	long art;
X	int count;
X	int modified = FALSE;
X	int respnum;
X	int i;
X
X	scan_dir(group);	/* load article numbers into base[] */
X
X	count = 0;
X
X	for (i = 0; i < top_base; i++) {	/* for each article # */
X		art = base[i];
X
X/*
X *  Do we already have this article in our index?  Change thread from
X *  -2 to -1 if so and skip the header eating.
X */
X
X		if ((respnum = valid_artnum(art)) >= 0) {
X			arts[respnum].thread = -1;
X			arts[respnum].unread = 1;
X			continue;
X		}
X
X		if (!modified) {
X			modified = TRUE;   /* we've modified the index */
X					   /* it will need to be re-written */
X#if 0
X			if (!update) {
X				MoveCursor(LINES, 0);
X				fputs("Indexing...    ", stdout);
X				fflush(stdout);
X			}
X#endif
X		}
X
X/*
X *  Add article to arts[]
X */
X		if (top >= max_art)
X			expand_art();
X
X		arts[top].artnum = art;
X		arts[top].thread = -1;
X		arts[top].inthread = FALSE;
X		arts[top].unread = 1;
X
X		sprintf(buf, "%s/%s/%ld", SPOOLDIR, group, art);
X#ifndef	MNEWS
X		fd = open(buf, 0);
X#else	/* MNEWS */
X#ifndef	OSK
X		if ((fd = open (buf, O_RDONLY)) != -1) {
X#else	/* OSK */
X		if ((fd = open (buf, S_IREAD)) != -1) {
X#endif	/* OSK */
X			int	n;
X			n = readln (fd, buf, 180);
X			lseek (fd, 0, 0);
X			if (n > 0) {
X				buf[n - 1] = '\0';
X				if (!strncmp (buf, "%(#)$ ", 6)) {
X					char	sav[200];
X					strcpy (sav, buf + 6);
X					sprintf (buf, "%s/%s", spooldir, sav);
X					close (fd);
X#ifndef	OSK
X					fd = open (buf, O_RDONLY);
X#else	/* OSK */
X					fd = open (buf, S_IREAD);
X#endif	/* OSK */
X				}
X			}
X		}
X#endif	/* MNEWS */
X
X		if (fd < 0) {
X			fprintf(stderr, "can't open article %s: ", buf);
X			perror("");
X			continue;
X		}
X
X		if (!parse_headers(fd, &arts[top]))
X			continue;
X		top++;
X		close(fd);
X
X		if (++count % 10 == 0 && !update) {
X			printf("\b\b\b\b%4d", count);
X			fflush(stdout);
X		}
X	}
X
X	complete_count += count;
X
X	return modified;
X}
X
X
X/*
X *  Go through the articles in arts[] and use .thread to snake threads
X *  through them.  Use the subject line to construct threads.  The
X *  first article in a thread should have .inthread set to FALSE, the
X *  rest TRUE.  Only do unexprired articles we haven't visited yet
X *  (arts[].thread == -1).
X */
X
Xmake_threads() {
X	int i;
X	int j;
X
X	for (i = 0; i < top; i++) {
X		if (arts[i].thread == -1)
X		    for (j = i+1; j < top; j++)
X			if (arts[i].hash == arts[j].hash
X			&&  arts[j].thread != -2
X			&&  strncmp(arts[i].nore, arts[j].nore, 10) == 0) {
X				arts[i].thread = j;
X				arts[j].inthread = TRUE;
X				break;
X			}
X	}
X}
X
X
X/*
X *  Return a pointer into s eliminating any leading Re:'s.  Example:
X *
X *	  Re: Reorganization of misc.jobs
X *	  ^   ^
X */
X
Xchar *
Xeat_re(s)
Xchar *s;
X{
X
X#if 1
X	while (*s == 'r' || *s == 'R') {
X		if (*(s+1) == 'e' || *(s+1) == 'E') {
X			if (*(s+2) == ':')
X				s += 3;
X			else if ((*(s+2) == '^') && isdigit (*(s+3))) {
X				s += 4;
X				if (*s == ':')
X					++s;
X			} else
X				break;
X		} else
X			break;
X		while (isspace (*s))
X			++s;
X	}
X#else
X	while (*s == 'R') {
X		if (strncmp(s, "Re: ", 4) == 0)
X			s += 4;
X		else if (strncmp(s, "Re:", 3) == 0)
X			s += 3;
X		else if (strncmp(s, "Re^2: ", 6) == 0)
X			s += 6;
X		else
X			break;
X	}
X#endif
X
X	return s;
X}
X
X
X/*
X *  Hash the subjects (after eating the Re's off) for a quicker
X *  thread search later.  We store the hashes for subjects in the
X *  index file for speed.
X */
X
Xlong
Xhash_s(s)
Xchar *s;
X{
X	long h = 0;
X
X	while (*s)
X		h = h + (*s++ & 0xff);
X
X	return h;
X}
X
X
Xparse_headers(fd, h)
Xint fd;
Xstruct header *h;
X{
X	char buf[1024];
X	char *p, *q;
X	char flag;
X	char *ptr;
X	int found_from, found_subj;
X	int n;
X
X	if ((n = read(fd, buf, 1024)) <= 0)
X		return FALSE;
X
X	buf[n > 1023 ? 1023 : n] = '\0';
X
X	p = buf;
X	while (p = index (p, '\n'))
X		if (*(p + 1) == '\n') {
X			*p = '\0';
X			break;
X		} else
X			++p;
X
X	p = buf;
X
X	h->from[0] = '\0';
X	h->subject[0] = '\0';
X	h->nore = h->subject;
X	h->hash = 0;
X
X	found_from = FALSE;
X	found_subj = FALSE;
X	while (1) {
X		q = p;
X		while (*p && *p != '\n') {
X			if ((unsigned char) *p & 0x7F < 32)
X				*p = ' ';
X			p++;
X		}
X		flag = *p;
X		*p++ = '\0';
X
X		if ((!found_from) && (strncmp(q, "From: ", 6) == 0)) {
X			if (ptr = index (&q[6], '('))
X				++ptr;
X			else
X				ptr = &q[6];
X			strncpy (h->from, ptr, MAX_FROM);
X			h->from[MAX_FROM-1] = '\0';
X			if (ptr = index (h->from, ')'))
X				*ptr = '\0';
X		} else if ((!found_subj) && (strncmp(q, "Subject: ", 9) == 0)) {
X			h->hash = hash_s(eat_re(&q[9]));
X			strncpy(h->subject, &q[9], MAX_SUBJ);
X			h->subject[MAX_SUBJ-1] = '\0';
X			h->nore = eat_re(h->subject);
X		}
X
X		if ((!flag) || (found_from && found_subj))
X			break;
X	}
X
X	return TRUE;
X}
X
X
X/* 
X *  Write out a .tindex file.  Write the group name first so if
X *  local indexing is done we can disambiguate between group name
X *  hash collisions by looking at the index file.
X */
Xvoid
Xdump_index(group)
Xchar *group;
X{
X	int i;
X	char buf[200];
X	FILE *fp;
X	char *p, *q;
X	long l;
X#ifndef	USE_UID
X	char lockfn[64];
X	int lockfd;
X
X	if (! local_index) {
X#ifndef	OSK
X		sprintf (lockfn, "/tmp/%-.10s", group);
X		lockfd = creat (lockfn, 0, 0444);
X#else	/* OSK */
X		sprintf (lockfn, "/dd/TMP/%-.20s", group);
X		for (p = lockfn; *p; ++p)
X			if (((unsigned char) *p >= 0x80) || ((!isalnum (*p)) && (*p != '/')))
X				*p = '_';
X		lockfd = create (lockfn, 0, 033);
X#endif	/* OSK */
X		if (lockfd < 0) {
X			sleep (1);
X			return;
X		}
X	}
X#endif	/* USE_UID */
X
X	fp = fopen(index_file, "w");
X	if (fp == NULL)
X#ifndef	USE_UID
X		goto dump_index_finish;
X#else	/* USE_UID */
X		return;
X#endif	/* USE_UID */
X
X	fprintf(fp, "%s\n", group);
X	fprintf(fp, "%d\n", num_arts());
X	for (i = 0; i < top; i++)
X	    if (arts[i].thread != -2) {
X		p = arts[i].nore;
X		q = arts[i].subject;
X		l = p - q;
X		fprintf(fp, "%ld\n%s\n%s\n%ld\n%ld\n",
X				arts[i].artnum,
X				arts[i].subject,
X				arts[i].from,
X				arts[i].hash,
X#if 0
X				(long) arts[i].nore - (long) arts[i].subject);
X#else
X				l);
X#endif
X	} else
X		++complete_expire;
X
X	fclose(fp);
X#ifdef	USE_UID
X#ifndef	OSK
X	chmod(index_file, 0644);
X#else	/* OSK */
X	chmod(index_file, 013);
X#endif	/* OSK */
X#else	/* USE_UID */
Xdump_index_finish:
X	if (local_index)
X#ifndef	OSK
X		chmod(index_file, 0644);
X#else	/* OSK */
X		chmod(index_file, 013);
X#endif	/* OSK */
X	else if (lockfd != -1) {
X		close (lockfd);
X#ifndef	OSK
X		chmod (lockfn, 0666);
X#endif	/* OSK */
X		unlink (lockfn);
X#ifndef	OSK
X		chmod(index_file, 0666);
X#else	/* OSK */
X		chmod(index_file, 033);
X#endif	/* OSK */
X	}
X#endif	/* USE_UID */
X}
X
X
X/*
X *  strncpy that stops at a newline and null terminates
X */
X
Xmy_strncpy(p, q, n)
Xchar *p;
Xchar *q;
Xint n;
X{
X
X	while (n--) {
X		if (!*q || *q == '\n')
X			break;
X		*p++ = *q++;
X	}
X	*p = '\0';
X}
X
X
X/*
X *  Read in a .tindex file.
X */
Xvoid
Xload_index()
X{
X	int i;
X	long j;
X	char buf[200];
X	FILE *fp;
X	int first = TRUE;
X
X	top = 0;
X
X	fp = fopen(index_file, "r");
X	if (fp == NULL)
X		return;
X
X	if (fgets(buf, 200, fp) == NULL
X	||  fgets(buf, 200, fp) == NULL) {
X		fprintf(stderr, "one\n");
X		goto corrupt_index;
X	}
X
X	i = atol(buf);
X	while (top < i) {
X		if (top >= max_art)
X			expand_art();
X
X		arts[top].thread = -2;
X		arts[top].inthread = FALSE;
X
X		if (fgets(buf, 200, fp) == NULL) {
X			fprintf(stderr, "two\n");
X			goto corrupt_index;
X		}
X		arts[top].artnum = atol(buf);
X
X		if (fgets(buf, 200, fp) == NULL) {
X			fprintf(stderr, "three\n");
X			goto corrupt_index;
X		}
X
X		my_strncpy(arts[top].subject, buf, MAX_SUBJ-1);
X			
X		if (fgets(buf, 200, fp) == NULL) {
X			fprintf(stderr, "four\n");
X			goto corrupt_index;
X		}
X		my_strncpy(arts[top].from, buf, MAX_FROM-1);
X
X		if (fgets(buf, 200, fp) == NULL) {
X			fprintf(stderr, "five\n");
X			goto corrupt_index;
X		}
X		arts[top].hash = atol(buf);
X
X		if (fgets(buf, 200, fp) == NULL) {
X			fprintf(stderr, "six\n");
X			goto corrupt_index;
X		}
X
X		j = atol(buf);
X#if 0
X		if (j < 0 || j > 100) {
X#if 0
X			goto corrupt_index;
X#else
X			arts[top].nore = eat_re(arts[top].subject);
X#endif
X		} else
X			arts[top].nore = arts[top].subject + j;
X#else
X		arts[top].nore = eat_re(arts[top].subject);
X#endif
X
X		top++;
X	}
X
X	fclose(fp);
X	return;
X
Xcorrupt_index:
X	fprintf(stderr, "index file %s corrupt\n", index_file);
X	fprintf(stderr, "top = %d\n", top);
X	exit(1);
X	unlink(index_file);
X	top = 0;
X}
X
X
X/*
X *  Look in the local $HOME/.tindex (or wherever) directory for the
X *  index file for the given group.  Hashing the group name gets
X *  a number.  See if that #.1 file exists; if so, read first line.
X *  Group we want?  If no, try #.2.  Repeat until no such file or
X *  we find an existing file that matches our group.
X */
Xvoid
Xfind_local_index(group)
Xchar *group;
X{
X	unsigned long h;
X	static char buf[200];
X	int i;
X	char *p;
X	FILE *fp;
X
X	{
X		char *t = group;
X
X		h = *t++;
X		while (*t)
X			h = (h << 1) + (*t++ & 0xff);
X	}
X
X	i = 1;
X	while (1) {
X		sprintf(index_file, "%s/%lu.%d", indexdir, h, i);
X		fp = fopen(index_file, "r");
X		if (fp == NULL)
X			return;
X
X		if (fgets(buf, 200, fp) == NULL) {
X			fclose(fp);
X			return;
X		}
X		fclose(fp);
X
X		for (p = buf; *p && *p != '\n'; p++) ;
X		*p = '\0';
X
X		if (strcmp(buf, group) == 0)
X			return;
X
X		i++;
X	}
X}
X
X
X/*
X *  Run the index file updater only for the groups we've loaded.
X */
X
Xdo_update() {
X	int i;
X	char group_path[200];
X	char *p;
X#ifdef	OSK
X	int len = 0;
X#endif	/* OSK */
X	time_t tim;
X	struct tm *tt;
X
X	complete_count = 0;
X	complete_expire = 0;
X
X	for (i = 0; i < local_top; i++) {
X		strcpy(group_path, active[my_group[i]].name);
X		for (p = group_path; *p; p++)
X#ifndef	OSK
X			if (*p == '.')
X				*p = '/';
X#else	/* OSK */
X		{
X			if (*p == '.') {
X				*p = '/';
X				len = 0;
X			} else if ((! isalnum (*p)) && (! index ("$_", *p)))
X				*p = '_';
X			if (++len > 26)
X				while (*(p + 1) && (*(p + 1) != '.'))
X					++p;
X		}
X#endif	/* OSK */
X
X		index_group(active[my_group[i]].name, group_path);
X	}
X
X	time (&tim);
X	tt = localtime (&tim);
X	printf ("[%02d.%02d.%04d %2d:%2d] Collected %d articles",
X		tt -> tm_mday,
X		tt -> tm_mon + 1,
X		tt -> tm_year + 1900,
X		tt -> tm_hour,
X		tt -> tm_min,
X		complete_count);
X
X	if (complete_expire)
X		printf (" (%d articles expired)", complete_expire);
X	putchar ('\n');
X}
__END__OF__THIS__FILE__
if test -f 'curses.c' ; then
  echo 'File curses.c already exists, overwriting it'
  del 'curses.c'
fi
echo Extracting \"'curses.c'\"
sed "s/^X//" >'curses.c' <<'__END__OF__THIS__FILE__'
X
X/*
X *  This is a screen management library borrowed with permission from the
X *  Elm mail system (a great mailer--I highly recommend it!).
X *
X *  I've hacked this library to only provide what Tass needs.
X *
X *  Original copyright follows:
X */
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986 Dave Taylor
X ******************************************************************************/
X
X#include <stdio.h>
X#ifndef	OSK
X#include <curses.h>
X#include <sys/ioctl.h>
X#else	/* OSK */
X#include <termcap.h>
X#include <sgstat.h>
Xchar	PC_, *UP, *BC;
Xshort	ospeed;
X#endif	/* OSK */
X#include "tass.h"
X
X#define		BACKSPACE	'\b'
X#define		VERY_LONG_STRING	2500
X
Xint LINES=23;
Xint COLS=80;
X
Xint inverse_okay = TRUE;
X
X/*
X#ifdef BSD
X#  ifndef BSD4_1
X#    include <sgtty.h>
X#  else
X#    include <termio.h>
X#  endif
X# else
X#  include <termio.h>
X#endif
X*/
X
X#include <ctype.h>
X
X/*
X#ifdef BSD
X#undef tolower
X#endif
X*/
X
X#define TTYIN	0
X
X#ifdef SHORTNAMES
X# define _clearinverse	_clrinv
X# define _cleartoeoln	_clrtoeoln
X# define _cleartoeos	_clr2eos
X#endif
X
X#ifndef BSD
X#ifndef	OSK
Xstruct termio _raw_tty, 
X              _original_tty;
X#else	/* OSK */
Xstruct sgbuf	_raw_tty, 
X		_original_tty;
X#endif	/* OSK */
X#else
X#define TCGETA	TIOCGETP
X#define TCSETAW	TIOCSETP
X
Xstruct sgttyb _raw_tty,
X	      _original_tty;
X#endif
X
Xstatic int _inraw = 0;                  /* are we IN rawmode?    */
X
X#define DEFAULT_LINES_ON_TERMINAL	24
X#define DEFAULT_COLUMNS_ON_TERMINAL	80
X
Xstatic int _memory_locked = 0;		/* are we IN memlock??   */
X
Xstatic int _intransmit;			/* are we transmitting keys? */
X
Xstatic
Xchar *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos,
X	*_setinverse, *_clearinverse, *_setunderline, *_clearunderline;
X
X# define	K_UP		0
X# define	K_DOWN		1
X# define	K_LEFT		2
X# define	K_RIGHT		3
X# define	K_PUP		4
X# define	K_PDOWN		5
X
X# define	K_MAX		6
X
Xstatic
Xchar *_spckey[K_MAX];
Xstatic
Xchar *_spckeytc[K_MAX] = {
X	"ku", "kd", "kl", "kr", "kP", "kN"
X};
X
Xstatic
Xint _lines,_columns;
X
Xstatic char _terminal[1024];              /* Storage for terminal entry */
Xstatic char _capabilities[1024];           /* String for cursor motion */
X
Xstatic char *ptr = _capabilities;	/* for buffering         */
X
Xint    outchar();			/* char output for tputs */
Xchar  *tgetstr(),     		       /* Get termcap capability */
X      *tgoto();				/* and the goto stuff    */
X
XInitScreen()
X{
Xint  tgetent(),      /* get termcap entry */
X     err;
Xchar termname[40];
Xchar *strcpy(), *getenv();
Xint i;
X	
X	if (getenv("TERM") == NULL) {
X		fprintf(stderr,
X		  "TERM variable not set; Tass requires screen capabilities\n");
X		return(FALSE);
X	}
X	if (strcpy(termname, getenv("TERM")) == NULL) {
X		fprintf(stderr,"Can't get TERM variable\n");
X		return(FALSE);
X	}
X	if ((err = tgetent(_terminal, termname)) != 1) {
X		fprintf(stderr,"Can't get entry for TERM\n");
X		return(FALSE);
X	}
X
X	/* load in all those pesky values */
X	_clearscreen       = tgetstr("cl", &ptr);
X	_moveto            = tgetstr("cm", &ptr);
X	_cleartoeoln       = tgetstr("ce", &ptr);
X	_cleartoeos        = tgetstr("cd", &ptr);
X	_lines	      	   = tgetnum("li");
X	_columns	   = tgetnum("co");
X	_setinverse        = tgetstr("so", &ptr);
X	_clearinverse      = tgetstr("se", &ptr);
X	_setunderline      = tgetstr("us", &ptr);
X	_clearunderline    = tgetstr("ue", &ptr);
X
X	if (!_clearscreen) {
X		fprintf(stderr,
X			"Terminal must have clearscreen (cl) capability\n");
X		return(FALSE);
X	}
X	if (!_moveto) {
X		fprintf(stderr,
X			"Terminal must have cursor motion (cm)\n");
X		return(FALSE);
X	}
X	if (!_cleartoeoln) {
X		fprintf(stderr,
X			"Terminal must have clear to end-of-line (ce)\n");
X		return(FALSE);
X	}
X	if (!_cleartoeos) {
X		fprintf(stderr,
X			"Terminal must have clear to end-of-screen (cd)\n");
X		return(FALSE);
X	}
X	if (_lines == -1)
X		_lines = DEFAULT_LINES_ON_TERMINAL;
X	if (_columns == -1)
X		_columns = DEFAULT_COLUMNS_ON_TERMINAL;
X
X	for (i = 0; i < K_MAX; ++i)
X		_spckey[i] = tgetstr (_spckeytc[i], &ptr);
X
X	return(TRUE);
X}
X
XScreenSize(lines, columns)
Xint *lines, *columns;
X{
X	/** returns the number of lines and columns on the display. **/
X
X	if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL;
X	if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL;
X
X	*lines = _lines - 1;		/* assume index from zero*/
X	*columns = _columns;		/* assume index from one */
X}
X
XClearScreen()
X{
X	/* clear the screen: returns -1 if not capable */
X
X	tputs(_clearscreen, 1, outchar);
X	fflush(stdout);      /* clear the output buffer */
X}
X
XMoveCursor(row, col)
Xint row, col;
X{
X	/** move cursor to the specified row column on the screen.
X            0,0 is the top left! **/
X
X	char *stuff, *tgoto();
X
X	stuff = tgoto(_moveto, col, row);
X	tputs(stuff, 1, outchar);
X	fflush(stdout);
X}
X
XCleartoEOLN()
X{
X	/** clear to end of line **/
X
X	tputs(_cleartoeoln, 1, outchar);
X	fflush(stdout);  /* clear the output buffer */
X}
X
XCleartoEOS()
X{
X	/** clear to end of screen **/
X
X	tputs(_cleartoeos, 1, outchar);
X	fflush(stdout);  /* clear the output buffer */
X}
X
XStartInverse()
X{
X	/** set inverse video mode **/
X
X	if (_setinverse && inverse_okay)
X		tputs(_setinverse, 1, outchar);
X/*	fflush(stdout);	*/
X}
X
X
XEndInverse()
X{
X	/** compliment of startinverse **/
X
X	if (_clearinverse && inverse_okay)
X		tputs(_clearinverse, 1, outchar);
X/*	fflush(stdout);	*/
X}
X
X#if 0
XStartUnderline()
X{
X	/** start underline mode **/
X
X	if (!_setunderline)
X		return(-1);
X
X	tputs(_setunderline, 1, outchar);
X	fflush(stdout);
X	return(0);
X}
X
X
XEndUnderline()
X{
X	/** the compliment of start underline mode **/
X
X	if (!_clearunderline)
X		return(-1);
X
X	tputs(_clearunderline, 1, outchar);
X	fflush(stdout);
X	return(0);
X}
X#endif
X
XRawState()
X{
X	/** returns either 1 or 0, for ON or OFF **/
X
X	return( _inraw );
X}
X
XRaw(state)
Xint state;
X{
X	/** state is either TRUE or FALSE, as indicated by call **/
X
X	if (state == FALSE && _inraw) {
X#ifndef	OSK
X	  (void) ioctl(TTYIN, TCSETAW, &_original_tty);
X#else	/* OSK */
X	  (void) _ss_opt (TTYIN, &_original_tty);
X#endif	/* OSK */
X	  _inraw = 0;
X	}
X	else if (state == TRUE && ! _inraw) {
X
X#ifndef	OSK
X	  (void) ioctl(TTYIN, TCGETA, &_original_tty);	/** current setting **/
X
X	  (void) ioctl(TTYIN, TCGETA, &_raw_tty);    /** again! **/
X#ifdef BSD
X	  _raw_tty.sg_flags &= ~(ECHO | CRMOD);	/* echo off */
X	  _raw_tty.sg_flags |= CBREAK;	/* raw on    */
X#else
X	  _raw_tty.c_lflag &= ~(ICANON | ECHO);	/* noecho raw mode        */
X
X	  _raw_tty.c_cc[VMIN] = '\01';	/* minimum # of chars to queue    */
X	  _raw_tty.c_cc[VTIME] = '\0';	/* minimum time to wait for input */
X
X#endif
X	  (void) ioctl(TTYIN, TCSETAW, &_raw_tty);
X#else	/* OSK */
X	  (void) _gs_opt (TTYIN, &_original_tty);
X	  _raw_tty = _original_tty;
X	  _raw_tty.sg_echo = 0;
X	  _raw_tty.sg_pause = 0;
X	  _raw_tty.sg_alf = 0;
X	  _raw_tty.sg_psch = 0;
X	  _raw_tty.sg_eofch = 0;
X	  
X	  (void) _ss_opt (TTYIN, &_raw_tty);
X#endif	/* OSK */
X
X	  _inraw = 1;
X	}
X}
X
Xint
XReadCh()
X{
X	/** read a character with Raw mode set! **/
X
X	register int result;
X	char ch;
X	register int i, j, n;
X	register char *spc;
X
X	spc = NULL;
X	do {
X		if (spc) {
X#ifdef	OSK
X			tsleep (4);
X			if (_gs_rdy (0) < 1)
X				tsleep (50 | (1 << 31));
X			if (_gs_rdy (0) < 1)
X#else	/* OSK */
X#ifdef	FIONREAD
X#define	CH_READ		FIONREAD
X#else	/* FIONREAD */
X#ifdef	TCRDCHK
X#define	CH_READ		TCRDCHK
X#else	/* TCRDCHK */
X	Panic!
X#endif	/* TCRDCHK */
X#endif	/* FIONREAD */
X			int tmp;
X
X			ioctl (0, CH_READ, &tmp);
X			if (tmp < 1)
X				nap (200);
X			ioctl (0, CH_READ, &tmp);
X			if (tmp < 1)
X#endif	/* OSK */
X				spc = NULL;
X		}
X		if ((result = read(0, &ch, 1)) != 1)
X			break;
X		if (!spc) {
X			for (i = 0; i < K_MAX; ++i)
X				if (_spckey[i] && (_spckey[i][0] == ch)) {
X					spc = _spckey[i];
X					n = 1;
X					break;
X				}
X		} else if (ch != spc[n]) {
X			for (j = 0; j < K_MAX; ++j)
X				if (!_spckey[j])
X					continue;
X				else if (spc[0]) {
X					if ((!strncmp (spc, _spckey[j], n - 1)) && (_spckey[j][n] == ch))
X						break;
X				} else if (spc[0] == _spckey[j][0]) {
X					if (n > 1) {
X						if ((!strncmp (spc + 1, _spckey[j] + 1, n - 2)) && (_spckey[j][n] == ch))
X							break;
X					} else if (_spckey[j][n] == ch)
X						break;
X				}
X			if (j < K_MAX) {
X				spc = _spckey[j];
X				i = j;
X				++n;
X			} else {
X				spc = NULL;
X				result = -1;
X			}
X		} else
X			++n;
X		if (spc && (!spc[n])) {
X			spc = NULL;
X			switch (i) {
X				case K_UP:
X					ch = ctrl('P');
X					break;
X				case K_DOWN:
X					ch = ctrl('N');
X					break;
X				case K_LEFT:
X					ch = ctrl('B');
X					break;
X				case K_RIGHT:
X					ch = ctrl('F');
X					break;
X				case K_PUP:
X					ch = ctrl('Z');
X					break;
X				case K_PDOWN:
X					ch = ctrl('V');
X					break;
X			}
X		}
X	} while (spc || (result < 0));
X        return((result <= 0 ) ? EOF : ch & 0x7F);
X}
X
X
Xoutchar(c)
Xchar c;
X{
X	/** output the given character.  From tputs... **/
X	/** Note: this CANNOT be a macro!              **/
X
X	putc(c, stdout);
X}
X
X#ifdef	NO_NAP
Xnap (n)
Xint n;
X{
X	int	t;
X
X	for (t = 100 * n; t > 0; --t)
X		;
X}
X#endif	/* NO_NAP */
__END__OF__THIS__FILE__
if test -f 'group.c' ; then
  echo 'File group.c already exists, overwriting it'
  del 'group.c'
fi
echo Extracting \"'group.c'\"
sed "s/^X//" >'group.c' <<'__END__OF__THIS__FILE__'
X
X
X#include	<stdio.h>
X#include	<signal.h>
X#ifdef		REGEXP
X#include	<regexp.h>
X#endif		/* REGEXP */
X#ifdef	OSK
X#include	<ctype.h>
X#endif	/* OSK */
X#include	"tass.h"
X
X#ifdef	GET_DIST
Xextern char	*index ();
X#endif	/* GET_DIST */
X
Xint index_point;
Xint first_subj_on_screen;
Xint last_subj_on_screen;
Xchar subject_search_string[LEN+1];	/* last search pattern */
Xextern int cur_groupnum;
Xextern int last_resp;		/* page.c */
Xextern int this_resp;		/* page.c */
Xextern int space_mode;		/* select.c */
Xextern char *cvers;
X
Xchar *glob_group;
X
X
X#ifdef SIGTSTP
Xvoid
Xgroup_susp(i)
Xint i;
X{
X
X	Raw(FALSE);
X	putchar('\n');
X	signal(SIGTSTP, SIG_DFL);
X	kill(0, SIGTSTP);
X
X	signal(SIGTSTP, group_susp);
X	Raw(TRUE);
X	mail_setup();
X	show_group_page(glob_group, TRUE);
X}
X#endif
X
X
Xgroup_page(group)
Xchar *group;
X{
X	char ch;
X	int i, n;
X	char group_path[200];
X	char *p;
X	char buf[200];
X
X	glob_group = group;
X
X	strcpy(group_path, group);		/* turn comp.unix.amiga into */
X	for (p = group_path; *p; p++)		/* comp/unix/amiga */
X		if (*p == '.')
X			*p = '/';
X#ifdef	OSK
X		else if ((!isalnum (*p)) || ((unsigned char) *p >= 0x80))
X			*p = '_';
X#endif	/* OSK */
X
X	last_resp = -1;
X	this_resp = -1;
X	index_group(group, group_path);		/* update index file */
X	read_newsrc_line(group);		/* get sequencer information */
X
X	if (space_mode) {
X		for (i = 0; i < top_base; i++)
X			if (new_responses(i))
X				break;
X		if (i < top_base)
X			index_point = i;
X		else
X			index_point = top_base - 1;
X	} else
X		index_point = top_base - 1;
X
X	show_group_page(group, TRUE);
X
X	while (1) {
X		if ((n = ReadCh()) == EOF)
X			longjmp (jmp_buffer, 1);
X		ch = (char) n;
X
X		if (ch > '0' && ch <= '9') {	/* 0 goes to basenote */
X			prompt_subject_num(ch, group);
X		} else switch (ch) {
X			case 'I':	/* toggle inverse video */
X				inverse_okay = !inverse_okay;
X				if (inverse_okay)
X					info_message("Inverse video enabled");
X				else
X					info_message("Inverse video disabled");
X				break;
X
X			case 's':	/* subscribe to this group */
X				subscribe(group, ':', my_group[cur_groupnum],
X									TRUE);
X				sprintf(buf, "subscribed to %s", group);
X				info_message(buf);
X				break;
X
X			case 'u':	/* unsubscribe to this group */
X				subscribe(group, '!', my_group[cur_groupnum],
X									TRUE);
X				sprintf(buf, "unsubscribed to %s", group);
X				info_message(buf);
X				break;
X
X			case 'g':	/* choose a new group by name */
X				n = choose_new_group();
X				if (n >= 0 && n != cur_groupnum) {
X					fix_new_highest(cur_groupnum);
X					cur_groupnum = n;
X					index_point = -3;
X					goto group_done;
X				}
X				break;
X
X			case 'c':	/* catchup--mark all articles as read */
X			    if (prompt_yn("Mark everything as read? (y/n): ")) {
X				for (n = 0; n < top; n++)
X					arts[n].unread = 0;
X				show_group_page(group, FALSE);
X				info_message("All articles marked as read");
X			    }
X			    break;
X
X#ifdef	USE_ARROW
X			case 27:	/* common arrow keys */
X				ch = ReadCh();
X				if (ch == '[' || ch == 'O')
X					ch = ReadCh();
X				switch (ch) {
X				case 'A':
X				case 'D':
X				case 'i':
X					goto group_up;
X
X				case 'B':
X				case 'I':
X				case 'C':
X					goto group_down;
X				}
X				break;
X#endif	/* USE_ARROW */
X
X			case 'n':	/* next group */
X				clear_message();
X				if (cur_groupnum + 1 >= local_top)
X					info_message("No more groups");
X				else {
X					fix_new_highest(cur_groupnum);
X					cur_groupnum++;
X					index_point = -3;
X					space_mode = FALSE;
X					goto group_done;
X				}
X				break;
X
X			case 'p':	/* previous group */
X				clear_message();
X				if (cur_groupnum <= 0)
X					info_message("No previous group");
X				else {
X					fix_new_highest(cur_groupnum);
X					cur_groupnum--;
X					index_point = -3;
X					space_mode = FALSE;
X					goto group_done;
X				}
X				break;
X
X			case ' ':
X				if (top_base <= 0)
X					info_message("*** No Articles ***");
X				else if (last_subj_on_screen == top_base)
X					info_message("*** End of Articles ***");
X				else
X					clear_message();
X				break;
X
X			case '\t':
X				fix_new_highest(cur_groupnum);
X				space_mode = TRUE;
X
X				if (index_point < 0
X				|| (n=next_unread((int) base[index_point]))<0) {
X					for (i = cur_groupnum+1;
X							i < local_top; i++)
X						if (unread[i] > 0)
X							break;
X					if (i >= local_top)
X						goto group_done;
X
X					cur_groupnum = i;
X					index_point = -3;
X					goto group_done;
X				}
X				index_point = show_page(n, group, group_path);
X				if (index_point < 0)
X					goto group_done;
X				show_group_page(group, TRUE);
X				break;
X	
X
X			case 'N':	/* go to next unread article */
X				if (index_point < 0) {
X					info_message("No next unread article");
X					break;
X				}
X
X				n = next_unread( (int) base[index_point]);
X				if (n == -1)
X					info_message("No next unread article");
X				else {
X					index_point =
X						show_page(n, group, group_path, TRUE);
X					if (index_point < 0) {
X						fix_new_highest(cur_groupnum);
X						space_mode = FALSE;
X						goto group_done;
X					}
X					show_group_page(group, TRUE);
X				}
X				break;
X
X			case 'P':	/* go to previous unread article */
X				if (index_point < 0) {
X				    info_message("No previous unread article");
X				    break;
X				}
X
X				n = prev_response( (int) base[index_point]);
X				n = prev_unread(n);
X				if (n == -1)
X				    info_message("No previous unread article");
X				else {
X					index_point =
X						show_page(n, group, group_path, TRUE);
X					if (index_point < 0) {
X						fix_new_highest(cur_groupnum);
X						space_mode = FALSE;
X						goto group_done;
X					}
X					show_group_page(group, TRUE);
X				}
X				break;
X
X			case 'w':	/* post a basenote */
X				post_base(group);
X				update_newsrc(group, my_group[cur_groupnum]);
X				index_group(group, group_path);
X				read_newsrc_line(group);
X				index_point = top_base - 1;
X				show_group_page(group, TRUE);
X				break;
X
X			case 't':	/* return to group selection page */
X			case ctrl('B'):	/* cursor key */
X				fix_new_highest(cur_groupnum);
X				goto group_done;
X
X#ifndef	OSK
X			case '\r':
X#else	/* OSK */
X			case '\l':
X#endif	/* OSK */
X			case '\n':	/* read current basenote */
X			case ctrl('F'):	/* cursor key */
X				if (index_point < 0) {
X					info_message("*** No Articles ***");
X					break;
X				}
X				index_point = show_page((int) base[index_point],
X							group, group_path, TRUE);
X				if (index_point < 0) {
X					fix_new_highest(cur_groupnum);
X					space_mode = FALSE;
X					goto group_done;
X				}
X				show_group_page(group, TRUE);
X				break;
X
X			case ctrl('D'):		/* page down */
X			case ctrl('V'):		/* full page down */
X				if (!top_base || index_point == top_base - 1)
X					break;
X
X				erase_subject_arrow();
X				index_point += (ch == ctrl('V')) ? 
X						NOTESLINES :
X						NOTESLINES / 2;
X				if (index_point >= top_base)
X					index_point = top_base - 1;
X
X				if (index_point < first_subj_on_screen
X				|| index_point >= last_subj_on_screen)
X					show_group_page(group, TRUE);
X				else
X					draw_subject_arrow();
X				break;
X
X			case '-':	/* go to last viewed article */
X				if (this_resp < 0) {
X					info_message("No last message");
X					break;
X				}
X				index_point = show_page(this_resp,
X							group, group_path, TRUE);
X				if (index_point < 0) {
X					fix_new_highest(cur_groupnum);
X					space_mode = FALSE;
X					goto group_done;
X				}
X				show_group_page(group, TRUE);
X				break;
X
X			case ctrl('U'):		/* page up */
X			case ctrl('Z'):		/* full page up */
X				if (!top_base)
X					break;
X
X				erase_subject_arrow();
X				index_point -= (ch == ctrl('Z')) ?
X						NOTESLINES :
X						NOTESLINES / 2;
X				if (index_point < 0)
X					index_point = 0;
X				if (index_point < first_subj_on_screen
X				|| index_point >= last_subj_on_screen)
X					show_group_page(group, TRUE);
X				else
X					draw_subject_arrow();
X				break;
X
X			case 'v':
X				info_message(cvers);
X				break;
X
X			case '!':
X				shell_escape();
X				show_group_page(group, TRUE);
X				break;
X
X			case ctrl('N'):
X			case 'j':		/* line down */
X#ifdef	USE_ARROW
Xgroup_down:
X#endif	/* USE_ARROW */
X				if (!top_base || index_point + 1 >= top_base)
X					break;
X
X				erase_subject_arrow();
X				index_point++;
X				if (index_point >= last_subj_on_screen)
X					show_group_page(group, FALSE);
X				else
X					draw_subject_arrow();
X				break;
X
X			case ctrl('P'):
X			case 'k':		/* line up */
X#ifdef	USE_ARROW
Xgroup_up:
X#endif	/* USE_ARROW */
X				if (!top_base || !index_point)
X					break;
X
X				erase_subject_arrow();
X				index_point--;
X				if (index_point + 1 <= first_subj_on_screen)
X					show_group_page(group, FALSE);
X				else
X					draw_subject_arrow();
X				break;
X
X			case ctrl('R'):
X			case ctrl('L'):
X			case ctrl('W'):
X			case 'i':		/* return to index */
X					show_group_page(group, FALSE);
X					break;
X
X			case '/':		/* forward search */
X					search_subject(TRUE, group);
X					break;
X
X			case '?':		/* backward search */
X					search_subject(FALSE, group);
X					break;
X
X#ifndef	OSK
X			case 'q':		/* quit */
X					index_point = -2;
X					fix_new_highest(cur_groupnum);
X					space_mode = FALSE;
X					goto group_done;
X#endif	/* OSK */
X
X			case 'h':
X				tass_group_help();
X				show_group_page(group, TRUE);
X				break;
X
X			default:
X			    info_message("Bad command.  Type 'h' for help.");
X		}
X	}
X
Xgroup_done:
X	update_newsrc(group, my_group[cur_groupnum]);
X
X	if (index_point == -2)
X		tass_done(0);
X}
X
X
X/*
X *  Correct highest[] for the group selection page display since
X *  new articles may have been read or marked unread
X */
X
Xfix_new_highest(groupnum)
Xint groupnum;
X{
X	int i;
X	int sum = 0;
X
X	for (i = 0; i < top; i++)
X		if (arts[i].unread)
X			sum++;
X
X	unread[groupnum] = sum;
X}
X
X
Xshow_mail () {
X	if (mail_check()) {			/* you have mail message in */
X		MoveCursor(0, COLS - 14);		/* upper right */
X		printf("you have mail");
X		fflush (stdout);
X	}
X}
X
Xshow_group_page(group, clr)
Xchar *group;
Xint clr;
X{
X	int i;
X	int n;
X	char resps[10];
X	char new_resps;
X	int respnum;
X
X#ifdef SIGTSTP
X	signal(SIGTSTP, group_susp);
X#endif
X
X	if (clr) {
X		ClearScreen();
X		printf("%s\r\012", nice_time());	/* time in upper left */
X		center_line(1, group);
X	}
X
X	show_mail ();
X
X	MoveCursor(INDEX_TOP, 0);
X
X	first_subj_on_screen = (index_point / NOTESLINES) * NOTESLINES;
X	if (first_subj_on_screen < 0)
X		first_subj_on_screen = 0;
X
X	last_subj_on_screen = first_subj_on_screen + NOTESLINES;
X	if (last_subj_on_screen >= top_base) {
X		last_subj_on_screen = top_base;
X		first_subj_on_screen = top_base - NOTESLINES;
X
X		if (first_subj_on_screen < 0)
X			first_subj_on_screen = 0;
X	}
X
X	for (i = first_subj_on_screen; i < last_subj_on_screen; i++) {
X		if (new_responses(i))
X			new_resps = '+';
X		else
X			new_resps = ' ';
X
X		n = nresp(i);
X		if (n)
X			sprintf(resps, "%4d", n);
X		else
X			strcpy(resps, "    ");
X
X		respnum = base[i];
X
X		MoveCursor(INDEX_TOP + i - first_subj_on_screen, 3);
X		printf("%4d %c %s", i + 1, new_resps, arts[respnum].subject);
X		CleartoEOLN ();
X		MoveCursor(INDEX_TOP + i - first_subj_on_screen, COLS - 35);
X		printf ("%s %-.30s", resps, arts[respnum].from);
X		MoveCursor(INDEX_TOP + i - first_subj_on_screen, COLS - 1);
X		CleartoEOLN ();
X	}
X
X	if (top_base <= 0)
X		info_message("*** No Articles ***");
X	else if (last_subj_on_screen == top_base)
X		info_message("*** End of Articles ***");
X
X	if (top_base > 0)
X		draw_subject_arrow();
X}
X
Xdraw_subject_arrow() {
X
X	draw_arrow(INDEX_TOP + (index_point-first_subj_on_screen) );
X}
X
Xerase_subject_arrow() {
X
X	erase_arrow(INDEX_TOP + (index_point-first_subj_on_screen) );
X}
X
X
Xprompt_subject_num(ch, group)
Xchar ch;
Xchar *group;
X{
Xint num;
X
X
X	clear_message();
X
X	if ((num = parse_num(ch, "Read article> ")) == -1) {
X		clear_message();
X		return FALSE;
X	}
X	num--;		/* index from 0 (internal) vs. 1 (user) */
X
X	if (num >= top_base)
X		num = top_base - 1;
X
X	if (num >= first_subj_on_screen
X	&&  num < last_subj_on_screen) {
X		erase_subject_arrow();
X		index_point = num;
X		draw_subject_arrow();
X	} else {
X		index_point = num;
X		show_group_page(group, TRUE);
X	}
X}
X
Xvoid
Xsearch_subject(forward, group)
Xint forward;
Xchar *group;
X{
X	char buf[LEN+1];
X	int i;
X#ifndef	REGEXP
X	extern char *regcmp();
X	extern char *regex();
X	char *re;
X#else	/* REGEXP */
X	regexp *re;
X#endif	/* REGEXP */
X	char *prompt;
X
X	clear_message();
X
X	if (forward)
X		prompt = "/";
X	else
X		prompt = "?";
X
X	if (!parse_string(prompt, buf))
X		return;
X
X	if (strlen(buf))
X		strcpy(subject_search_string, buf);
X	else if (!strlen(subject_search_string)) {
X		info_message("No search pattern");
X		return;
X	}
X
X	i = index_point;
X
X	glob_name(subject_search_string, buf);
X
X#ifndef	REGEXP
X	if ((re = regcmp(buf, NULL)) == NULL) {
X#else	/* REGEXP */
X	if ((re = regcomp (buf)) == NULL) {
X#endif	/* REGEXP */
X		info_message("Bad search pattern");
X		return;
X	}
X
X	do {
X		if (forward)
X			i++;
X		else
X			i--;
X
X		if (i >= top_base)
X			i = 0;
X		if (i < 0)
X			i = top_base - 1;
X
X#ifndef	REGEXP
X		if (regex(re, arts[ base[i] ].subject) != NULL) {
X#else	/* REGEXP */
X		if (regexec (re, arts[ base[i] ].subject) != NULL) {
X#endif	/* REGEXP */
X			if (i >= first_subj_on_screen
X			&&  i < last_subj_on_screen) {
X				erase_subject_arrow();
X				index_point = i;
X				draw_subject_arrow();
X			} else {
X				index_point = i;
X				show_group_page(group, TRUE);
X			}
X			return;
X		}
X	} while (i != index_point);
X
X	info_message("No match");
X}
X
X
X/*
X *  Post an original article (not a followup)
X */
X
Xpost_base(group)
Xchar *group;
X{
X	FILE *fp;
X	char nam[100];
X	char ch;
X	char subj[LEN+1];
X	char buf[200];
X	int line;
X#ifdef	GET_DIST
X	char *ptr;
X#endif	/* GET_DIST */
X
X	if (!parse_string("Subject: ", subj))
X		return(FALSE);
X	if (subj[0] == '\0')
X		return(FALSE);
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	sprintf(nam, "%s/.article", homedir);
X	umask (omask);
X	if ((fp = fopen(nam, "w")) == NULL) {
X		umask (0);
X		fprintf(stderr, "can't open %s: ", nam);
X		perror("");
X		return(FALSE);
X	}
X#ifndef	OSK
X	chmod(nam, 0600);
X#else	/* OSK */
X	chmod(nam, 013);
X#endif	/* OSK */
X
X	fprintf(fp, "Subject: %s\n", subj);
X	fprintf(fp, "Newsgroups: %s\n", group);
X#ifndef	GET_DIST
X/*	fprintf(fp, "Distribution: \n");	*/
X	line = 2;
X#else	/* GET_DIST */
X	strcpy (buf, group);
X	if (ptr = index (buf, '.'))
X		*ptr = '\0';
X	fprintf(fp, "Distribution: %s\n", buf);
X	line = 3;
X#endif	/* GET_DIST */
X	if (*my_org) {
X		fprintf(fp, "Organization: %s\n", my_org);
X		++line;
X	}
X	fprintf(fp, "\n");
X	++line;
X
X#ifdef	APPEND_SIG
X	add_signature (fp);
X#endif	/* APPEND_SIG */
X
X	fclose(fp);
X	umask (0);
X
X	ch = 'e';
X	while (1) {
X		switch (ch) {
X		case 'e':
X			invoke_editor(nam, line);
X			break;
X
X		case 'a':
X			return FALSE;
X
X		case 'p':
X			printf("\rPosting...  ");
X			CleartoEOLN ();
X#ifndef	OSK
X			sprintf(buf, "%s/inews -h < %s", LIBDIR, nam);
X#else	/* OSK */
X			sprintf(buf, "inews -h <%s", nam);
X#endif	/* OSK */
X			if (invoke_cmd(buf)) {
X				printf("article posted ");
X				fflush(stdout);
X				goto post_base_done;
X			} else {
X				printf("article rejected ");
X				fflush(stdout);
X				break;
X			}
X		}
X
X		do {
X			MoveCursor(LINES, 0);
X			fputs("abort, edit, post: ", stdout);
X			fflush(stdout);
X			ch = ReadCh();
X		} while (ch != 'a' && ch != 'e' && ch != 'p');
X	}
X
Xpost_base_done:
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X
X	continue_prompt();
X
X	return(TRUE);
X}
X
X
X/*
X *  Return the number of unread articles there are within a thread
X */
X
Xnew_responses(thread)
Xint thread;
X{
X	int i;
X	int sum = 0;
X
X	for (i = base[thread]; i >= 0; i = arts[i].thread)
X		if (arts[i].unread)
X			sum++;
X
X	return sum;
X}
X
Xvoid
Xtass_group_help() {
X	char ch;
X
Xgroup_help_start:
X
X	ClearScreen();
X	center_line(0, TASS_HEADER);
X	center_line(1, "Index Page Commands (page 1 of 2)");
X
X	MoveCursor(3, 0);
X
X	printf("4        Select article 4\r\012");
X	printf("^D, ^V   half / full Page down\r\012");
X	printf("^U, ^Z   half / full  Page up\r\012");
X	printf("<CR>     Read current article\r\012");
X	printf("<TAB>    View next unread article or group\r\012");
X	printf("c        Mark all articles as read\r\012");
X	printf("g        Choose a new group by name\r\012");
X	printf("j        Down a line\r\012");
X	printf("k        Up a line\r\012");
X	printf("n        Go to next group\r\012");
X	printf("N        Go to next unread article\r\012");
X	printf("p        Go to previous group\r\012");
X	printf("P        Go to previous unread article\r\012");
X	printf("q        Quit\r\012");
X
X	center_line(LINES, "-- hit space for more commands --");
X	ch = ReadCh();
X	if (ch != ' ')
X		return;
X
X	ClearScreen();
X	center_line(0, TASS_HEADER);
X	center_line(1, "Index Page Commands (page 2 of 2)");
X
X	MoveCursor(3, 0);
X
X	printf("s        Subscribe to this group\r\012");
X	printf("t        Return to group selection index\r\012");
X	printf("u        Unsubscribe to this group\r\012");
X	printf("w        Post an article\r\012");
X	printf("/        Search forward for subject\r\012");
X	printf("?        Search backward for subject\r\012");
X	printf("-        Show last message\r\012");
X
X	center_line(LINES, "-- hit any key --");
X	ch = ReadCh();
X	if (ch == 'b')
X		goto group_help_start;
X}
__END__OF__THIS__FILE__
exit 0
: end of shell archive

-- 
Frank Kaefer # fkk@stasys.sta.sub.org # Starnberg, Germany
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads