🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: de.comp.sources.os9
Article #14

Tass for OS-9 Part02/03

#14
From: Frank Kaefer
Date: Thu, 24 Dec 1992 13:05
3489 lines
72853 bytes
Submitted-by: Ulrich Dessauer <ud@Nightmare.ddt.sub.org>
Archive-name: tass/part02

: ----- Cut here ----- Cut here ----- Cut here ----- Cut here -----
: Use  sh filename  to extract shell archive
: This shell archive contains following files:
:     'mail.c                                        836 bytes'
:     'main.c                                      23086 bytes'
:     'misc.c                                       8468 bytes'
:     'page.c                                      31558 bytes'
: 
if test -f 'mail.c' ; then
  echo 'File mail.c already exists, overwriting it'
  del 'mail.c'
fi
echo Extracting \"'mail.c'\"
sed "s/^X//" >'mail.c' <<'__END__OF__THIS__FILE__'
X
X#include	<stdio.h>
X#ifndef		OSK
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#else		/* OSK */
X#include	<types.h>
X#include	<stat.h>
Xtypedef long	off_t;
X#endif		/* OSK */
X#include	"tass.h"
X
Xchar *mailbox_name = NULL;
Xoff_t mailbox_size;
X
X
X/*
X *  Record size of mailbox so we can detect if new mail has arrived
X */
X
Xmail_setup() {
X	struct stat buf;
X	extern char *getenv();
X
X	if (do_mail_check) {
X		if (mailbox_name == NULL)
X			mailbox_name = getenv("MAIL");
X
X		if (mailbox_name == NULL)
X			mailbox_size = 0;
X		else {
X			if (stat(mailbox_name, &buf) >= 0)
X				mailbox_size = buf.st_size;
X			else
X				mailbox_size = 0;
X		}
X	}
X}
X
X
X/*
X *  Return TRUE if new mail has arrived
X */
X
Xmail_check() {
X	struct stat buf;
X
X	if (mailbox_name != NULL
X	&&  stat(mailbox_name, &buf) >= 0
X	&&  mailbox_size < buf.st_size)
X		return TRUE;
X
X	return FALSE;
X}
X
__END__OF__THIS__FILE__
if test -f 'main.c' ; then
  echo 'File main.c already exists, overwriting it'
  del 'main.c'
fi
echo Extracting \"'main.c'\"
sed "s/^X//" >'main.c' <<'__END__OF__THIS__FILE__'
X
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 */
X
X#include	<stdio.h>
X#include	<signal.h>
X#ifdef	OSK
X#include	<modes.h>
X#include	<pwd.h>
X#else	/* OSK */
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#endif	/* OSK */
X#include	"tass.h"
X
X#ifdef	OSK
Xextern char	*index ();
X
Xextern
X#endif	/* OSK */
Xint LINES, COLS;
X
Xint max_active;
Xstruct group_ent *active;		/* active file */
Xint group_hash[TABLE_SIZE];		/* group name --> active[] */
Xint *my_group;				/* .newsrc --> active[] */
Xint *unread;				/* highest art read in group */
Xint num_active;				/* one past top of active */
Xint local_top;				/* one past top of my_group */
Xint update = FALSE;			/* update index files only mode */
X
Xstruct header *arts;
Xlong *base;
Xint max_art;
Xint top = 0;
Xint top_base;
X
X#ifdef	USE_UID
Xint tass_uid;
Xint tass_gid;
Xint real_uid;
Xint real_gid;
X#ifdef	OSK
X#define	DEF_TASSUID	(39 | (1 << 16))
X#endif	/* OSK */
X#endif	/* USE_UID */
X
Xint local_index;			/* do private indexing? */
X
Xint do_mail_check;			/* if we want to check for mail */
X
Xint omask;
X
Xjmp_buf jmp_buffer;
X
Xchar *cvers = "Tass 3.0  (c) Copyright 1991 by Rich Skrenta.  All rights reserved";
X
X
X#ifdef SIGTSTP
Xvoid
Xmain_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, main_susp);
X	mail_setup();
X	Raw(TRUE);
X}
X#endif
X
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	extern int optind, opterr;
X	extern char *optarg;
X	int errflag = 0;
X	int i;
X	int c;
X#ifdef	OSK
X	struct passwd	*pw;
X#endif	/* OSK */
X
X	for (i = 0; i < TABLE_SIZE; i++)
X		group_hash[i] = -1;
X
X#ifdef	SIGPIPE
X	signal(SIGPIPE, SIG_IGN);
X#endif	/* SIGPIPE */
X#ifdef SIGTSTP
X	signal(SIGTSTP, main_susp);
X#endif
X#ifdef	SIGQUIT
X	signal(SIGQUIT, SIG_IGN);
X#endif	/* SIGQUIT */
X#ifdef	SIGINT
X	signal(SIGINT, SIG_IGN);
X#endif	/* SIGINT */
X
X#ifdef	USE_UID
X#ifndef	OSK
X	tass_uid = geteuid();
X	tass_gid = getegid();
X	real_uid = getuid();
X	real_gid = getgid();
X#else	/* OSK */
X	real_uid = getuid ();
X	setpwent ();
X	if (pw = getpwnam ("news"))
X		tass_uid = pw->pw_uid | (pw->pw_gid << 16);
X	else
X		tass_uid = DEF_TASSUID;
X	endpwent ();
X	setup_convtable (TRUE, TRUE);
X	setuid (tass_uid);
X#endif	/* OSK */
X#endif	/* USE_UID */
X
X	omask = umask (0);
X	init_selfinfo();	/* set up char *'s: homedir, newsrc, etc. */
X	init_alloc();		/* allocate initial array sizes */
X
X#ifdef	USE_UID
X	if (tass_uid == real_uid) {	/* run out of someone's account */
X#endif	/* USE_UID */
X		local_index = FALSE;	/* index in their home directory */
X		if (access (indexdir, S_IFDIR) == -1)
X#ifndef	OSK
X			mkdir(indexdir, 0755);
X#else	/* OSK */
X			makdir (indexdir, S_IREAD | S_IWRITE, 033);
X#endif	/* OSK */
X#ifdef	USE_UID
X	} else			/* we're setuid, index in /usr/spool/news */
X		local_index = FALSE;
X#endif	/* USE_UID */
X
X	do_mail_check = FALSE;
X
X	read_active();		/* load the active file into active[] */
X
X	while ((c = getopt(argc, argv, "f:ugm")) != -1) {
X		switch(c) {
X		case 'f':
X			strcpy(newsrc, optarg);
X			break;
X
X		case 'u':
X			update = TRUE;
X			break;
X
X		case 'g':
X			local_index = TRUE;
X			break;
X
X		case 'm':
X			do_mail_check = TRUE;
X			break;
X
X		case '?':
X		default:
X			errflag++;
X		}
X	}
X
X	if (errflag) {
X	    fprintf(stderr, "usage: tass [options] [newsgroups]\n");
X	    fprintf(stderr, "   -f file   use file instead of $HOME/.newsrc\n");
X	    fprintf(stderr, "   -u        update index files only\n");
X	    fprintf(stderr, "   -g        don't use global instead of local index files\n");
X	    fprintf(stderr, "   -m        check for new mail while reading news\n");
X	    exit(1);
X	}
X
X	if (!update)
X		printf("Tass 3.0\n");
X
X	if (optind < argc) {
X		while (optind < argc) {
X			if (add_group(argv[optind], TRUE) < 0)
X				fprintf(stderr,
X					"group %s not found in active file\n",
X								argv[optind]);
X			optind++;
X		}
X	} else
X		read_newsrc(TRUE);
X
X	if (update) {			/* index file updater only */
X		do_update();
X		exit(0);
X	}
X
X	if (InitScreen() == FALSE) {
X		fprintf(stderr,"Screen initialization failed\n");
X		exit(1);
X	}
X
X	if (!setjmp (jmp_buffer)) {
X		ScreenSize(&LINES, &COLS);
X		Raw(TRUE);
X
X		mail_setup();		/* record mailbox size for "you have mail" */
X		selection_index();
X	}
X
X	tass_done(0);
X}
X
X
X
Xtass_done(ret)
Xint ret;
X{
X
X	MoveCursor(LINES, 0);
X	printf("\r\012");
X	Raw(FALSE);
X	umask (omask);
X	exit(ret);
X}
X
X
X/*
X *  Dynamic table management
X *  These settings are memory conservative:  small initial allocations
X *  and a 50% expansion on table overflow.  A fast vm system with
X *  much memory might want to start with higher initial allocations
X *  and a 100% expansion on overflow, especially for the arts[] array.
X */
X
Xinit_alloc() {
X
X	max_active = 100;	/* initial alloc */
X
X	active = (struct group_ent *) my_malloc(sizeof(*active) * max_active);
X	my_group = (int *) my_malloc(sizeof(int) * max_active);
X	unread = (int *) my_malloc(sizeof(int) * max_active);
X
X	max_art = 300;		/* initial alloc */
X
X	arts = (struct header *) my_malloc(sizeof(*arts) * max_art);
X	base = (long *) my_malloc(sizeof(long) * max_art);
X}
X
X
Xexpand_art() {
X
X	max_art += max_art / 2;		/* increase by 50% */
X
X	arts = (struct header *) my_realloc(arts, sizeof(*arts) * max_art);
X	base = (long *) my_realloc(base, sizeof(long) * max_art);
X}
X
X
Xexpand_active() {
X
X	max_active += max_active / 2;		/* increase by 50% */
X
X	active = (struct group_ent *) my_realloc(active,
X						sizeof(*active) * max_active);
X	my_group = (int *) my_realloc(my_group, sizeof(int) * max_active);
X	unread = (int *) my_realloc(unread, sizeof(int) * max_active);
X}
X
X
X/*
X *  Load the active file into active[]
X */
X
Xread_active()
X{
X	FILE *fp;
X	char *p, *q;
X	char buf[200];
X	long h;
X	int i;
X
X	num_active = 0;
X
X	if ((fp = fopen(active_file, "r")) == NULL) {
X		fprintf(stderr, "can't open %s: ", active_file);
X		perror("");
X		exit(1);
X	}
X
X	while (fgets(buf, 200, fp) != NULL) {
X		for (p = buf; *p && *p != ' '; p++) ;
X		if (*p != ' ') {
X#ifndef	OSK
X			fprintf(stderr, "active file corrupt\n");
X#else	/* OSK */
X			fprintf(stderr, "active file corrupt in line '%s'\n", buf);
X#endif	/* OSK */
X			continue;
X		}
X
X		*p++ = '\0';
X
X		if (num_active >= max_active)
X			expand_active();
X
X		{		/* hash group name for fast lookup later */
X			char *t = buf;
X
X			h = *t++;
X			while (*t)
X				h = (h * 64 + *t++) % TABLE_SIZE;
X		}
X
X		if (group_hash[h] == -1)
X			group_hash[h] = num_active;
X		else {				/* hash linked list chaining */
X			for (i = group_hash[h]; active[i].next >= 0;
X						i = active[i].next) {
X				if (strcmp(active[i].name, buf) == 0)
X					goto read_active_continue;
X							/* kill dups */
X			}
X			if (strcmp(active[i].name, buf) == 0)
X				goto read_active_continue;
X			active[i].next = num_active;
X		}
X
X		for (q = p; *q && *q != ' '; q++) ;
X		if (*q != ' ') {
X			fprintf(stderr, "active file corrupt\n");
X			continue;
X		}
X
X		active[num_active].name = str_save(buf);
X
X		active[num_active].max = atol(p);
X		active[num_active].min = atol(q);
X
X		active[num_active].next = -1;	    /* hash chaining */
X		active[num_active].flag = NOTGOT;   /* not in my_group[] yet */
X
X		num_active++;
X
Xread_active_continue:;
X
X	}
X
X	fclose(fp);
X}
X
X
X
X/*
X *  Read $HOME/.newsrc into my_group[].  my_group[] ints point to
X *  active[] entries.  Sub_only determines whether we just read
X *  subscribed groups or all of them.
X */
Xvoid
Xread_newsrc(sub_only)
Xint sub_only;		/* TRUE=subscribed groups only, FALSE=all groups */
X{
X	FILE *fp;
X	char *p;
X	char buf[8192];
X	char c;
X	int i;
X
X	local_top = 0;
X
X#ifdef	USE_UID
X	setuid(real_uid);	/* become the user to write in his */
X	setgid(real_gid);	/* home directory */
X#endif	/* USE_UID */
X
X	fp = fopen(newsrc, "r");
X	if (fp == NULL) {		/* attempt to make a .newsrc */
X#ifdef	USE_UID
X		setuid(tass_uid);
X		setgid(tass_gid);
X#endif	/* USE_UID */
X		for (i = 0; i < num_active; i++) {
X			if (local_top >= max_active)
X				expand_active();
X			my_group[local_top] = i;
X			active[i].flag = 0;
X#if 0
X			unread[local_top] = active[i].max - active[i].min;
X#else
X			unread[local_top] = -1;
X#endif
X			local_top++;
X		}
X		write_newsrc();
X		return;
X	}
X
X	while (fgets(buf, 8192, fp) != NULL) {
X		p = buf;
X		while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
X			p++;
X		c = *p;
X		*p++ = '\0';
X		if (c == '!' && sub_only)
X			continue;		/* unsubscribed */
X
X		if ((i = add_group(buf, FALSE)) < 0) {
X		    fprintf(stderr, "group %s not found in active file\n", buf);
X		    continue;
X		}
X
X		if (c != '!')		/* if we're subscribed to it */
X			active[my_group[i]].flag |= SUBS;
X
X		unread[i] = parse_unread(p, my_group[i]);
X	}
X	fclose(fp);
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X}
X
X
X/*
X *  Write a new newsrc from my_group[] and active[]
X *  Used to a create a new .newsrc if there isn't one already, or when
X *  the newsrc is reset.
X */
X
Xwrite_newsrc() {
X	FILE *fp;
X	int i;
X
X#ifdef	USE_UID
X	setuid(real_uid);	/* become the user to write in his */
X	setgid(real_gid);	/* home directory */
X#endif	/* USE_UID */
X
X	umask (omask);
X	fp = fopen(newsrc, "w");
X	if (fp == NULL)
X		goto write_newsrc_done;
X
X	for (i = 0; i < num_active; i++)
X		fprintf(fp, "%s: \n", active[i].name);
X
X	fclose(fp);
X
Xwrite_newsrc_done:
X	umask (0);
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X}
X
X
X/*
X *  Load the sequencer rang lists and mark arts[] according to the
X *  .newsrc info for a particular group.  i.e.  rec.arts.comics: 1-94,97
X */
X
Xread_newsrc_line(group)
Xchar *group;
X{
X	FILE *fp;
X	char buf[8192];
X	char *p;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	fp = fopen(newsrc, "r");
X	if (fp == NULL)
X		goto read_newsrc_line_done;
X
X	while (fgets(buf, 8192, fp) != NULL) {
X		p = buf;
X		while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
X			p++;
X		*p++ = '\0';
X		if (strcmp(buf, group) != 0)
X			continue;
X		parse_seq(p);
X		break;
X	}
X
X	fclose(fp);
Xread_newsrc_line_done:
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#else	/* USE UID */
X	;
X#endif	/* USE_UID */
X}
X
X
X/*
X *  For our current group, update the sequencer information in .newsrc
X */
X
Xupdate_newsrc(group, groupnum)
Xchar *group;
Xint groupnum;			/* index into active[] for this group */
X{
X	FILE *fp;
X	FILE *newfp;
X	char buf[8192];
X	char *p;
X	char c;
X	int gotit = FALSE;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	umask (omask);
X	fp = fopen(newsrc, "r");
X	newfp = fopen(newnewsrc, "w");
X	if (newfp == NULL)
X		goto update_done;
X
X	if (fp != NULL) {
X		while (fgets(buf, 8192, fp) != NULL) {
X			for (p = buf; *p; p++)
X				if (*p == '\n') {
X					*p = '\0';
X					break;
X				}
X
X			p = buf;
X			while (*p && *p != ' ' && *p != ':' && *p != '!')
X					p++;
X			c = *p;
X			if (c != '\0')
X				*p++ = '\0';
X
X			if (c != '!')
X				c = ':';
X
X			if (strcmp(buf, group) == 0) {
X				fprintf(newfp, "%s%c ", buf, c);
X				gotit = TRUE;
X				print_seq(newfp, groupnum);
X				fprintf(newfp, "\n");
X			} else
X				fprintf(newfp, "%s%c%s\n", buf, c, p);
X		}
X		fclose(fp);
X	}
X
X	fclose(newfp);
X	unlink(newsrc);
X#ifndef	OSK
X	link(newnewsrc, newsrc);
X	unlink(newnewsrc);
X#else	/* OSK */
X	rename(newnewsrc, newsrc);
X#endif	/* OSK */
X
Xupdate_done:
X	umask (0);
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X}
X
X
X/*
X *  Subscribe/unsubscribe to a group in .newsrc.  ch should either be
X *  '!' to unsubscribe or ':' to subscribe.  num is the group's index
X *  in active[].
X */
X
Xsubscribe(group, ch, num, out_seq)
Xchar *group;
Xchar ch;
Xint num;
Xint out_seq;				/* output sequencer info? */
X{
X	FILE *fp;
X	FILE *newfp;
X	char buf[8192];
X	char *p;
X	char c;
X	int gotit = FALSE;
X
X	if (ch == '!')
X		active[num].flag &= ~SUBS;
X	else
X		active[num].flag |= SUBS;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	umask (omask);
X	fp = fopen(newsrc, "r");
X	newfp = fopen(newnewsrc, "w");
X	if (newfp == NULL)
X		goto update_done;
X
X	if (fp != NULL) {
X		while (fgets(buf, 8192, fp) != NULL) {
X			for (p = buf; *p; p++)
X				if (*p == '\n') {
X					*p = '\0';
X					break;
X				}
X
X			p = buf;
X			while (*p && *p != ' ' && *p != ':' && *p != '!')
X					p++;
X			c = *p;
X			if (c != '\0')
X				*p++ = '\0';
X
X			if (c != '!')
X				c = ':';
X
X			if (strcmp(buf, group) == 0) {
X				fprintf(newfp, "%s%c%s\n", buf, ch, p);
X				gotit = TRUE;
X			} else
X				fprintf(newfp, "%s%c%s\n", buf, c, p);
X		}
X		fclose(fp);
X	}
X
X	if (!gotit) {
X		if (out_seq) {
X			fprintf(newfp, "%s%c ", group, ch);
X			print_seq(newfp, num);
X			fprintf(newfp, "\n");
X		} else
X			fprintf(newfp, "%s%c\n", group, ch);
X	}
X
X	fclose(newfp);
X	unlink(newsrc);
X#ifndef	OSK
X	link(newnewsrc, newsrc);
X	unlink(newnewsrc);
X#else	/* OSK */
X	rename(newnewsrc, newsrc);
X#endif	/* OSK */
X
Xupdate_done:
X	umask (0);
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X}
X
Xvoid
Xprint_seq(fp, groupnum)
XFILE *fp;
Xint groupnum;			/* index into active[] for this group */
X{
X	int i;
X	int flag = FALSE;
X
X	if (top <= 0) {
X		if (active[groupnum].min > 1) {
X			fprintf(fp, "1-%ld", active[groupnum].min);
X			fflush(fp);
X		}
X		return;
X	}
X
X	i = 0;
X	if (arts[0].artnum > 1) {
X		for (; i < top && !arts[i].unread; i++) ;
X		if (i > 0)
X			fprintf(fp, "1-%ld", arts[i-1].artnum);
X		else
X			fprintf(fp, "1-%ld", arts[0].artnum - 1);
X		flag = TRUE;
X	}
X
X	for (; i < top; i++) {
X		if (!arts[i].unread) {
X			if (flag)
X				fprintf(fp, ",");
X			else
X				flag = TRUE;
X			fprintf(fp, "%ld", arts[i].artnum);
X			if (i+1 < top && !arts[i+1].unread) {
X				while (i+1 < top && !arts[i+1].unread)
X					i++;
X				fprintf(fp, "-%ld", arts[i].artnum);
X			}
X		}
X	}
X
X	if (!flag && active[groupnum].min > 1)
X		fprintf(fp, "1-%ld", active[groupnum].min);
X	fflush(fp);
X}
X
X
Xparse_seq(s)
Xchar *s;
X{
X	long low, high;
X	int i;
X
X	while (*s) {
X		while (*s && (*s < '0' || *s > '9'))
X			s++;
X
X		if (*s && *s >= '0' && *s <= '9') {
X			low = atol(s);
X			while (*s && *s >= '0' && *s <= '9')
X				s++;
X			if (*s == '-') {
X				s++;
X				high = atol(s);
X				while (*s && *s >= '0' && *s <= '9')
X					s++;
X			}  else
X				high = low;
X
X			for (i = 0; i < top; i++)
X				if (arts[i].artnum >= low &&
X				    arts[i].artnum <= high)
X					arts[i].unread = 0;
X		}
X	}
X}
X
X
Xparse_unread(s, groupnum)
Xchar *s;
Xint groupnum;			/* index for group in active[] */
X{
X	long low, high;
X	long last_high;
X	int i;
X	int sum = 0;
X	int gotone = FALSE;
X	int n;
X
X/*
X *  Read the first range from the .newsrc sequencer information.  If the
X *  top of the first range is higher than what the active file claims is
X *  the bottom, use it as the new bottom instead
X */
X
X	high = 0;
X	if (*s) {
X		while (*s && (*s < '0' || *s > '9'))
X			s++;
X
X		if (*s && *s >= '0' && *s <= '9') {
X			low = atol(s);
X			while (*s && *s >= '0' && *s <= '9')
X				s++;
X			if (*s == '-') {
X				s++;
X				high = atol(s);
X				while (*s && *s >= '0' && *s <= '9')
X					s++;
X			}  else
X				high = low;
X			gotone = TRUE;
X		}
X	}
X
X	if (high < active[groupnum].min)
X		high = active[groupnum].min;
X
X#ifdef	OSK
X	if (! gotone) {
X		high = active [groupnum].min;
X		low = high;
X		gotone = low != active[groupnum].max;
X	}
X#endif	/* OSK */
X
X	while (*s) {
X		last_high = high;
X
X		while (*s && (*s < '0' || *s > '9'))
X			s++;
X
X		if (*s && *s >= '0' && *s <= '9') {
X			low = atol(s);
X			while (*s && *s >= '0' && *s <= '9')
X				s++;
X			if (*s == '-') {
X				s++;
X				high = atol(s);
X				while (*s && *s >= '0' && *s <= '9')
X					s++;
X			}  else
X				high = low;
X
X			if (low > last_high)	/* otherwise seq out of order */
X				sum += (low - last_high) - 1;
X		}
X	}
X
X	if (gotone) {
X		if (active[groupnum].max > high)
X			sum += active[groupnum].max - high;
X		return sum;
X	}
X
X	n = (int) (active[groupnum].max - active[groupnum].min);
X	if (n < 2)
X		return 0;
X
X	return -1;
X}
X
X
Xget_line_unread(group, groupnum)
Xchar *group;
Xint groupnum;				/* index for group in active[] */
X{
X	FILE *fp;
X	char buf[8192];
X	char *p;
X	int ret = -1;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	fp = fopen(newsrc, "r");
X	if (fp == NULL) {
X#ifdef	USE_UID
X		setuid(tass_uid);
X		setgid(tass_gid);
X#endif	/* USE_UID */
X
X		return -1;
X	}
X
X	while (fgets(buf, 8192, fp) != NULL) {
X		p = buf;
X		while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
X			p++;
X		*p++ = '\0';
X		if (strcmp(buf, group) != 0)
X			continue;
X		ret = parse_unread(p, groupnum);
X		break;
X	}
X
X	fclose(fp);
X
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X
X	return ret;
X}
X
X
Xreset_newsrc()
X{
X	FILE *fp;
X	FILE *newfp;
X	char buf[8192];
X	char *p;
X	char c;
X	int gotit = FALSE;
X	int i;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	umask (omask);
X	fp = fopen(newsrc, "r");
X	newfp = fopen(newnewsrc, "w");
X	if (newfp == NULL)
X		goto update_done;
X
X	if (fp != NULL) {
X		while (fgets(buf, 8192, fp) != NULL) {
X			for (p = buf; *p && *p != '\n'; p++) ;
X			*p = '\0';
X
X			p = buf;
X			while (*p && *p != ' ' && *p != ':' && *p != '!')
X					p++;
X			c = *p;
X			if (c != '\0')
X				*p++ = '\0';
X
X			if (c != '!')
X				c = ':';
X
X			fprintf(newfp, "%s%c\n", buf, c);
X		}
X		fclose(fp);
X	}
X
X	fclose(newfp);
X	unlink(newsrc);
X#ifndef	OSK
X	link(newnewsrc, newsrc);
X	unlink(newnewsrc);
X#else	/* OSK */
X	rename(newnewsrc, newsrc);
X#endif	/* OSK */
X
Xupdate_done:
X	umask (0);
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X
X	for (i = 0; i < local_top; i++)
X		unread[i] = -1;
X}
X
X
Xdelete_group(group)
Xchar *group;
X{
X	FILE *fp;
X	FILE *newfp;
X	char buf[8192];
X	char *p;
X	char c;
X	int gotit = FALSE;
X	FILE *del;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	umask (omask);
X	fp = fopen(newsrc, "r");
X	newfp = fopen(newnewsrc, "w");
X	if (newfp == NULL)
X		goto del_done;
X	del = fopen(delgroups, "a+");
X	if (del == NULL)
X		goto del_done;
X
X	if (fp != NULL) {
X		while (fgets(buf, 8192, fp) != NULL) {
X			for (p = buf; *p && *p != '\n'; p++) ;
X			*p = '\0';
X
X			p = buf;
X			while (*p && *p != ' ' && *p != ':' && *p != '!')
X					p++;
X			c = *p;
X			if (c != '\0')
X				*p++ = '\0';
X
X			if (c != '!')
X				c = ':';
X
X			if (strcmp(buf, group) == 0) {
X				fprintf(del, "%s%c%s\n", buf, c, p);
X				gotit = TRUE;
X			} else
X				fprintf(newfp, "%s%c%s\n", buf, c, p);
X		}
X		fclose(fp);
X	}
X
X	fclose(newfp);
X
X	if (!gotit)
X		fprintf(del, "%s! \n", group);
X
X	fclose(del);
X	unlink(newsrc);
X#ifndef	OSK
X	link(newnewsrc, newsrc);
X	unlink(newnewsrc);
X#else	/* OSK */
X	rename(newnewsrc, newsrc);
X#endif	/* OSK */
X
Xdel_done:
X	umask (0);
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X}
X
X
Xundel_group() {
X	FILE *del;
X	FILE *newfp;
X	FILE *fp;
X	char buf[2][8192];
X	char *p;
X	int which = 0;
X	long h;
X	extern int cur_groupnum;
X	int i, j;
X	char c;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	del = fopen(delgroups, "r");
X	if (del == NULL) {
X#ifdef	USE_UID
X		setuid(tass_uid);
X		setgid(tass_gid);
X#endif	/* USE_UID */
X		return FALSE;
X	}
X	unlink(delgroups);
X	umask (omask);
X	newfp = fopen(delgroups, "w");
X	if (newfp == NULL) {
X		umask (0);
X#ifdef	USE_UID
X		setuid(tass_uid);
X		setgid(tass_gid);
X#endif	/* USE_UID */
X		return FALSE;
X	}
X
X	buf[0][0] = '\0';
X	buf[1][0] = '\0';
X
X	while (fgets(buf[which], 8192, del) != NULL) {
X		which = !which;
X		if (*buf[which])
X			fputs(buf[which], newfp);
X	}
X
X	fclose(del);
X	fclose(newfp);
X	which = !which;
X
X	if (!*buf[which]) {
X		umask (0);
X#ifdef	USE_UID
X		setuid(tass_uid);
X		setgid(tass_gid);
X#endif	/* USE_UID */
X		return FALSE;
X	}
X
X	for (p = buf[which]; *p && *p != '\n'; p++) ;
X	*p = '\0';
X
X	p = buf[which];
X	while (*p && *p != ' ' && *p != ':' && *p != '!')
X		p++;
X	c = *p;
X	if (c != '\0')
X		*p++ = '\0';
X
X	if (c != '!')
X		c = ':';
X
X	{			/* find the hash of the group name */
X		char *t = buf[which];
X
X		h = *t++;
X		while (*t)
X			h = (h * 64 + *t++) % TABLE_SIZE;
X	}
X
X	for (i = group_hash[h]; i >= 0; i = active[i].next) {
X		if (strcmp(buf[which], active[i].name) == 0) {
X			for (j = 0; j < local_top; j++)
X				if (my_group[j] == i) {
X					umask (0);
X#ifdef	USE_UID
X					setuid(tass_uid);
X					setgid(tass_gid);
X#endif	/* USE_UID */
X					return j;
X				}
X
X			active[i].flag &= ~NOTGOT;   /* mark that we got it */
X			if (c != '!')
X				active[i].flag |= SUBS;
X
X			if (local_top >= max_active)
X				expand_active();
X			local_top++;
X			for (j = local_top; j > cur_groupnum; j--) {
X				my_group[j] = my_group[j-1];
X				unread[j] = unread[j-1];
X			}
X			my_group[cur_groupnum] = i;
X			unread[cur_groupnum] = parse_unread(p, i);
X
X			fp = fopen(newsrc, "r");
X			if (fp == NULL) {
X				umask (0);
X#ifdef	USE_UID
X				setuid(tass_uid);
X				setgid(tass_gid);
X#endif	/* USE_UID */
X				return FALSE;
X			}
X			newfp = fopen(newnewsrc, "w");
X			if (newfp == NULL) {
X				fclose(fp);
X				umask (0);
X#ifdef	USE_UID
X				setuid(tass_uid);
X				setgid(tass_gid);
X#endif	/* USE_UID */
X				return FALSE;
X			}
X			i = 0;
X			while (fgets(buf[!which], 8192, fp) != NULL) {
X				for (p = buf[!which]; *p && *p != '\n'; p++) ;
X				*p = '\0';
X
X				p = buf[!which];
X				while (*p && *p!=' ' && *p != ':' && *p != '!')
X					p++;
X				c = *p;
X				if (c != '\0')
X					*p++ = '\0';
X
X				if (c != '!')
X					c = ':';
X
X				while (i < cur_groupnum) {
X					if (strcmp(buf[!which],
X					  active[my_group[i]].name) == 0) {
X						fprintf(newfp, "%s%c%s\n",
X							buf[!which], c, p);
X						goto foo_cont;
X					}
X					i++;
X				}
X				fprintf(newfp, "%s%c%s\n", buf[which], c, p);
X				fprintf(newfp, "%s%c%s\n", buf[!which], c, p);
X				break;
Xfoo_cont:;
X			}
X
X			while (fgets(buf[!which], 8192, fp) != NULL)
X				fputs(buf[!which], newfp);
X
X			fclose(newfp);
X			fclose(fp);
X			unlink(newsrc);
X#ifndef	OSK
X			link(newnewsrc, newsrc);
X			unlink(newnewsrc);
X#else	/* OSK */
X			rename(newnewsrc, newsrc);
X#endif	/* OSK */
X			umask (0);
X#ifdef	USE_UID
X			setuid(tass_uid);
X			setgid(tass_gid);
X#endif	/* USE_UID */
X			return TRUE;
X		}
X	}
X
X	umask (0);
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X
X	return FALSE;
X}
X
Xvoid
Xmark_group_read(group, groupnum)
Xchar *group;
Xint groupnum;			/* index into active[] for this group */
X{
X	FILE *fp;
X	FILE *newfp;
X	char buf[8192];
X	char *p;
X	char c;
X	int gotit = FALSE;
X
X	if (active[groupnum].max < 2)
X		return;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	umask (omask);
X	fp = fopen(newsrc, "r");
X	newfp = fopen(newnewsrc, "w");
X	if (newfp == NULL)
X		goto mark_group_read_done;
X
X	if (fp != NULL) {
X		while (fgets(buf, 8192, fp) != NULL) {
X			for (p = buf; *p; p++)
X				if (*p == '\n') {
X					*p = '\0';
X					break;
X				}
X
X			p = buf;
X			while (*p && *p != ' ' && *p != ':' && *p != '!')
X					p++;
X			c = *p;
X			if (c != '\0')
X				*p++ = '\0';
X
X			if (c != '!')
X				c = ':';
X
X			if (strcmp(buf, group) == 0) {
X				fprintf(newfp, "%s%c 1-%ld\n", buf, c,
X						active[groupnum].max);
X				gotit = TRUE;
X			} else
X				fprintf(newfp, "%s%c%s\n", buf, c, p);
X		}
X		fclose(fp);
X	}
X
X	fclose(newfp);
X	unlink(newsrc);
X#ifndef	OSK
X	link(newnewsrc, newsrc);
X	unlink(newnewsrc);
X#else	/* OSK */
X	rename(newnewsrc, newsrc);
X#endif	/* OSK */
X
Xmark_group_read_done:
X	umask (0);
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X}
__END__OF__THIS__FILE__
if test -f 'misc.c' ; then
  echo 'File misc.c already exists, overwriting it'
  del 'misc.c'
fi
echo Extracting \"'misc.c'\"
sed "s/^X//" >'misc.c' <<'__END__OF__THIS__FILE__'
X
X#include	<stdio.h>
X#include	<signal.h>
X#include	<pwd.h>
X#ifndef		OSK
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#else		/* OSK */
X#include	<types.h>
X#include	<stat.h>
X#endif		/* OSK */
X#include	"tass.h"
X
Xextern char *rindex ();
X
Xchar active_file[LEN];
X#ifdef	OSK
Xchar spooldir[LEN];
X#endif	/* OSK */
Xchar homedir[LEN];
Xchar userid[LEN];
Xchar delgroups[LEN];
Xchar newsrc[LEN];
Xchar newnewsrc[LEN];
Xchar indexdir[LEN];
Xchar my_org[LEN];		/* organization */
X#ifdef	INCSTR
Xchar *incstr;
X#endif	/* INCSTR */
X
X/*
X *  Which base note (an index into base[]) does a respnum
X *  (an index into arts[]) corresponsd to?
X *
X *  In other words, base[] points to an entry in arts[] which is
X *  the head of a thread, linked with arts[].thread.  For any q: arts[q],
X *  find i such that base[i]->arts[n]->arts[o]->...->arts[q]
X */
X
Xwhich_base(n)
Xint n;
X{
X	int i, j;
X
X	for (i = 0; i < top_base; i++)
X		for (j = base[i]; j >= 0; j = arts[j].thread)
X			if (j == n)
X				return i;
X
X	fprintf(stderr, "can't find base article\n");
X	return 0;
X}
X
X
X/*
X *  Find how deep in a thread a response is.  Start counting at zero
X */
X
Xwhich_resp(n)
Xint n;
X{
X	int i, j;
X	int num = 0;
X
X	i = which_base(n);
X
X	for (j = base[i]; j != -1; j = arts[j].thread)
X		if (j == n)
X			break;
X		else
X			num++;
X
X	return num;
X}
X
X
X/*
X *  Given an index into base[], find the number of responses for
X *  that basenote
X */
X
Xnresp(n)
Xint n;
X{
X	int i;
X	int oldi = -3;
X	int sum = 0;
X
X	assert(n < top_base);
X
X	for (i = base[n]; i != -1; i = arts[i].thread) {
X		assert(i != -2);
X		assert(i != oldi);
X		oldi = i;
X		sum++;
X	}
X
X	return sum - 1;
X}
X
X
Xasfail(file, line, cond)
Xchar	*file;
Xint	line;
Xchar	*cond;
X{
X	fprintf(stderr, "tass: assertion failure: %s (%d): %s\n",
X							file, line, cond);
X	exit(1);
X}
X
X
X/*
X *  Make regular expressions pleasant for the masses:  glob them
X */
X
Xglob_name(group, grp)
Xchar *group;
Xchar *grp;
X{
Xchar *p, *q;
X
X/*
X *  Prefix the .'s in the group name so they won't be interpreted
X *  as regular expression commands.  Change 'all' into '*'
X */
X
X	p = group;
X	q = grp;
X
X	if (strncmp(p, "all", 3) == 0 && (p[3] == '.' || p[3] == '\0')) {
X		*q++ = '.';
X		*q++ = '*';
X		p = &p[3];
X	}
X	while (*p != '\0') {
X		if (*p == '.') {
X			*q++ = '\\';
X			*q++ = '.';
X			p++;
X
X			if (strncmp(p, "all", 3) == 0 &&
X				(p[3] == '.' || p[3] == '\0')) {
X					*q++ = '.';
X					*q++ = '*';
X					p = &p[3];
X				}
X		} else if (*p == '*') {
X			*q++ = '.';
X			*q++ = '*';
X			p++;
X		} else
X			*q++ = *p++;
X	}
X	*q = '\0';
X}
X
X
X/*
X * init_selfinfo
X *   Deterimines users home directory, userid, and a path
X *   for an rc file in the home directory
X */
X
Xinit_selfinfo()
X{
X	struct passwd *myentry;
X	extern struct passwd *getpwuid();
X	struct stat sb;
X	char nam[LEN];
X	char *p;
X	extern char *getenv();
X	FILE *fp;
X
X#ifndef	OSK
X	myentry = getpwuid(getuid());
X#else	/* OSK */
X	setpwent ();
X#ifdef	USE_UID
X	myentry = getpwuid(real_uid () & 0xffff);
X#else	/* USE_UID */
X	myentry = getpwuid(getuid () & 0xffff);
X#endif	/* USE_UID */
X	endpwent ();
X#endif	/* OSK */
X	strcpy(userid, myentry->pw_name);
X	strcpy(homedir, myentry->pw_dir);
X
X	sprintf(newsrc, "%s/.newsrc", homedir);
X	sprintf(newnewsrc, "%s/.newnewsrc", homedir);
X	sprintf(delgroups, "%s/.delgroups", homedir);
X	sprintf(indexdir, "%s/.tindex", homedir);
X#ifndef	OSK
X	sprintf(active_file, "%s/active", LIBDIR);
X#else	/* OSK */
X	if (!info_str ("MNEWS.LIB", active_file, LEN - 20))
X		strcpy (active_file, LIBDIR);
X	strcat (active_file, "/Active");
X	if (!info_str ("MNEWS.DIR", spooldir, LEN))
X		strcpy (spooldir, SPOOLDIR);
X#endif	/* OSK */
X#ifdef	INCSTR
X	if (!(incstr = getenv ("INCSTR")))
X		incstr = "> ";
X#endif	/* INCSTR */
X
X	if (stat(active_file, &sb) >= 0)
X		goto got_active;
X
X#ifndef	OSK
X/*
X *  I hate forgetting to define LIBDIR correctly.  Guess a
X *  couple of likely places if it's not where LIBDIR says it is.
X */
X
X	strcpy(active_file, "/usr/lib/news/active");
X	if (stat(active_file, &sb) >= 0)
X		goto got_active;
X
X	strcpy(active_file, "/usr/local/lib/news/active");
X	if (stat(active_file, &sb) >= 0)
X		goto got_active;
X
X	strcpy(active_file, "/usr/public/lib/news/active");
X	if (stat(active_file, &sb) >= 0)
X		goto got_active;
X
X/*
X *  Oh well.  Revert to what LIBDIR says it is to produce a
X *  useful error message when read_active() fails later.
X */
X
X	sprintf(active_file, "%s/active", LIBDIR);
X#endif	/* OSK */
X
Xgot_active:
X
X	*my_org = '\0';
X	p = getenv("ORGANIZATION");
X	if (p != NULL) {
X		strcpy(my_org, p);
X		goto got_org;
X	}
X
X#ifndef	OSK
X	sprintf(nam, "%s/organization", LIBDIR);
X	fp = fopen(nam, "r");
X
X	if (fp == NULL) {
X		sprintf(nam, "/usr/lib/news/organization");
X		fp = fopen(nam, "r");
X	}
X
X	if (fp == NULL) {
X		sprintf(nam, "/usr/local/lib/news/organization");
X		fp = fopen(nam, "r");
X	}
X
X	if (fp == NULL) {
X		sprintf(nam, "/usr/public/lib/news/organization");
X		fp = fopen(nam, "r");
X	}
X
X	if (fp == NULL) {
X		sprintf(nam, "/etc/organization");
X		fp = fopen(nam, "r");
X	}
X
X	if (fp != NULL) {
X		if (fgets(my_org, LEN, fp) != NULL) {
X			for (p = my_org; *p && *p != '\n'; p++) ;
X			*p = '\0';
X		}
X		fclose(fp);
X	}
X#endif	/* OSK */
X
Xgot_org:;
X
X}
X
X
Xchar *
Xmy_malloc(size)
Xunsigned size;
X{
X	char *p;
X	extern char *malloc();
X
X	p = malloc(size);
X	if (p == NULL) {
X		fprintf(stderr, "tass: out of memory\n");
X		exit(1);
X	}
X	return p;
X}
X
X
Xchar *
Xmy_realloc(p, size)
Xchar *p;
Xunsigned size;
X{
X	extern char *malloc();
X	extern char *realloc();
X
X	if (p == NULL)
X		p = malloc(size);
X	else
X		p = realloc(p, size);
X
X	if (p == NULL) {
X		fprintf(stderr, "tass: out of memory\n");
X		exit(1);
X	}
X	return p;
X}
X
X
Xchar *
Xstr_save(s)
Xchar *s;
X{
Xchar *p;
X
X	assert(s != NULL);
X	
X	p = my_malloc(strlen(s) + 1);
X	strcpy(p, s);
X
X	return(p);
X}
X
X
Xcopy_fp(a, b, prefix)
XFILE *a;
XFILE *b;
Xchar *prefix;
X{
X	char buf[8192];
X
X	while (fgets(buf, 8192, a) != NULL) {
X#ifdef	MNEWS
X		conv_charset (buf, strlen (buf));
X#endif	/* MNEWS */
X		fprintf(b, "%s%s", prefix, buf);
X	}
X}
X
X
Xchar *
Xget_val(env, def)
Xchar *env;		/* Environment variable we're looking for	*/
Xchar *def;		/* Default value if no environ value found	*/
X{
X	extern char *getenv();
X	char *ptr;
X
X	if ((ptr = getenv(env)) != NULL)
X		return(ptr);
X	else
X		return(def);
X}
X
X
Xinvoke_editor(nam, line)
Xchar *nam;
Xint line;
X{
X	char buf[200];
X	static int first = TRUE;
X	static char editor[200];
X	static int type = -1;
X	char *ptr;
X
X	if (first) {
X#ifndef	OSK
X		strcpy(editor, get_val("EDITOR", "/usr/bin/vi"));
X#else	/* OSK */
X		strcpy(editor, get_val("EDITOR", "me"));
X#endif	/* OSK */
X		if (ptr = rindex (editor, '/'))
X			++ptr;
X		else
X			ptr = editor;
X		if (!strncmp (ptr, "vi", 2))
X			type = 0;
X		else if ((!strncmp (ptr, "me", 2)) || (!strncmp (ptr, "emacs")))
X			type = 1;
X		first = FALSE;
X	}
X
X	++line;
X	switch (type) {
X		case -1:
X		default:
X			sprintf(buf, "%s %s", editor, nam);
X			break;
X		case 0:
X			sprintf (buf, "%s +%d %s", editor, line, nam);
X			break;
X		case 1:
X			sprintf (buf, "%s -g=%d %s", editor, line, nam);
X			break;
X	}
X	printf("\r%s", buf);
X	CleartoEOLN ();
X	return invoke_cmd(buf);
X}
X
X
Xinvoke_cmd(nam)
Xchar *nam;
X{
X	int ret;
X#ifdef SIGTSTP
X	void (*susp)();
X#endif
X
X	Raw(FALSE);
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X#ifndef	OSK
X#ifdef SIGTSTP
X	susp = signal(SIGTSTP, SIG_DFL);
X#endif
X
X	ret = system(nam);
X
X#ifdef SIGTSTP
X	signal(SIGTSTP, susp);
X#endif
X#else	/* OSK */
X	ret = system (nam);
X#endif	/* OSK */
X
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X	Raw(TRUE);
X
X	return ret == 0;
X}
X
X
Xshell_escape() {
X	char shell[LEN];
X	char *p;
X#ifdef SIGTSTP
X	void (*susp)();
X#endif
X
X	if (!parse_string("!", shell))
X		strcpy(shell, get_val("SHELL", "/bin/sh"));
X
X	for (p = shell; *p && (*p == ' ' || *p == '\t'); p++) ;
X
X	if (!*p)
X		strcpy(shell, get_val("SHELL", "/bin/sh"));
X	
X	Raw(FALSE);
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	fputs("\r\012", stdout);
X
X#ifndef	OSK
X#ifdef SIGTSTP
X	susp = signal(SIGTSTP, SIG_DFL);
X#endif
X
X	system(p);
X
X#ifdef SIGTSTP
X	signal(SIGTSTP, susp);
X#endif
X#else	/* OSK */
X	system (p);
X#endif	/* OSK */
X
X#ifdef	USE_UID
X	setuid(tass_uid);
X	setgid(tass_gid);
X#endif	/* USE_UID */
X
X	Raw(TRUE);
X
X	continue_prompt();
X	mail_setup();
X}
X
X
X/*
X *  Find the next unread response in this group 
X */
X
Xnext_unread(n)
Xint n;
X{
X
X	while (n >= 0) {
X		if (arts[n].unread == 1)
X			return n;
X		n = next_response(n);
X	}
X
X	return -1;
X}
X
X
X/*
X *  Find the previous unread response in this thread
X */
X
Xprev_unread(n)
Xint n;
X{
X
X	while (n >= 0) {
X		if (arts[n].unread == 1)
X			return n;
X		n = prev_response(n);
X	}
X
X	return -1;
X}
X
__END__OF__THIS__FILE__
if test -f 'page.c' ; then
  echo 'File page.c already exists, overwriting it'
  del 'page.c'
fi
echo Extracting \"'page.c'\"
sed "s/^X//" >'page.c' <<'__END__OF__THIS__FILE__'
X
X#include	<stdio.h>
X#include	<signal.h>
X#ifndef	OSK
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#else	/* OSK */
X#include	<types.h>
X#include	<stat.h>
X
Xextern FILE	*popen ();
X#endif	/* OSK */
X#include	"tass.h"
X
X#define		MAX_PAGES	1000
X#define		NOTE_UNAVAIL	-1
X
Xchar note_h_path[LEN];			/* Path:	*/
Xchar note_h_date[LEN];			/* Date:	*/
Xchar note_h_subj[LEN];			/* Subject:	*/
Xchar note_h_from[LEN];			/* From:	*/
Xchar note_h_reply[LEN];			/* Reply-To:	*/
Xchar note_h_org[LEN];			/* Organization: */
Xchar note_h_newsgroups[LEN];		/* Newsgroups:	*/
Xchar note_h_messageid[LEN];		/* Message-ID:	*/
Xchar note_h_distrib[LEN];		/* Distribution: */
Xchar note_h_followup[LEN];		/* Followup-To: */
X
Xint	note_line;
Xint	note_page;		/* what page we're on */
Xlong	note_mark[MAX_PAGES];	/* ftells on beginnings of pages */
XFILE	*note_fp;		/* the body of the current article */
Xint	note_end;		/* we're done showing this article */
Xint	rotate;			/* 0=normal, 13=rot13 decode */
X
Xstruct stat note_stat;		/* so we can tell how big it is */
X
Xchar	note_full_name[100];
Xchar	note_from_addr[100];
X
X
Xint last_resp;		/* current & previous article for - command */
Xint this_resp;
X
Xint glob_respnum;
Xchar *glob_page_group;
Xextern int cur_groupnum;
X
X
X#ifdef SIGTSTP
Xvoid
Xpage_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, page_susp);
X	mail_setup();
X	Raw(TRUE);
X	redraw_page(glob_respnum, glob_page_group);
X}
X#endif
X
X
Xshow_page(respnum, group, group_path)
Xint respnum;
Xchar *group;
Xchar *group_path;
X{
X	char ch;
X	int n;
X	long art;
X	int sav_unread;
X
Xrestart:
X
X	glob_respnum = respnum;
X	glob_page_group = group;
X
X#ifdef SIGTSTP
X	signal(SIGTSTP, page_susp);
X#endif
X
X	if (respnum != this_resp) {	   /* remember current & previous */
X		last_resp = this_resp;	   /* articles for - command */
X		this_resp = respnum;
X	}
X
X	rotate = 0;			/* normal mode, not rot13 */
X	art = arts[respnum].artnum;
X	sav_unread = arts[respnum].unread;
X	arts[respnum].unread = 0;	/* mark article as read */
X	open_note(art, group_path);
X
X	if (note_page == NOTE_UNAVAIL) {
X		ClearScreen();
X		printf("[Article %ld unvailable]\r\012", art);
X		fflush(stdout);
X	} else
X		show_note_page(respnum, group);
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') || (ch == '=')) {
X
X			if (ch != '=')
X				n = prompt_response(ch, respnum);
X			else {
X				n = choose_resp (which_base (respnum), 0);
X				info_message ("Base note selected");
X			}
X
X			if (n != -1) {
X				arts[respnum].unread = sav_unread;
X				respnum = n;
X				goto restart;
X			}
X
X		} else switch (ch) {
X			case '|':	/* pipe article into command */
X				pipe_article();
X				redraw_page(respnum, group);
X				break;
X
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				goto pager_ctrlr;
X				break;
X
X			case 's':
X				save_art_to_file();
X				break;
X
X			case 'S':
X				save_thread_to_file(respnum, group_path);
X				break;
X
X			case ctrl('X'):
X			case '%':	/* toggle rot-13 mode */
X				if (rotate)
X					rotate = 0;
X				else
X					rotate = 13;
X				goto pager_ctrlr;
X				break;
X
X			case 'P':	/* previous unread article */
X				n = prev_unread(prev_response(respnum));
X				if (n == -1)
X				    info_message("No previous unread article");
X				else {
X					note_cleanup();
X					respnum = n;
X					goto restart;
X				}
X				break;
X
X			case 'F':	/* post a followup to this article */
X			case 'W':	/* ... compatible to notes */
X				if (post_response(group, TRUE)) {
X					update_newsrc(group,
X						my_group[cur_groupnum]);
X					n = which_base(respnum);
X					note_cleanup();
X					index_group(group, group_path);
X					read_newsrc_line(group);
X					respnum = choose_resp(n, nresp(n));
X					goto restart;
X				} else
X					redraw_page(respnum, group);
X				break;
X
X			case 'f':	/* post a followup to this article */
X			case 'w':	/* ... compatible to notes */
X				if (post_response(group, FALSE)) {
X					update_newsrc(group,
X						my_group[cur_groupnum]);
X					n = which_base(respnum);
X					note_cleanup();
X					index_group(group, group_path);
X					read_newsrc_line(group);
X					respnum = choose_resp(n, nresp(n));
X					goto restart;
X				} else
X					redraw_page(respnum, group);
X				break;
X
X			case 'z':	/* mark article as unread (to return) */
X				arts[respnum].unread = 2;
X				info_message("Article marked as unread");
X				break;
X
X			case 'K':	/* mark rest of thread as read */
X			case 'J':	/* ... compatible to notes */
X				for (n = respnum; n >= 0; n = arts[n].thread)
X					arts[n].unread = 0;
X				n = next_unread(next_response(respnum));
X				if (n == -1)
X					goto return_to_index;
X				else {
X					note_cleanup();
X					respnum = n;
X					goto restart;
X				}
X				break;
X
X			case 'i':	/* return to index page */
X			case ctrl('B'):	/* possibly cursor doen */
Xreturn_to_index:
X				note_cleanup();
X				return( which_base(respnum) );
X
X			case 't':	/* return to group selection page */
X				note_cleanup();
X				return -1;
X
X			case ctrl('R'):	  /* redraw beginning of article */
Xpager_ctrlr:
X				if (note_page == NOTE_UNAVAIL) {
X					ClearScreen();
X					printf("[Article %ld unvailable]\r\012",
X							arts[respnum].artnum);
X					fflush(stdout);
X				} else {
X					note_page = 0;
X					note_end = FALSE;
X					fseek(note_fp, note_mark[0], 0);
X					show_note_page(respnum, group);
X				}
X				break;
X
X			case '!':
X				shell_escape();
X				redraw_page(respnum, group);
X				break;
X
X			case '\b':
X			case 'b':	/* back a page */
X			case ctrl('P'):	/* possibly cursor doen */
X				if (note_page == NOTE_UNAVAIL
X				||  note_page <= 1) {
X					note_cleanup();
X					n = prev_response(respnum);
X					if (n == -1)
X						return( which_resp(respnum) );
X
X					respnum = n;
X					goto restart;
X
X				} else {
X					note_page -= 2;
X					note_end = FALSE;
X					fseek(note_fp, note_mark[note_page], 0);
X					show_note_page(respnum, group);
X				}
X				break;
X
X			case 'm':	/* mail article to somebody */
X				mail_to_someone();
X				redraw_page(respnum, group);
X				break;
X
X			case 'r':	/* reply to author through mail */
X				mail_to_author(FALSE);
X				redraw_page(respnum, group);
X				break;
X
X			case 'R':	/* reply to author, copy text */
X				mail_to_author(TRUE);
X				redraw_page(respnum, group);
X				break;
X
X			case '-':	/* show last viewed article */
X				if (last_resp < 0) {
X					info_message("No last message");
X					break;
X				}
X				note_cleanup();
X				respnum = last_resp;
X				goto restart;
X
X
X			case 'p':	/* previous article */
X				note_cleanup();
X				n = prev_response(respnum);
X				if (n == -1)
X					return( which_resp(respnum) );
X
X				respnum = n;
X				goto restart;
X
X			case 'n':	/* skip to next article */
X				note_cleanup();
X				n = next_response(respnum);
X				if (n == -1)
X					return( which_base(respnum) );
X
X				respnum = n;
X				goto restart;
X
X			case 'k':
X			case 'j':	/* ... compatible to notes */
X				if (note_page == NOTE_UNAVAIL) {
X					n = next_unread(next_response(respnum));
X					if (n == -1)
X						return( which_base(respnum) );
X
X					respnum = n;
X					goto restart;
X
X				} else {
X					note_cleanup();
X					n = next_unread(next_response(respnum));
X					if (n == -1)
X						return( which_base(respnum) );
X
X					respnum = n;
X					goto restart;
X				}
X				break;
X
X			case ' ': 	/* next page or response */
X			case ctrl('N'):	/* possibly cursor doen */
X				if (note_page == NOTE_UNAVAIL) {
X					n = next_response(respnum);
X					if (n == -1)
X						return( which_base(respnum) );
X
X					respnum = n;
X					goto restart;
X
X				} else if (note_end) {
X					note_cleanup();
X					n = next_response(respnum);
X					if (n == -1)
X						return( which_base(respnum) );
X
X					respnum = n;
X					goto restart;
X				} else
X					show_note_page(respnum, group);
X				break;
X
X			case '\t': 	/* next page or unread response */
X				if (note_page == NOTE_UNAVAIL) {
X					n = next_unread(next_response(respnum));
X					if (n == -1)
X						return( which_base(respnum) );
X
X					respnum = n;
X					goto restart;
X
X				} else if (note_end) {
X					note_cleanup();
X					n = next_unread(next_response(respnum));
X					if (n == -1)
X						return( which_base(respnum) );
X
X					respnum = n;
X					goto restart;
X				} else
X					show_note_page(respnum, group);
X				break;
X
X			case 'N':	/* next unread article */
X				n = next_unread(next_response(respnum));
X				if (n == -1)
X					info_message("No next unread article");
X				else {
X					note_cleanup();
X					respnum = n;
X					goto restart;
X				}
X				break;
X
X			case 'C':	/* Cancel */
X			case 'd':	/* delete */
X			case 'Z':	/* zap (director option in notes) */
X				note_cleanup();
X				cancel_article ();
X				note_page = NOTE_UNAVAIL;
X				/* Fall through ... */
X
X#ifndef	OSK
X			case '\r':
X#else	/* OSK */
X			case '\l':
X#endif	/* OSK */
X			case '\n':	/* go to start of next thread */
X			case ctrl('F'):	/* possibly cursor down */
X				note_cleanup();
X				n = next_basenote(respnum);
X				if (n == -1)
X					return( which_base(respnum) );
X
X				respnum = n;
X				goto restart;
X
X#ifndef	OSK
X			case 'q':	/* quit */
X				return -2;
X#endif	/* OSK */
X
X			case 'H':	/* show article headers */
X				if (note_page == NOTE_UNAVAIL) {
X					n = next_response(respnum);
X					if (n == -1)
X						return( which_base(respnum) );
X
X					respnum = n;
X					goto restart;
X				} else {
X					note_page = 0;
X					note_end = FALSE;
X					fseek(note_fp, 0L, 0);
X					show_note_page(respnum, group);
X				}
X				break;
X
X
X			case 'h':
X				tass_page_help();
X				redraw_page(respnum, group);
X				break;
X
X			default:
X			    info_message("Bad command.  Type 'h' for help.");
X		}
X	}
X}
X
X
Xnote_cleanup() {
X
X	if (note_page != NOTE_UNAVAIL)
X		fclose(note_fp);
X}
X
X
Xredraw_page(respnum, group)
Xint respnum;
Xchar *group;
X{
X
X	if (note_page == NOTE_UNAVAIL) {
X		ClearScreen();
X		printf("[Article %ld unvailable]\r\012", arts[respnum].artnum);
X		fflush(stdout);
X	} else if (note_page > 0) {
X		note_page--;
X		fseek(note_fp, note_mark[note_page], 0);
X		show_note_page(respnum, group);
X	}
X}
X
X
Xshow_note_page(respnum, group)
Xint respnum;
Xchar *group;
X{
X	char buf[LEN];
X	char buf2[LEN+50];
X	int percent;
X	char *p, *q;
X	int i, j;
X	int ctrl_L;		/* form feed character detected */
X
X	ClearScreen();
X
X	note_line = 1;
X
X	if (note_page == 0)
X		show_first_header(respnum, group);
X	else
X		show_cont_header(respnum);
X
X	ctrl_L = FALSE;
X	while (note_line < LINES) {
X		if (fgets(buf, LEN, note_fp) == NULL) {
X			note_end = TRUE;
X			break;
X		}
X
X		buf[LEN-1] = '\0';
X#ifdef	MNEWS
X		conv_charset (buf, strlen (buf));
X#endif	/* MNEWS */
X		if (rotate)
X			for (p = buf, q = buf2;
X					*p && *p != '\n' && q<&buf2[LEN]; p++) {
X				if (*p == '\b' && q > buf2) {
X					q--;
X				} else if (*p == 12) {		/* ^L */
X					*q++ = '^';
X					*q++ = 'L';
X					ctrl_L = TRUE;
X				} else if (*p == '\t') {
X					i = q - buf2;
X					j = (i|7) + 1;
X
X					while (i++ < j)
X						*q++ = ' ';
X				} else if (*p & 0x7F < 32) {
X					*q++ = '^';
X					*q++ = (*p & 0x7F) + '@';
X				} else if (*p >= 'A' && *p <= 'Z')
X					*q++ = 'A' + (*p - 'A' + rotate) % 26;
X				else if (*p >= 'a' && *p <= 'z')
X					*q++ = 'a' + (*p - 'a' + rotate) % 26;
X				else
X					*q++ = *p;
X			}
X		else
X			for (p = buf, q = buf2;
X					*p && *p != '\n' && q<&buf2[LEN]; p++) {
X				if (*p == '\b' && q > buf2) {
X					q--;
X				} else if (*p == 12) {		/* ^L */
X					*q++ = '^';
X					*q++ = 'L';
X					ctrl_L = TRUE;
X				} else if (*p == '\t') {
X					i = q - buf2;
X					j = (i|7) + 1;
X
X					while (i++ < j)
X						*q++ = ' ';
X				} else if ((*p & 0x7F) < 32) {
X					*q++ = '^';
X					*q++ = (*p & 0x7F) + '@';
X				} else
X					*q++ = *p;
X			}
X
X		*q = '\0';
X
X		printf("%s\r\012", buf2);
X
X#if 1
X		note_line += (strlen(buf2) / COLS) + 1;
X#else
X		if (*buf2)
X			note_line += (strlen(buf2) + COLS) / (COLS+1);
X		else
X			note_line++;
X#endif
X		if (ctrl_L)
X			break;
X	}
X
X	note_mark[++note_page] = ftell(note_fp);
X
X	MoveCursor(LINES, MORE_POS);
X/*	StartInverse();	*/
X	if (note_end) {
X		if (arts[respnum].thread != -1)
X			printf("-- next response --");
X		else
X			printf("-- last response --");
X	} else {
X		if (note_stat.st_size > 0) {
X		    percent = note_mark[note_page] * 100 / note_stat.st_size;
X		    printf("--More--(%d%%)", percent);
X		} else
X		    printf("--More--");
X	}
X/*	EndInverse();	*/
X
X	fflush(stdout);
X}
X
X
Xshow_first_header(respnum, group)
Xint respnum;
Xchar *group;
X{
X	int whichresp;
X	int x_resp;
X	char buf[200];
X	char tmp[200];
X	int pos, i;
X	int n;
X
X	whichresp = which_resp( respnum );
X	x_resp = nresp( which_base(respnum) );
X
X	ClearScreen();
X
X	strcpy(buf, note_h_date);
X	pos = (COLS - strlen(group)) / 2;
X	for (i = strlen(buf); i < pos; i++)
X		buf[i] = ' ';
X	buf[i] = '\0';
X
X	strcat(buf, group);
X
X	for (i = strlen(buf); i < RIGHT_POS; i++)
X		buf[i] = ' ';
X	buf[i] = '\0';
X
X	printf("%sNote %3d of %3d\r\012", buf, which_base(respnum) + 1, top_base);
X
X	sprintf(buf, "Article %ld  ", arts[respnum].artnum);
X	n = strlen(buf);
X	fputs(buf, stdout);
X
X	pos = (COLS - strlen( note_h_subj )) / 2 - 2;
X
X	if (pos > n)
X		MoveCursor(1, pos);
X	else
X		MoveCursor(1, n);
X
X	StartInverse();
X	strcpy(buf, note_h_subj);
X	buf[RIGHT_POS - 2 - n] = '\0';
X	fputs(buf, stdout);
X	EndInverse();
X
X	MoveCursor(1, RIGHT_POS);
X	if (whichresp)
X		printf("Resp %3d of %3d\r\012", whichresp, x_resp);
X	else {
X		if (x_resp == 0)
X			printf("No responses\r\012");
X		else if (x_resp == 1)
X			printf("1 Response\r\012");
X		else
X			printf("%d Responses\r\012", x_resp);
X	}
X
X	if (*note_h_org)
X		sprintf(tmp, "%s at %s", note_full_name, note_h_org);
X	else
X		strcpy(tmp, note_full_name);
X
X	tmp[79] = '\0';
X
X	sprintf(buf, "%s  ", note_from_addr);
X
X	pos = COLS - 1 - strlen(tmp);
X	if (strlen(buf) + strlen(tmp) >= COLS - 1) {
X		strncat(buf, tmp, COLS - 1 - strlen(buf));
X		buf[COLS - 1] = '\0';
X	} else {
X		for (i = strlen(buf); i < pos; i++)
X			buf[i] = ' ';
X		buf[i] = '\0';
X		strcat(buf, tmp);
X	}
X	printf("%s\r\012\r\012", buf);
X
X	note_line += 4;
X}
X
X
Xshow_cont_header(respnum)
Xint respnum;
X{
Xint whichresp;
Xint whichbase;
Xchar buf[LEN+60];
X
X	whichresp = which_resp(respnum);
X	whichbase = which_base(respnum);
X
X	assert (whichbase < top_base);
X
X	if (whichresp)
X		sprintf(buf, "Note %d of %d, Resp %d (page %d):  %s",
X			whichbase + 1,
X			top_base,
X			whichresp,
X			note_page + 1,
X			note_h_subj);
X	else
X		sprintf(buf, "Note %d of %d (page %d):  %s",
X			whichbase + 1,
X			top_base,
X			note_page + 1,
X			note_h_subj);
X
X	buf[COLS] = '\0';
X	printf("%s\r\012\r\012", buf);
X
X	note_line += 2;
X}
X
Xvoid
Xopen_note(art, group_path)
Xlong art;
Xchar *group_path;
X{
X	char buf[1025];
X
X	note_page = 0;
X
X#ifndef	OSK
X	sprintf(buf, "/usr/spool/news/%s/%ld", group_path, art);
X#else	/* OSK */
X	sprintf(buf, "%s/%s/%ld", spooldir, group_path, art);
X#endif	/* OSK */
X
X	if (stat(buf, &note_stat) < 0)
X		note_stat.st_size = 0;
X
X	note_fp = fopen(buf, "r");
X
X#ifdef	MNEWS
X	if (note_fp)
X		if (fgets (buf, 256, note_fp)) {
X			rewind (note_fp);
X			if (!strncmp (buf, "%(#)$ ", 6)) {
X				char	sav[200];
X
X				buf[strlen (buf) - 1] = '\0';
X				strcpy (sav, buf + 6);
X				sprintf (buf, "%s/%s", spooldir, sav);
X				fclose (note_fp);
X				if (stat (buf, &note_stat) < 0)
X					note_stat.st_size = 0;
X				note_fp = fopen (buf, "r");
X			}
X		}
X#endif	/* MNEWS */
X
X	if (note_fp == NULL) {
X		fprintf(stderr, "can't open %s: ", buf);
X		perror("");
X		note_page = NOTE_UNAVAIL;
X		return;
X	}
X
X	note_h_from[0] = '\0';
X	note_h_reply[0] = '\0';
X	note_h_path[0] = '\0';
X	note_h_subj[0] = '\0';
X	note_h_org[0] = '\0';
X	note_h_date[0] = '\0';
X	note_h_newsgroups[0] = '\0';
X	note_h_messageid[0] = '\0';
X	note_h_distrib[0] = '\0';
X	note_h_followup[0] = '\0';
X
X	while (fgets(buf, 1024, note_fp) != NULL) {
X		buf[1024] = '\0';
X		buf[strlen(buf)-1] = '\0';
X#ifdef	MNEWS
X		conv_charset (buf, strlen (buf));
X#endif	/* MNEWS */
X
X		if (*buf == '\0')
X			break;
X
X		if (strncmp(buf, "From: ", 6) == 0) {
X			strncpy(note_h_from, &buf[6], LEN);
X			note_h_from[LEN-1] = '\0';
X		} else if (strncmp(buf, "Reply-To: ", 10) == 0) {
X			strncpy(note_h_reply, &buf[10], LEN);
X			note_h_reply[LEN-1] = '\0';
X		} else if (strncmp(buf, "Path: ", 6) == 0) {
X			strncpy(note_h_path, &buf[6], LEN);
X			note_h_path[LEN-1] = '\0';
X		} else if (strncmp(buf, "Subject: ", 9) == 0) {
X			strncpy(note_h_subj, &buf[9], LEN);
X			note_h_subj[LEN-1] = '\0';
X		} else if (strncmp(buf, "Organization: ", 14) == 0) {
X			strncpy(note_h_org, &buf[14], LEN);
X			note_h_org[LEN-1] = '\0';
X		} else if (strncmp(buf, "Date: ", 6) == 0) {
X			strncpy(note_h_date, &buf[6], LEN);
X			note_h_date[LEN-1] = '\0';
X		} else if (strncmp(buf, "Newsgroups: ", 12) == 0) {
X			strncpy(note_h_newsgroups, &buf[12], LEN);
X			note_h_newsgroups[LEN-1] = '\0';
X		} else if (strncmp(buf, "Message-ID: ", 12) == 0) {
X			strncpy(note_h_messageid, &buf[12], LEN);
X			note_h_messageid[LEN-1] = '\0';
X		} else if (strncmp(buf, "Distribution: ", 14) == 0) {
X			strncpy(note_h_distrib, &buf[14], LEN);
X			note_h_distrib[LEN-1] = '\0';
X		} else if (strncmp(buf, "Followup-To: ", 13) == 0) {
X			strncpy(note_h_followup, &buf[13], LEN);
X			note_h_followup[LEN-1] = '\0';
X		}
X	}
X
X	note_page = 0;
X	note_mark[0] = ftell(note_fp);
X
X	parse_from(note_h_from, note_from_addr, note_full_name);
X	note_end = FALSE;
X
X	return;
X}
X
X
Xprompt_response(ch, respnum)
Xint respnum;
X{
X	int num;
X
X	clear_message();
X
X	if ((num = parse_num(ch, "Read response> ")) == -1) {
X		clear_message();
X		return(-1);
X	}
X
X	return choose_resp( which_base(respnum), num );
X}
X
X
X/*
X *  return response number n from thread i
X */
X
Xchoose_resp(i, n)
Xint i;
Xint n;
X{
X	int j;
X
X	j = base[i];
X
X	while (n-- && arts[j].thread >= 0)
X		j = arts[j].thread;
X
X	return j;
X}
X
X
X/*
X *  Parse various From: lines into the component mail addresses and
X *  real names
X */
X
Xparse_from(str, addr, name)
Xchar *str;
Xchar *addr;
Xchar *name;
X{
X	while (*str && *str != ' ')
X		*addr++ = *str++;
X	*addr = '\0';
X	if (*str++ == ' ') {
X		if (*str++ == '(') {
X			if (*str == '"')
X				str++;  /* Kill "quotes around names"         */
X					/* But don't touch quotes inside the  */
X					/* Name (that's what that nonsense    */
X					/* below is for			      */
X			while (*str && *str != ')' && !(*str=='"'&&str[1]==')'))
X				*name++ = *str++;
X		}
X	}
X	*name = '\0';
X}
X
X
X/*
X *  Find the previous response.  Go to the last response in the previous
X *  thread if we go past the beginning of this thread.
X */
X
Xprev_response(n)
Xint n;
X{
X	int resp;
X	int i;
X
X	resp = which_resp(n);
X
X	if (resp > 0)
X		return choose_resp( which_base(n), resp-1 );
X
X	i = which_base(n) - 1;
X
X	if (i < 0)
X		return -1;
X
X	return choose_resp( i, nresp(i) );
X}
X
X
X/*
X *  Find the next response.  Go to the next basenote if there
X *  are no more responses in this thread
X */
X
Xnext_response(n)
Xint n;
X{
X	int i;
X
X	if (arts[n].thread >= 0)
X		return arts[n].thread;
X
X	i = which_base(n) + 1;
X
X	if (i >= top_base)
X		return -1;
X
X	return base[i];
X}
X
X
X/*
X *  Given a respnum (index into arts[]), find the respnum of the
X *  next basenote
X */
X
Xnext_basenote(n)
Xint n;
X{
X	int i;
X
X	i = which_base(n) + 1;
X	if (i >= top_base)
X		return -1;
X
X	return base[i];
X}
X
Xvoid
Xtass_page_help() {
X	char ch;
X
Xpage_help_start:
X
X	ClearScreen();
X	center_line(0, TASS_HEADER);
X	center_line(1, "Article Pager Commands (page 1 of 2)");
X
X	MoveCursor(3, 0);
X
X	printf("0, =     Read the base article in this thread\r\012");
X	printf("4        Read response 4 in this thread\r\012");
X	printf("<CR>     Skip to next base article\r\012");
X	printf("<TAB>    Advance to next page or unread article\r\012");
X	printf("b        Back a page\r\012");
X	printf("f, w     Post a followup\r\012");
X	printf("F, W     Post a followup, copy text)\r\012");
X	printf("H        Show article headers\r\012");
X	printf("i        Return to index page\r\012");
X	printf("k, j      Mark article as read & advance to next unread\r\012");
X	printf("K, J      Mark rest of thread as read && advance to next unread\r\012");
X	printf("m        Mail this article to someone\r\012");
X	printf("n        Skip to the next article)\r\012");
X	printf("N        Skip to the next unread article\r\012");
X	printf("p        Go to the previous article\r\012");
X	printf("P        Go to the previous unread article\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, "Article Pager Commands (page 2 of 2)");
X
X	MoveCursor(3, 0);
X
X	printf("q        Quit\r\012");
X	printf("r        Reply through mail to author\r\012");
X	printf("R        Reply through mail to author, copy text\r\012");
X	printf("s        Save article to file\r\012");
X	printf("S        Save thread to file\r\012");
X	printf("t        Return to group selection index\r\012");
X	printf("z        Mark article as unread\r\012");
X	printf("d, C, Z  delete article (if it's yours)\r\012");
X	printf("^R       Redisplay first page of article\r\012");
X	printf("%%, ^X    Toggle rot-13 decoding for this article\r\012");
X	printf("-        Show last message\r\012");
X	printf("|        Pipe article into command\r\012");
X
X	center_line(LINES, "-- hit any key --");
X	ch = ReadCh();
X
X	if (ch == 'b')
X		goto page_help_start;
X}
X
X
X
X/*
X *  Read a file grabbing the address given for To: and
X *  sticking it in mail_to
X */
Xvoid
Xfind_new_to(nam, mail_to)
Xchar *nam;
Xchar *mail_to;
X{
X	FILE *fp;
X	char buf[LEN];
X	char buf2[LEN];
X	char dummy[LEN];
X	char *ptr;
X
X	fp = fopen(nam, "r");
X	if (fp != NULL) {
X
X		while (fgets(buf, 1024, fp) != NULL) {
X			if (*buf == '\n')
X				break;
X			if (strncmp(buf, "To: ", 4) == 0) {
X				buf[strlen(buf)-1] = '\0';
X				strncpy(buf2, &buf[4], LEN);
X				buf2[LEN-1] = '\0';
X				parse_from(buf2, mail_to, dummy);
X				break;
X			}
X		}
X
X		fclose(fp);
X	}
X	for (ptr = mail_to; *ptr; ++ptr)
X		if (*ptr == ',')
X			*ptr++ = ' ';
X		else if (*ptr == '(') {
X			int	nest = 0;
X			while (*ptr) {
X				if (*ptr == '(')
X					++nest;
X				else if (*ptr == ')')
X					--nest;
X				*ptr++ = ' ';
X				if (nest <= 0)
X					break;
X			}
X		} else
X			++ptr;
X}
X
Xvoid
Xcancel_article () {
X	char	buf[LEN+40];
X	FILE	*pp;
X	int	err;
X
X	if (!note_h_messageid[0]) {
X		printf ("\rLost message-id!");
X		CleartoEOLN ();
X		return;
X	}
X	printf("\rCancel %s...  ", note_h_messageid);
X	CleartoEOLN ();
X#ifndef	MNEWS
X	Insert your cancel-cmd here!
X#else	/* MNEWS */
X	sprintf(buf, "inews '-c=cancel:%s'", note_h_messageid);
X#endif	/* MNEWS */
X	if (pp = popen (buf, "w")) {
X		fprintf (pp, "\nArticle canceled from tass\n");
X		err = pclose (pp);
X	} else
X		err = 1;
X	if (! err)
X		printf("article canceled ");
X	else
X		printf("article not canceled ");
X	continue_prompt();
X}
X
Xmail_to_someone() {
X	char nam[100];
X	FILE *fp;
X	char ch;
X	char buf[200];
X	char mail_to[LEN+1];
X	char subj[LEN+1];
X	int line;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	if (!parse_string("Mail article to: ", mail_to))
X		return(FALSE);
X	if (mail_to[0] == '\0')
X		return(FALSE);
X
X	sprintf(nam, "%s/.letter", 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, "To: %s\n", mail_to);
X	fprintf(fp, "Subject: %s\n", note_h_subj);
X	if (*note_h_followup)
X		fprintf(fp, "Newsgroups: %s\n\n", note_h_followup);
X	else
X		fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);
X	line = 3;
X	if (*my_org) {
X		fprintf(fp, "Organization: %s\n", my_org);
X		++line;
X	}
X	fputs("\n", fp);
X	++line;
X
X	fseek(note_fp, 0L, 0);
X	copy_fp(note_fp, fp, "");
X
X	fclose(fp);
X	umask (0);
X
X	while (1) {
X		do {
X			MoveCursor(LINES, 0);
X			fputs("abort, edit, send: ", stdout);
X			fflush(stdout);
X			ch = ReadCh();
X		} while (ch != 'a' && ch != 'e' && ch != 's');
X
X		switch (ch) {
X		case 'e':
X			invoke_editor(nam, line);
X			break;
X
X		case 'a':
X			return FALSE;
X
X		case 's':
X/*
X *  Open letter an get the To: line in case they changed it with
X *  the editor
X */
X
X			find_new_to(nam, mail_to);
X			printf("\rMailing to %s...", mail_to);
X			CleartoEOLN ();
X#ifndef	OSK
X			sprintf(buf, "%s \"%s\" < %s", MAILER,
X#else	/* OSK */
X			sprintf(buf, "%s \"%s\" <%s", MAILER,
X#endif	/* OSK */
X							mail_to, nam);
X			if (invoke_cmd(buf)) {
X				printf("Message sent ");
X				fflush(stdout);
X				goto mail_to_someone_done;
X			} else {
X				printf("Command failed: %s ", buf);
X				fflush(stdout);
X				break;
X			}
X		}
X	}
X
Xmail_to_someone_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
Xmail_to_author(copy_text)
Xint copy_text;
X{
X	char nam[100];
X	FILE *fp;
X	char ch;
X	char buf[200];
X	char mail_to[LEN+1];
X	int line;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	printf("\r\012Mailing to %s...\r\012\r\012", note_h_from);
X
X	sprintf(nam, "%s/.letter", 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, "To: %s\n", note_h_from);
X	fprintf(fp, "Subject: Re: %s\n", eat_re(note_h_subj) );
X	fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);
X	line = 3;
X	if (*my_org) {
X		fprintf(fp, "Organization: %s\n", my_org);
X		++line;
X	}
X	fputs("\n", fp);
X	++line;
X
X	if (copy_text) {		/* if "copy_text" */
X		fprintf(fp, "In article %s you write:\n", note_h_messageid);
X
X		fseek(note_fp, note_mark[0], 0);
X#ifndef	INCSTR
X		copy_fp(note_fp, fp, "> ");
X#else	/* INCSTR */
X		copy_fp(note_fp, fp, incstr);
X#endif	/* INCSTR */
X	}
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 's':
X			strcpy(mail_to, note_h_reply[0] ? note_h_reply : note_from_addr);
X			find_new_to(nam, mail_to);
X			printf("\rMailing to %s...  ", mail_to);
X			CleartoEOLN ();
X#ifndef	OSK
X			sprintf(buf, "/usr/bin/rmail \"%s\" < %s",
X#else	/* OSK */
X			sprintf(buf, "%s \"%s\" <%s", MAILER,
X#endif	/* OSK */
X								mail_to, nam);
X			if (invoke_cmd(buf)) {
X				printf("Message sent ");
X				fflush(stdout);
X				goto mail_to_author_done;
X			} else {
X				printf("Command failed: %s ", buf);
X				fflush(stdout);
X				break;
X			}
X		}
X
X		do {
X			MoveCursor(LINES, 0);
X			fputs("abort, edit, send: ", stdout);
X			fflush(stdout);
X			ch = ReadCh();
X		} while (ch != 'a' && ch != 'e' && ch != 's');
X	}
X
Xmail_to_author_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
Xpost_response(group, respnum)
Xint respnum;
X{
X	FILE *fp;
X	char nam[100];
X	char ch;
X	char buf[200];
X	int post_anyway = FALSE;
X	int line;
X
X	if (*note_h_followup && strcmp(note_h_followup, "poster") == 0) {
X		clear_message();
X		MoveCursor(LINES,0);
X		printf("Note: Responses have been directed to the poster");
X		if (!prompt_yn("Post anyway? (y/n): "))
X			return FALSE;
X		*note_h_followup = '\0';
X	} else if (*note_h_followup && strcmp(note_h_followup, group) != 0) {
X	    clear_message();
X	    MoveCursor(LINES,0);
X	    printf("Note:  Responses have been directed to %s\r\012\r\012",
X							note_h_followup);
X	    if (!prompt_yn("Continue? (y/n): "))
X		return FALSE;
X	}
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: Re: %s\n", eat_re(note_h_subj));
X
X	if (*note_h_followup && strcmp(note_h_followup, "poster") != 0)
X		fprintf(fp, "Newsgroups: %s\n", note_h_followup);
X	else
X		fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);
X	line = 2;
X
X	if (*my_org) {
X		fprintf(fp, "Organization: %s\n", my_org);
X		++line;
X	}
X
X	if (note_h_distrib != '\0') {
X		fprintf(fp, "Distribution: %s\n", note_h_distrib);
X		++line;
X	}
X
X	fprintf(fp, "References: %s\n", note_h_messageid);
X	fprintf(fp, "\n");
X	line += 2;
X
X	if (respnum) {		/* if "copy_text" */
X		fprintf(fp, "%s writes:\n", note_h_from);
X		++line;
X
X		fseek(note_fp, note_mark[0], 0);
X#ifndef	INCSTR
X		copy_fp(note_fp, fp, "> ");
X#else	/* INCSTR */
X		copy_fp(note_fp, fp, incstr);
X#endif	/* INCSTR */
X	}
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_response_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_response_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
Xvoid
Xsave_art_to_file()
X{
X	char nam[LEN];
X	FILE *fp;
X	char *p;
X
X	if (!parse_string("Save article to file: ", nam))
X		return;
X	if (nam[0] == '\0')
X		return;
X
X	for (p = nam; *p && (*p == ' ' || *p == '\t'); p++) ;
X	if (!*p)
X		return;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	umask (omask);
X	if ((fp = fopen(p, "a+")) == NULL) {
X		umask (0);
X		fprintf(stderr, "can't open %s: ", nam);
X		perror("");
X		info_message("-- article not saved --");
X#ifdef	USE_UID
X		setuid(real_uid);
X		setgid(real_gid);
X#endif	/* USE_UID */
X		return;
X	}
X
X	MoveCursor(LINES, 0);
X	fputs("Saving...", stdout);
X	fflush(stdout);
X
X	fprintf(fp, "From %s %s\n", note_h_path, note_h_date);
X
X	fseek(note_fp, 0L, 0);
X	copy_fp(note_fp, fp, "");
X	fputs("\n", fp);
X
X	fclose(fp);
X	umask (0);
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	info_message("-- article saved --");
X}
X
Xvoid
Xsave_thread_to_file(respnum, group_path)
Xlong respnum;
Xchar *group_path;
X{
X	char nam[LEN];
X	FILE *fp;
X	FILE *art;
X	int i;
X	char buf[8192];
X	int b;
X	int count = 0;
X	char *p;
X
X	b = which_base(respnum);
X
X	if (!parse_string("Save thread to file: ", nam))
X		return;
X	if (nam[0] == '\0')
X		return;
X
X	for (p = nam; *p && (*p == ' ' || *p == '\t'); p++) ;
X	if (!*p)
X		return;
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	umask (omask);
X	if ((fp = fopen(nam, "a+")) == NULL) {
X		umask (0);
X		fprintf(stderr, "can't open %s: ", nam);
X		perror("");
X		info_message("-- thread not saved --");
X#ifdef	USE_UID
X		setuid(real_uid);
X		setgid(real_gid);
X#endif	/* USE_UID */
X		return;
X	}
X
X	MoveCursor(LINES, 0);
X	fputs("Saving...    ", stdout);
X	fflush(stdout);
X
X	note_cleanup();
X
X	for (i = base[b]; i >= 0; i = arts[i].thread) {
X		open_note(arts[i].artnum, group_path);
X
X		fprintf(fp, "From %s %s\n", note_h_path, note_h_date);
X		fseek(note_fp, 0L, 0);
X		copy_fp(note_fp, fp, "");
X		fputs("\n", fp);
X
X		note_cleanup();
X		printf("\b\b\b\b%4d", ++count);
X		fflush(stdout);
X	}
X
X	fclose(fp);
X	umask (0);
X
X#ifdef	USE_UID
X	setuid(real_uid);
X	setgid(real_gid);
X#endif	/* USE_UID */
X
X	info_message("-- thread saved --");
X	open_note(arts[respnum].artnum, group_path);
X}
X
Xvoid
Xpipe_article() {
X	char command[LEN];
X	FILE *fp;
X
X	if (!parse_string("Pipe to command: ", command))
X		return;
X	if (command[0] == '\0')
X		return;
X
X	fp = popen(command, "w");
X	if (fp == NULL) {
X		fprintf(stderr, "command failed: ");
X		perror("");
X		goto pipe_article_done;
X	}
X
X	fseek(note_fp, 0L, 0);
X	copy_fp(note_fp, fp, "");
X	pclose(fp);
X
Xpipe_article_done:
X
X	continue_prompt();
X}
X
__END__OF__THIS__FILE__
exit 0
: end of shell archive

-- 
Frank Kaefer # fkk@stasys.sta.sub.org # Starnberg, Germany
Technical Details
Message-ID: <1hccjaINN773@stasys.sta.sub.org>
Path: arc1-ca.pugleaf.net!sparky!uunet!elroy.jpl.nasa.gov!ames!sun-barr!news2me.EBay.Sun.COM!seven-up.East.Sun.COM!sungy!stasys!stasys!not-for-mail

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!sparky!uunet!elroy.jpl.nasa.gov!ames!sun-barr!news2me.EBay.Sun.COM!seven-up.East.Sun.COM!sungy!stasys!stasys!not-for-mail
From: frank.kaefer@stasys.sta.sub.org (Frank Kaefer)
Newsgroups: de.comp.sources.os9,comp.os.os9
Subject: Tass for OS-9 Part02/03
Message-ID: <1hccjaINN773@stasys.sta.sub.org>
Date: 24 Dec 92 13:05:46 GMT
Sender: news@stasys.sta.sub.org
Followup-To: de.comp.sources.d
Organization: Stasys News Server, Starnberg, Germany
Lines: 3489
Approved: frank.kaefer@stasys.sta.sub.org (Frank Kaefer)
NNTP-Posting-Host: stasys.sta.sub.org