Tass newsreader (part 2 of 2)

Rich Skrenta skrenta at blekko.commodore.com
Fri Feb 15 06:35:43 AEST 1991


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# This archive contains:
#	page.c		prompt.c	screen.c	select.c	
#	tass.h		time.c		
#

echo x - page.c
cat >page.c <<'@EOF'

#include	<stdio.h>
#include	<signal.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	"tass.h"


#define		MAX_PAGES	1000
#define		NOTE_UNAVAIL	-1

char note_h_path[LEN];			/* Path:	*/
char note_h_date[LEN];			/* Date:	*/
char note_h_subj[LEN];			/* Subject:	*/
char note_h_from[LEN];			/* From:	*/
char note_h_org[LEN];			/* Organization: */
char note_h_newsgroups[LEN];		/* Newsgroups:	*/
char note_h_messageid[LEN];		/* Message-ID:	*/
char note_h_distrib[LEN];		/* Distribution: */
char note_h_followup[LEN];		/* Followup-To: */

int	note_line;
int	note_page;		/* what page we're on */
long	note_mark[MAX_PAGES];	/* ftells on beginnings of pages */
FILE	*note_fp;		/* the body of the current article */
int	note_end;		/* we're done showing this article */
int	rotate;			/* 0=normal, 13=rot13 decode */

struct stat note_stat;		/* so we can tell how big it is */

char	note_full_name[100];
char	note_from_addr[100];


int last_resp;		/* current & previous article for - command */
int this_resp;

int glob_respnum;
char *glob_page_group;
extern int cur_groupnum;


#ifdef SIGTSTP
void
page_susp(i)
int i;
{

	Raw(FALSE);
	putchar('\n');
	signal(SIGTSTP, SIG_DFL);
	kill(0, SIGTSTP);

	signal(SIGTSTP, page_susp);
	mail_setup();
	Raw(TRUE);
	redraw_page(glob_respnum, glob_page_group);
}
#endif


show_page(respnum, group, group_path)
int respnum;
char *group;
char *group_path;
{
	char ch;
	int n;
	long art;

restart:

	glob_respnum = respnum;
	glob_page_group = group;

#ifdef SIGTSTP
	signal(SIGTSTP, page_susp);
#endif

	if (respnum != this_resp) {	   /* remember current & previous */
		last_resp = this_resp;	   /* articles for - command */
		this_resp = respnum;
	}

	rotate = 0;			/* normal mode, not rot13 */
	art = arts[respnum].artnum;
	arts[respnum].unread = 0;	/* mark article as read */
	open_note(art, group_path);

	if (note_page == NOTE_UNAVAIL) {
		ClearScreen();
		printf("[Article %ld unvailable]\r\r", art);
		fflush(stdout);
	} else
		show_note_page(respnum, group);

	while (1) {
		ch = ReadCh();

		if (ch >= '0' && ch <= '9') {

			n = prompt_response(ch, respnum);
			if (n != -1) {
				respnum = n;
				goto restart;
			}

		} else switch (ch) {
			case '|':	/* pipe article into command */
				pipe_article();
				redraw_page(respnum, group);
				break;

			case 'I':	/* toggle inverse video */
				inverse_okay = !inverse_okay;
				if (inverse_okay)
					info_message("Inverse video enabled");
				else
					info_message("Inverse video disabled");
				goto pager_ctrlr;
				break;

			case 's':
				save_art_to_file();
				break;

			case 'S':
				save_thread_to_file(respnum, group_path);
				break;

			case ctrl('X'):
			case '%':	/* toggle rot-13 mode */
				if (rotate)
					rotate = 0;
				else
					rotate = 13;
				goto pager_ctrlr;
				break;

			case 'P':	/* previous unread article */
				n = prev_unread(prev_response(respnum));
				if (n == -1)
				    info_message("No previous unread article");
				else {
					note_cleanup();
					respnum = n;
					goto restart;
				}
				break;

			case 'F':	/* post a followup to this article */
				if (post_response(group, TRUE)) {
					update_newsrc(group,
						my_group[cur_groupnum]);
					n = which_base(respnum);
					note_cleanup();
					index_group(group, group_path);
					read_newsrc_line(group);
					respnum = choose_resp(n, nresp(n));
					goto restart;
				} else
					redraw_page(respnum, group);
				break;

			case 'f':	/* post a followup to this article */
				if (post_response(group, FALSE)) {
					update_newsrc(group,
						my_group[cur_groupnum]);
					n = which_base(respnum);
					note_cleanup();
					index_group(group, group_path);
					read_newsrc_line(group);
					respnum = choose_resp(n, nresp(n));
					goto restart;
				} else
					redraw_page(respnum, group);
				break;

			case 'z':	/* mark article as unread (to return) */
				arts[respnum].unread = 2;
				info_message("Article marked as unread");
				break;

			case 'K':	/* mark rest of thread as read */
				for (n = respnum; n >= 0; n = arts[n].thread)
					arts[n].unread = 0;
				n = next_unread(next_response(respnum));
				if (n == -1)
					goto return_to_index;
				else {
					note_cleanup();
					respnum = n;
					goto restart;
				}
				break;

			case 'i':	/* return to index page */
return_to_index:
				note_cleanup();
				return( which_base(respnum) );

			case 't':	/* return to group selection page */
				note_cleanup();
				return -1;

			case ctrl('R'):	  /* redraw beginning of article */
pager_ctrlr:
				if (note_page == NOTE_UNAVAIL) {
					ClearScreen();
					printf("[Article %ld unvailable]\r\n",
							arts[respnum].artnum);
					fflush(stdout);
				} else {
					note_page = 0;
					note_end = FALSE;
					fseek(note_fp, note_mark[0], 0);
					show_note_page(respnum, group);
				}
				break;

			case '!':
				shell_escape();
				redraw_page(respnum, group);
				break;

			case '\b':
			case 'b':	/* back a page */
				if (note_page == NOTE_UNAVAIL
				||  note_page <= 1) {
					note_cleanup();
					n = prev_response(respnum);
					if (n == -1)
						return( which_resp(respnum) );

					respnum = n;
					goto restart;

				} else {
					note_page -= 2;
					note_end = FALSE;
					fseek(note_fp, note_mark[note_page], 0);
					show_note_page(respnum, group);
				}
				break;

			case 'm':	/* mail article to somebody */
				mail_to_someone();
				redraw_page(respnum, group);
				break;

			case 'r':	/* reply to author through mail */
				mail_to_author(FALSE);
				redraw_page(respnum, group);
				break;

			case 'R':	/* reply to author, copy text */
				mail_to_author(TRUE);
				redraw_page(respnum, group);
				break;

			case '-':	/* show last viewed article */
				if (last_resp < 0) {
					info_message("No last message");
					break;
				}
				note_cleanup();
				respnum = last_resp;
				goto restart;


			case 'p':	/* previous article */
				note_cleanup();
				n = prev_response(respnum);
				if (n == -1)
					return( which_resp(respnum) );

				respnum = n;
				goto restart;

			case 'n':	/* skip to next article */
				note_cleanup();
				n = next_response(respnum);
				if (n == -1)
					return( which_base(respnum) );

				respnum = n;
				goto restart;

			case 'k':
				if (note_page == NOTE_UNAVAIL) {
					n = next_unread(next_response(respnum));
					if (n == -1)
						return( which_base(respnum) );

					respnum = n;
					goto restart;

				} else {
					note_cleanup();
					n = next_unread(next_response(respnum));
					if (n == -1)
						return( which_base(respnum) );

					respnum = n;
					goto restart;
				}
				break;

			case ' ': 	/* next page or response */
				if (note_page == NOTE_UNAVAIL) {
					n = next_response(respnum);
					if (n == -1)
						return( which_base(respnum) );

					respnum = n;
					goto restart;

				} else if (note_end) {
					note_cleanup();
					n = next_response(respnum);
					if (n == -1)
						return( which_base(respnum) );

					respnum = n;
					goto restart;
				} else
					show_note_page(respnum, group);
				break;

			case '\t': 	/* next page or unread response */
				if (note_page == NOTE_UNAVAIL) {
					n = next_unread(next_response(respnum));
					if (n == -1)
						return( which_base(respnum) );

					respnum = n;
					goto restart;

				} else if (note_end) {
					note_cleanup();
					n = next_unread(next_response(respnum));
					if (n == -1)
						return( which_base(respnum) );

					respnum = n;
					goto restart;
				} else
					show_note_page(respnum, group);
				break;

			case 'N':	/* next unread article */
				n = next_unread(next_response(respnum));
				if (n == -1)
					info_message("No next unread article");
				else {
					note_cleanup();
					respnum = n;
					goto restart;
				}
				break;

			case '\r':
			case '\n':	/* go to start of next thread */
				note_cleanup();
				n = next_basenote(respnum);
				if (n == -1)
					return( which_base(respnum) );

				respnum = n;
				goto restart;

			case 'q':	/* quit */
				return -2;

			case 'H':	/* show article headers */
				if (note_page == NOTE_UNAVAIL) {
					n = next_response(respnum);
					if (n == -1)
						return( which_base(respnum) );

					respnum = n;
					goto restart;
				} else {
					note_page = 0;
					note_end = FALSE;
					fseek(note_fp, 0L, 0);
					show_note_page(respnum, group);
				}
				break;


			case 'h':
				tass_page_help();
				redraw_page(respnum, group);
				break;

			default:
			    info_message("Bad command.  Type 'h' for help.");
		}
	}
}


note_cleanup() {

	if (note_page != NOTE_UNAVAIL)
		fclose(note_fp);
}


redraw_page(respnum, group)
int respnum;
char *group;
{

	if (note_page == NOTE_UNAVAIL) {
		ClearScreen();
		printf("[Article %ld unvailable]\r\r", arts[respnum].artnum);
		fflush(stdout);
	} else if (note_page > 0) {
		note_page--;
		fseek(note_fp, note_mark[note_page], 0);
		show_note_page(respnum, group);
	}
}


show_note_page(respnum, group)
int respnum;
char *group;
{
	char buf[LEN];
	char buf2[LEN+50];
	int percent;
	char *p, *q;
	int i, j;
	int ctrl_L;		/* form feed character detected */

	ClearScreen();

	note_line = 1;

	if (note_page == 0)
		show_first_header(respnum, group);
	else
		show_cont_header(respnum);

	ctrl_L = FALSE;
	while (note_line < LINES) {
		if (fgets(buf, LEN, note_fp) == NULL) {
			note_end = TRUE;
			break;
		}

		buf[LEN-1] = '\0';
		if (rotate)
			for (p = buf, q = buf2;
					*p && *p != '\n' && q<&buf2[LEN]; p++) {
				if (*p == '\b' && q > buf2) {
					q--;
				} else if (*p == 12) {		/* ^L */
					*q++ = '^';
					*q++ = 'L';
					ctrl_L = TRUE;
				} else if (*p == '\t') {
					i = q - buf2;
					j = (i|7) + 1;

					while (i++ < j)
						*q++ = ' ';
				} else if (*p & 0x7F < 32) {
					*q++ = '^';
					*q++ = (*p & 0x7F) + '@';
				} else if (*p >= 'A' && *p <= 'Z')
					*q++ = 'A' + (*p - 'A' + rotate) % 26;
				else if (*p >= 'a' && *p <= 'z')
					*q++ = 'a' + (*p - 'a' + rotate) % 26;
				else
					*q++ = *p;
			}
		else
			for (p = buf, q = buf2;
					*p && *p != '\n' && q<&buf2[LEN]; p++) {
				if (*p == '\b' && q > buf2) {
					q--;
				} else if (*p == 12) {		/* ^L */
					*q++ = '^';
					*q++ = 'L';
					ctrl_L = TRUE;
				} else if (*p == '\t') {
					i = q - buf2;
					j = (i|7) + 1;

					while (i++ < j)
						*q++ = ' ';
				} else if ((*p & 0x7F) < 32) {
					*q++ = '^';
					*q++ = (*p & 0x7F) + '@';
				} else
					*q++ = *p;
			}

		*q = '\0';

		printf("%s\r\n", buf2);

#if 1
		note_line += (strlen(buf2) / COLS) + 1;
#else
		if (*buf2)
			note_line += (strlen(buf2) + COLS) / (COLS+1);
		else
			note_line++;
#endif
		if (ctrl_L)
			break;
	}

	note_mark[++note_page] = ftell(note_fp);

	MoveCursor(LINES, MORE_POS);
/*	StartInverse();	*/
	if (note_end) {
		if (arts[respnum].thread != -1)
			printf("-- next response --");
		else
			printf("-- last response --");
	} else {
		if (note_stat.st_size > 0) {
		    percent = note_mark[note_page] * 100 / note_stat.st_size;
		    printf("--More--(%d%%)", percent);
		} else
		    printf("--More--");
	}
/*	EndInverse();	*/

	fflush(stdout);
}


show_first_header(respnum, group)
int respnum;
char *group;
{
	int whichresp;
	int x_resp;
	char buf[200];
	char tmp[200];
	int pos, i;
	int n;

	whichresp = which_resp( respnum );
	x_resp = nresp( which_base(respnum) );

	ClearScreen();

	strcpy(buf, note_h_date);
	pos = (COLS - strlen(group)) / 2;
	for (i = strlen(buf); i < pos; i++)
		buf[i] = ' ';
	buf[i] = '\0';

	strcat(buf, group);

	for (i = strlen(buf); i < RIGHT_POS; i++)
		buf[i] = ' ';
	buf[i] = '\0';

	printf("%sNote %3d of %3d\r\n", buf, which_base(respnum) + 1, top_base);

	sprintf(buf, "Article %ld  ", arts[respnum].artnum);
	n = strlen(buf);
	fputs(buf, stdout);

	pos = (COLS - strlen( note_h_subj )) / 2 - 2;

	if (pos > n)
		MoveCursor(1, pos);
	else
		MoveCursor(1, n);

	StartInverse();
	strcpy(buf, note_h_subj);
	buf[RIGHT_POS - 2 - n] = '\0';
	fputs(buf, stdout);
	EndInverse();

	MoveCursor(1, RIGHT_POS);
	if (whichresp)
		printf("Resp %3d of %3d\r\n", whichresp, x_resp);
	else {
		if (x_resp == 0)
			printf("No responses\r\n");
		else if (x_resp == 1)
			printf("1 Response\r\n");
		else
			printf("%d Responses\r\n", x_resp);
	}

	if (*note_h_org)
		sprintf(tmp, "%s at %s", note_full_name, note_h_org);
	else
		strcpy(tmp, note_full_name);

	tmp[79] = '\0';

	sprintf(buf, "%s  ", note_from_addr);

	pos = COLS - 1 - strlen(tmp);
	if (strlen(buf) + strlen(tmp) >= COLS - 1) {
		strncat(buf, tmp, COLS - 1 - strlen(buf));
		buf[COLS - 1] = '\0';
	} else {
		for (i = strlen(buf); i < pos; i++)
			buf[i] = ' ';
		buf[i] = '\0';
		strcat(buf, tmp);
	}
	printf("%s\r\n\r\n", buf);

	note_line += 4;
}


show_cont_header(respnum)
int respnum;
{
int whichresp;
int whichbase;
char buf[200];

	whichresp = which_resp(respnum);
	whichbase = which_base(respnum);

	assert (whichbase < top_base);

	if (whichresp)
		sprintf(buf, "Note %d of %d, Resp %d (page %d):  %s",
			whichbase + 1,
			top_base,
			whichresp,
			note_page + 1,
			note_h_subj);
	else
		sprintf(buf, "Note %d of %d (page %d):  %s",
			whichbase + 1,
			top_base,
			note_page + 1,
			note_h_subj);

	buf[COLS] = '\0';
	printf("%s\r\n\r\n", buf);

	note_line += 2;
}


open_note(art, group_path)
long art;
char *group_path;
{
	char buf[1025];

	note_page = 0;

	sprintf(buf, "/usr/spool/news/%s/%ld", group_path, art);

	if (stat(buf, &note_stat) < 0)
		note_stat.st_size = 0;

	note_fp = fopen(buf, "r");
	if (note_fp == NULL) {
		fprintf(stderr, "can't open %s: ", buf);
		perror("");
		note_page = NOTE_UNAVAIL;
		return;
	}

	note_h_from[0] = '\0';
	note_h_path[0] = '\0';
	note_h_subj[0] = '\0';
	note_h_org[0] = '\0';
	note_h_date[0] = '\0';
	note_h_newsgroups[0] = '\0';
	note_h_messageid[0] = '\0';
	note_h_distrib[0] = '\0';
	note_h_followup[0] = '\0';

	while (fgets(buf, 1024, note_fp) != NULL) {
		buf[1024] = '\0';
		buf[strlen(buf)-1] = '\0';

		if (*buf == '\0')
			break;

		if (strncmp(buf, "From: ", 6) == 0) {
			strncpy(note_h_from, &buf[6], LEN);
			note_h_from[LEN-1] = '\0';
		} else if (strncmp(buf, "Path: ", 6) == 0) {
			strncpy(note_h_path, &buf[6], LEN);
			note_h_path[LEN-1] = '\0';
		} else if (strncmp(buf, "Subject: ", 9) == 0) {
			strncpy(note_h_subj, &buf[9], LEN);
			note_h_subj[LEN-1] = '\0';
		} else if (strncmp(buf, "Organization: ", 14) == 0) {
			strncpy(note_h_org, &buf[14], LEN);
			note_h_org[LEN-1] = '\0';
		} else if (strncmp(buf, "Date: ", 6) == 0) {
			strncpy(note_h_date, &buf[6], LEN);
			note_h_date[LEN-1] = '\0';
		} else if (strncmp(buf, "Newsgroups: ", 12) == 0) {
			strncpy(note_h_newsgroups, &buf[12], LEN);
			note_h_newsgroups[LEN-1] = '\0';
		} else if (strncmp(buf, "Message-ID: ", 12) == 0) {
			strncpy(note_h_messageid, &buf[12], LEN);
			note_h_messageid[LEN-1] = '\0';
		} else if (strncmp(buf, "Distribution: ", 14) == 0) {
			strncpy(note_h_distrib, &buf[14], LEN);
			note_h_distrib[LEN-1] = '\0';
		} else if (strncmp(buf, "Followup-To: ", 13) == 0) {
			strncpy(note_h_followup, &buf[13], LEN);
			note_h_followup[LEN-1] = '\0';
		}
	}

	note_page = 0;
	note_mark[0] = ftell(note_fp);

	parse_from(note_h_from, note_from_addr, note_full_name);
	note_end = FALSE;

	return;
}


prompt_response(ch, respnum)
int respnum;
{
	int num;

	clear_message();

	if ((num = parse_num(ch, "Read response> ")) == -1) {
		clear_message();
		return(-1);
	}

	return choose_resp( which_base(respnum), num );
}


/*
 *  return response number n from thread i
 */

choose_resp(i, n)
int i;
int n;
{
	int j;

	j = base[i];

	while (n-- && arts[j].thread >= 0)
		j = arts[j].thread;

	return j;
}


/*
 *  Parse various From: lines into the component mail addresses and
 *  real names
 */

parse_from(str, addr, name)
char *str;
char *addr;
char *name;
{
	while (*str && *str != ' ')
		*addr++ = *str++;
	*addr = '\0';
	if (*str++ == ' ') {
		if (*str++ == '(') {
			if (*str == '"')
				str++;  /* Kill "quotes around names"         */
					/* But don't touch quotes inside the  */
					/* Name (that's what that nonsense    */
					/* below is for			      */
			while (*str && *str != ')' && !(*str=='"'&&str[1]==')'))
				*name++ = *str++;
		}
	}
	*name = '\0';
}


/*
 *  Find the previous response.  Go to the last response in the previous
 *  thread if we go past the beginning of this thread.
 */

prev_response(n)
int n;
{
	int resp;
	int i;

	resp = which_resp(n);

	if (resp > 0)
		return choose_resp( which_base(n), resp-1 );

	i = which_base(n) - 1;

	if (i < 0)
		return -1;

	return choose_resp( i, nresp(i) );
}


/*
 *  Find the next response.  Go to the next basenote if there
 *  are no more responses in this thread
 */

next_response(n)
int n;
{
	int i;

	if (arts[n].thread >= 0)
		return arts[n].thread;

	i = which_base(n) + 1;

	if (i >= top_base)
		return -1;

	return base[i];
}


/*
 *  Given a respnum (index into arts[]), find the respnum of the
 *  next basenote
 */

next_basenote(n)
int n;
{
	int i;

	i = which_base(n) + 1;
	if (i >= top_base)
		return -1;

	return base[i];
}


tass_page_help() {
	char ch;

page_help_start:

	ClearScreen();
	center_line(0, TASS_HEADER);
	center_line(1, "Article Pager Commands (page 1 of 2)");

	MoveCursor(3, 0);

	printf("0        Read the base article in this thread\r\n");
	printf("4        Read response 4 in this thread\r\n");
	printf("<CR>     Skip to next base article\r\n");
	printf("<TAB>    Advance to next page or unread article\r\n");
	printf("b        Back a page\r\n");
	printf("f        Post a followup\r\n");
	printf("F        Post a followup, copy text)\r\n");
	printf("H        Show article headers\r\n");
	printf("i        Return to index page\r\n");
	printf("k        Mark article as read & advance to next unread\r\n");
printf("K        Mark rest of thread as read && advance to next unread\r\n");
	printf("m        Mail this article to someone\r\n");
	printf("n        Skip to the next article)\r\n");
	printf("N        Skip to the next unread article\r\n");
	printf("p        Go to the previous article\r\n");
	printf("P        Go to the previous unread article\r\n");

	center_line(LINES, "-- hit space for more commands --");
	ch = ReadCh();
	if (ch != ' ')
		return;

	ClearScreen();
	center_line(0, TASS_HEADER);
	center_line(1, "Article Pager Commands (page 2 of 2)");

	MoveCursor(3, 0);

	printf("q        Quit\r\n");
	printf("r        Reply through mail to author\r\n");
	printf("R        Reply through mail to author, copy text\r\n");
	printf("s        Save article to file\r\n");
	printf("S        Save thread to file\r\n");
	printf("t        Return to group selection index\r\n");
	printf("z        Mark article as unread\r\n");
	printf("^R       Redisplay first page of article\r\n");
	printf("%%, ^X    Toggle rot-13 decoding for this article\r\n");
	printf("-        Show last message\r\n");
	printf("|        Pipe article into command\r\n");

	center_line(LINES, "-- hit any key --");
	ch = ReadCh();

	if (ch == 'b')
		goto page_help_start;
}



/*
 *  Read a file grabbing the address given for To: and
 *  sticking it in mail_to
 */

find_new_to(nam, mail_to)
char *nam;
char *mail_to;
{
	FILE *fp;
	char buf[LEN];
	char buf2[LEN];
	char dummy[LEN];

	fp = fopen(nam, "r");
	if (fp == NULL)
		return;

	while (fgets(buf, 1024, fp) != NULL) {
		if (*buf == '\n')
			break;
		if (strncmp(buf, "To: ", 4) == 0) {
			buf[strlen(buf)-1] = '\0';
			strncpy(buf2, &buf[4], LEN);
			buf2[LEN-1] = '\0';
			parse_from(buf2, mail_to, dummy);
			break;
		}
	}

	fclose(fp);
}


mail_to_someone() {
	char nam[100];
	FILE *fp;
	char ch;
	char buf[200];
	char mail_to[LEN+1];
	char subj[LEN+1];

	setuid(real_uid);
	setgid(real_gid);

	if (!parse_string("Mail article to: ", mail_to))
		return;
	if (mail_to[0] == '\0')
		return;

	sprintf(nam, "%s/.letter", homedir);
	if ((fp = fopen(nam, "w")) == NULL) {
		fprintf(stderr, "can't open %s: ", nam);
		perror("");
		return(FALSE);
	}
	chmod(nam, 0600);

	fprintf(fp, "To: %s\n", mail_to);
	fprintf(fp, "Subject: %s\n", note_h_subj);
	if (*note_h_followup)
		fprintf(fp, "Newsgroups: %s\n\n", note_h_followup);
	else
		fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);
	if (*my_org)
		fprintf(fp, "Organization: %s\n", my_org);
	fputs("\n", fp);

	fseek(note_fp, 0L, 0);
	copy_fp(note_fp, fp, "");

	fclose(fp);

	while (1) {
		do {
			MoveCursor(LINES, 0);
			fputs("abort, edit, send: ", stdout);
			fflush(stdout);
			ch = ReadCh();
		} while (ch != 'a' && ch != 'e' && ch != 's');

		switch (ch) {
		case 'e':
			invoke_editor(nam);
			break;

		case 'a':
			return FALSE;

		case 's':
/*
 *  Open letter an get the To: line in case they changed it with
 *  the editor
 */

			find_new_to(nam, mail_to);
			printf("\nMailing to %s...", mail_to);
			fflush(stdout);
			sprintf(buf, "%s \"%s\" < %s", MAILER,
							mail_to, nam);
			if (invoke_cmd(buf)) {
				printf("Message sent\n");
				fflush(stdout);
				goto mail_to_someone_done;
			} else {
				printf("Command failed: %s\n", buf);
				fflush(stdout);
				break;
			}
		}
	}

mail_to_someone_done:
	setuid(tass_uid);
	setgid(tass_gid);

	continue_prompt();

	return TRUE;
}


mail_to_author(copy_text)
int copy_text;
{
	char nam[100];
	FILE *fp;
	char ch;
	char buf[200];
	char mail_to[LEN+1];

	setuid(real_uid);
	setgid(real_gid);

	printf("\r\nMailing to %s...\r\n\r\n", note_h_from);

	sprintf(nam, "%s/.letter", homedir);
	if ((fp = fopen(nam, "w")) == NULL) {
		fprintf(stderr, "can't open %s: ", nam);
		perror("");
		return(FALSE);
	}
	chmod(nam, 0600);

	fprintf(fp, "To: %s\n", note_h_from);
	fprintf(fp, "Subject: Re: %s\n", eat_re(note_h_subj) );
	fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);
	if (*my_org)
		fprintf(fp, "Organization: %s\n", my_org);
	fputs("\n", fp);

	if (copy_text) {		/* if "copy_text" */
		fprintf(fp, "In article %s you write:\n", note_h_messageid);

		fseek(note_fp, note_mark[0], 0);
		copy_fp(note_fp, fp, "> ");
	}

	fclose(fp);

	ch = 'e';
	while (1) {
		switch (ch) {
		case 'e':
			invoke_editor(nam);
			break;

		case 'a':
			return FALSE;

		case 's':
			strcpy(mail_to, note_from_addr);
			find_new_to(nam, mail_to);
			printf("\nMailing to %s...  ", mail_to);
			fflush(stdout);
			sprintf(buf, "/usr/bin/rmail \"%s\" < %s",
								mail_to, nam);
			if (invoke_cmd(buf)) {
				printf("Message sent\n");
				fflush(stdout);
				goto mail_to_author_done;
			} else {
				printf("Command failed: %s\n", buf);
				fflush(stdout);
				break;
			}
		}

		do {
			MoveCursor(LINES, 0);
			fputs("abort, edit, send: ", stdout);
			fflush(stdout);
			ch = ReadCh();
		} while (ch != 'a' && ch != 'e' && ch != 's');
	}

mail_to_author_done:
	setuid(tass_uid);
	setgid(tass_gid);

	continue_prompt();

	return TRUE;
}


post_response(group, respnum)
int respnum;
{
	FILE *fp;
	char nam[100];
	char ch;
	char buf[200];
	int post_anyway = FALSE;

	if (*note_h_followup && strcmp(note_h_followup, "poster") == 0) {
		clear_message();
		MoveCursor(LINES,0);
		printf("Note: Responses have been directed to the poster");
		if (!prompt_yn("Post anyway? (y/n): "))
			return FALSE;
		*note_h_followup = '\0';
	} else if (*note_h_followup && strcmp(note_h_followup, group) != 0) {
	    clear_message();
	    MoveCursor(LINES,0);
	    printf("Note:  Responses have been directed to %s\r\n\r\n",
							note_h_followup);
	    if (!prompt_yn("Continue? (y/n): "))
		return FALSE;
	}

	setuid(real_uid);
	setgid(real_gid);

	sprintf(nam, "%s/.article", homedir);
	if ((fp = fopen(nam, "w")) == NULL) {
		fprintf(stderr, "can't open %s: ", nam);
		perror("");
		return FALSE;
	}
	chmod(nam, 0600);

	fprintf(fp, "Subject: Re: %s\n", eat_re(note_h_subj));

	if (*note_h_followup && strcmp(note_h_followup, "poster") != 0)
		fprintf(fp, "Newsgroups: %s\n", note_h_followup);
	else
		fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);

	if (*my_org)
		fprintf(fp, "Organization: %s\n", my_org);

	if (note_h_distrib != '\0')
		fprintf(fp, "Distribution: %s\n", note_h_distrib);

	fprintf(fp, "References: %s\n", note_h_messageid);
	fprintf(fp, "\n");

	if (respnum) {		/* if "copy_text" */
		fprintf(fp, "%s writes:\n", note_h_from);

		fseek(note_fp, note_mark[0], 0);
		copy_fp(note_fp, fp, "> ");
	}

	fclose(fp);

	ch = 'e';
	while (1) {
		switch (ch) {
		case 'e':
			invoke_editor(nam);
			break;

		case 'a':
			return FALSE;

		case 'p':
			printf("Posting...  ");
			fflush(stdout);
			sprintf(buf, "%s/inews -h < %s", LIBDIR, nam);
			if (invoke_cmd(buf)) {
				printf("article posted\n");
				fflush(stdout);
				goto post_response_done;
			} else {
				printf("article rejected\n");
				fflush(stdout);
				break;
			}
		}

		do {
			MoveCursor(LINES, 0);
			fputs("abort, edit, post: ", stdout);
			fflush(stdout);
			ch = ReadCh();
		} while (ch != 'a' && ch != 'e' && ch != 'p');
	}

post_response_done:
	setuid(tass_uid);
	setgid(tass_gid);

	continue_prompt();

	return TRUE;
}


save_art_to_file()
{
	char nam[LEN];
	FILE *fp;
	char *p;

	if (!parse_string("Save article to file: ", nam))
		return;
	if (nam[0] == '\0')
		return;

	for (p = nam; *p && (*p == ' ' || *p == '\t'); p++) ;
	if (!*p)
		return;

	setuid(real_uid);
	setgid(real_gid);

	if ((fp = fopen(p, "a+")) == NULL) {
		fprintf(stderr, "can't open %s: ", nam);
		perror("");
		info_message("-- article not saved --");
		setuid(real_uid);
		setgid(real_gid);
		return;
	}

	MoveCursor(LINES, 0);
	fputs("Saving...", stdout);
	fflush(stdout);

	fprintf(fp, "From %s %s\n", note_h_path, note_h_date);

	fseek(note_fp, 0L, 0);
	copy_fp(note_fp, fp, "");
	fputs("\n", fp);

	fclose(fp);

	setuid(real_uid);
	setgid(real_gid);
	info_message("-- article saved --");
}


save_thread_to_file(respnum, group_path)
long respnum;
char *group_path;
{
	char nam[LEN];
	FILE *fp;
	FILE *art;
	int i;
	char buf[8192];
	int b;
	int count = 0;
	char *p;

	b = which_base(respnum);

	if (!parse_string("Save thread to file: ", nam))
		return;
	if (nam[0] == '\0')
		return;

	for (p = nam; *p && (*p == ' ' || *p == '\t'); p++) ;
	if (!*p)
		return;

	setuid(real_uid);
	setgid(real_gid);

	if ((fp = fopen(nam, "a+")) == NULL) {
		fprintf(stderr, "can't open %s: ", nam);
		perror("");
		info_message("-- thread not saved --");
		setuid(real_uid);
		setgid(real_gid);
		return;
	}

	MoveCursor(LINES, 0);
	fputs("Saving...    ", stdout);
	fflush(stdout);

	note_cleanup();

	for (i = base[b]; i >= 0; i = arts[i].thread) {
		open_note(arts[i].artnum, group_path);

		fprintf(fp, "From %s %s\n", note_h_path, note_h_date);
		fseek(note_fp, 0L, 0);
		copy_fp(note_fp, fp, "");
		fputs("\n", fp);

		note_cleanup();
		printf("\b\b\b\b%4d", ++count);
		fflush(stdout);
	}

	fclose(fp);
	setuid(real_uid);
	setgid(real_gid);

	info_message("-- thread saved --");
	open_note(arts[respnum].artnum, group_path);
}


pipe_article() {
	char command[LEN];
	FILE *fp;

	if (!parse_string("Pipe to command: ", command))
		return;
	if (command[0] == '\0')
		return;

	fp = popen(command, "w");
	if (fp == NULL) {
		fprintf(stderr, "command failed: ");
		perror("");
		goto pipe_article_done;
	}

	fseek(note_fp, 0L, 0);
	copy_fp(note_fp, fp, "");
	pclose(fp);

pipe_article_done:

	continue_prompt();
}

@EOF

chmod 644 page.c

echo x - prompt.c
cat >prompt.c <<'@EOF'

#include	<stdio.h>
#include	"tass.h"


/*
 *  parse_num
 *  get a number from the user
 *  Return -1 if missing or bad number typed
 */

parse_num(ch, prompt)
char ch;
char *prompt;
{
	char buf[40];
	int len;
	int i;
	int num;

	MoveCursor(LINES,0);
	printf("%s %c",prompt,ch);
	fflush(stdout);
	buf[0] = ch;
	buf[1] = '\0';
	len = 1;
	ch = ReadCh();
	while (ch != '\n'&& ch != '\r') {
		if (ch >= '0' && ch <= '9' && len < 4) {
			buf[len++] = ch;
			buf[len] = '\0';
			putchar(ch);
		} else if (ch == 8 || ch == 127) {
			if (len) {
				len--;
				buf[len] = '\0';
				putchar('\b');
				putchar(' ');
				putchar('\b');
			} else {
				MoveCursor(LINES, 0);
				CleartoEOLN();
				return(-1);
			}
		} else if (ch == 21) {	/* control-U	*/
			for (i = len;i>0;i--) {
				putchar('\b');
				putchar(' ');
				putchar('\b');
			}
			buf[0] = '\0';
			len = 0;
		} else
			putchar(7);
		fflush(stdout);
		ch = ReadCh();
	}

	MoveCursor(LINES, 0);
	CleartoEOLN();

	if (len) {
		num = atoi(buf);
		return(num);
	} else
		return(-1);
}


/*
 *  parse_string
 *  get a string from the user
 *  Return TRUE if a valid string was typed, FALSE otherwise
 */

parse_string(prompt, buf)
char *prompt;
char *buf;
{
int len;
int i;
char ch;

	clear_message();
	MoveCursor(LINES,0);
	printf("%s", prompt);
	fflush(stdout);
	buf[0] = '\0';
	len = 0;
	ch = ReadCh();
	while (ch != '\n' && ch != '\r') {
		if (ch >= ' ' && len < 60) {
			buf[len++] = ch;
			buf[len] = '\0';
			putchar(ch);
		} else if (ch == 8 || ch == 127) {
			if (len) {
				len--;
				buf[len] = '\0';
				putchar('\b');
				putchar(' ');
				putchar('\b');
			} else {
				MoveCursor(LINES, 0);
				CleartoEOLN();
				return(FALSE);
			}
		} else if (ch == 21) {	/* control-U	*/
			for (i = len;i>0;i--) {
				putchar('\b');
				putchar(' ');
				putchar('\b');
			}
			buf[0] = '\0';
			len = 0;
		} else
			putchar(7);
		fflush(stdout);
		ch = ReadCh();
	}
	MoveCursor(LINES,0);
	CleartoEOLN();

	return TRUE;
}


prompt_yn(prompt)
char *prompt;
{
	char ch;

	clear_message();
	MoveCursor(LINES,0);
	printf("%s", prompt);
	fflush(stdout);

	ch = ReadCh();
	clear_message();

	if (ch == 'y' || ch == 'Y')
		return TRUE;

	return FALSE;
}


continue_prompt() {

	printf("-Hit return to continue-");
	fflush(stdout);
	while (ReadCh() != '\n') ;
}


@EOF

chmod 644 prompt.c

echo x - screen.c
cat >screen.c <<'@EOF'

#include	<stdio.h>
#include	"tass.h"



info_message(msg)
char *msg;
{
	clear_message();	  /* Clear any old messages hanging around */
	center_line(LINES, msg);  /* center the message at screen bottom  */
	MoveCursor(LINES, 0);
}


clear_message()
{
	MoveCursor(LINES, 0);
	CleartoEOLN();
}


center_line(line, str)
int line;
char *str;
{
int pos;

	pos = (COLS - strlen(str)) / 2;
	MoveCursor(line, pos);
	printf("%s", str);
	fflush(stdout);
}


draw_arrow(line)
int line;
{
	MoveCursor(line, 0);
	printf("->");
	fflush(stdout);
	MoveCursor(LINES, 0);
}

erase_arrow(line)
int line;
{
	MoveCursor(line, 0);
	printf("  ");
	fflush(stdout);
}

@EOF

chmod 644 screen.c

echo x - select.c
cat >select.c <<'@EOF'

#include	<stdio.h>
#include	<signal.h>
#include	"tass.h"


int first_group_on_screen;
int last_group_on_screen;
int cur_groupnum = 0;
extern int index_point;
int space_mode;
extern char *cvers;

char group_search_string[LEN+1];



#ifdef SIGTSTP
void
select_susp(i)
int i;
{

	Raw(FALSE);
	putchar('\n');
	signal(SIGTSTP, SIG_DFL);
	kill(0, SIGTSTP);

	signal(SIGTSTP, select_susp);
	Raw(TRUE);
	mail_setup();
	group_selection_page();
}
#endif


selection_index()
{
	char ch;
	int n;
	int i;
	char buf[200];

	group_selection_page();		/* display group selection page */

	while (1) {
		ch = ReadCh();

		if (ch > '0' && ch <= '9') {
			prompt_group_num(ch);
		} else switch (ch) {
			case 'c':	/* catchup--mark all articles as read */
			    if (prompt_yn("Mark group as read? (y/n): ")) {
				unread[cur_groupnum] = 0;
				mark_group_read(
					    active[my_group[cur_groupnum]].name,
					    my_group[cur_groupnum]);
				group_selection_page();
			    }
			    break;

			case ctrl('K'):
				if (local_top <= 0) {
					info_message("No groups to delete");
					break;
				}

				delete_group(
					active[my_group[cur_groupnum]].name);
				active[my_group[cur_groupnum]].flag = NOTGOT;

				local_top--;
				for (i = cur_groupnum; i < local_top; i++) {
					my_group[i] = my_group[i+1];
					unread[i] = unread[i+1];
				}
				if (cur_groupnum >= local_top)
					cur_groupnum = local_top - 1;

				group_selection_page();
				break;

			case ctrl('Y'):
				undel_group();
				group_selection_page();
				break;

			case 'I':		/* toggle inverse video */
				inverse_okay = !inverse_okay;
				if (inverse_okay)
					info_message("Inverse video enabled");
				else
					info_message("Inverse video disabled");
				break;

			case ctrl('R'):	/* reset .newsrc */
			    if (prompt_yn("Reset newsrc? (y/n): ")) {
				reset_newsrc();
				cur_groupnum = 0;
				group_selection_page();
			    }
			    break;

			case '$':	/* reread .newsrc, no unsub groups */
				cur_groupnum = 0;
				local_top = 0;
				for (i = 0; i < num_active; i++)
					active[i].flag = NOTGOT;
				read_newsrc(TRUE);
				group_selection_page();
				break;

			case 's':	/* subscribe to current group */
			    MoveCursor(INDEX_TOP +
				(cur_groupnum-first_group_on_screen), 3);
			    putchar(' ');
			    fflush(stdout);
			    MoveCursor(LINES, 0);

			    subscribe(active[my_group[cur_groupnum]].name,
					':', my_group[cur_groupnum], FALSE);
			    sprintf(buf, "subscribed to %s",
					active[my_group[cur_groupnum]].name);
			    info_message(buf);
			    break;

			case 'u':	/* unsubscribe to current group */
			    MoveCursor(INDEX_TOP +
				(cur_groupnum-first_group_on_screen), 3);
			    putchar('u');
			    fflush(stdout);
			    MoveCursor(LINES, 0);

			    subscribe(active[my_group[cur_groupnum]].name,
					'!', my_group[cur_groupnum], FALSE);
			    sprintf(buf, "unsubscribed to %s",
					active[my_group[cur_groupnum]].name);
			    info_message(buf);
			    break;

			case ' ':
				clear_message();
				break;

			case '\t':
				for (i = cur_groupnum; i < local_top; i++)
					if (unread[i] != 0)
						break;
				if (i >= local_top) {
					info_message("No more groups to read");
					break;
				}

				erase_group_arrow();
				cur_groupnum = i;
				if (cur_groupnum >= last_group_on_screen)
					group_selection_page();
				else
					draw_group_arrow();
				space_mode = TRUE;
				goto go_into_group;

			case 'g':	/* prompt for a new group name */
				n = choose_new_group();
				if (n >= 0) {
					erase_group_arrow();
					cur_groupnum = n;
					if (cur_groupnum < first_group_on_screen
					|| cur_groupnum >= last_group_on_screen)
						group_selection_page();
					else
						draw_group_arrow();
				}
				break;

			case 27:	/* (ESC) common arrow keys */
				ch = ReadCh();
				if (ch == '[' || ch == 'O')
					ch = ReadCh();
				switch (ch) {
				case 'A':
				case 'D':
				case 'i':
					goto select_up;

				case 'B':
				case 'I':
				case 'C':
					goto select_down;
				}
				break;

			case 'y':	/* pull in rest of groups from active */
				n = local_top;
				for (i = 0; i < num_active; i++)
					active[i].flag = NOTGOT;
				read_newsrc(FALSE);
				for (i = 0; i < num_active; i++)
					if (active[i].flag & NOTGOT) {
						active[i].flag &= ~NOTGOT;
						my_group[local_top] = i;
						unread[local_top] = -1;
						local_top++;
					}
				if (n < local_top) {
					sprintf(buf, "Added %d group%s",
						local_top - n,
						local_top - n == 1 ? "" : "s");
					group_selection_page();
					info_message(buf);
				} else
				    info_message("No more groups to yank in");
				break;

			case ctrl('U'):		/* page up */
				erase_group_arrow();
				cur_groupnum -= NOTESLINES / 2;
				if (cur_groupnum < 0)
					cur_groupnum = 0;
				if (cur_groupnum < first_group_on_screen
				||  cur_groupnum >= last_group_on_screen)
					group_selection_page();
				else
					draw_group_arrow();
				break;

			case ctrl('D'):		/* page down */
				erase_group_arrow();
				cur_groupnum += NOTESLINES / 2;
				if (cur_groupnum >= local_top)
					cur_groupnum = local_top - 1;

				if (cur_groupnum <= first_group_on_screen
				||  cur_groupnum >= last_group_on_screen)
					group_selection_page();
				else
					draw_group_arrow();
				break;

			case '!':
				shell_escape();
				group_selection_page();
				break;

			case 'v':
				info_message(cvers);
				break;

			case ctrl('N'):		/* line down */
			case 'j':
select_down:
				if (cur_groupnum + 1 >= local_top)
					break;

				if (cur_groupnum + 1 >= last_group_on_screen) {
					cur_groupnum++;
					group_selection_page();
				} else {
					erase_group_arrow();
					cur_groupnum++;
					draw_group_arrow();
				}
				break;

			case ctrl('P'):		/* line up */
			case 'k':
select_up:
				if (!cur_groupnum)
					break;

				if (cur_groupnum <= first_group_on_screen) {
					cur_groupnum--;
					group_selection_page();
				} else {
					erase_group_arrow();
					cur_groupnum--;
					draw_group_arrow();
				}
				break;

			case 't':		/* redraw */
			case ctrl('W'):
			case ctrl('L'):
				group_selection_page();
				break;

			case '\r':	/* go into group */
			case '\n':
				space_mode = FALSE;
go_into_group:
				clear_message();
				index_point = -1;
				do {
					group_page(
					  active[my_group[cur_groupnum]].name);
				} while (index_point == -3);
				group_selection_page();
				break;

			case '/':	/* search forward */
				search_group(TRUE);
				break;

			case '?':	/* search backward */
				search_group(FALSE);
				break;

			case 'q':	/* quit */
				tass_done(0);

			case 'h':
				tass_select_help();
				group_selection_page();
				break;

			default:
			    info_message("Bad command.  Type 'h' for help.");
		}
	}
}


group_selection_page() {
	int i;
	int n;
	char new[10];
	char subs;

#ifdef SIGTSTP
	signal(SIGTSTP, select_susp);
#endif

	ClearScreen();
	printf("%s\r\n", nice_time());		/* print time in upper left */

	if (mail_check()) {			/* you have mail message */
		MoveCursor(0, 66);		/* in upper right */
		printf("you have mail\n");
	}

	center_line(1, "Group Selection");
	MoveCursor(INDEX_TOP, 0);

	first_group_on_screen = (cur_groupnum / NOTESLINES) * NOTESLINES;

	last_group_on_screen = first_group_on_screen + NOTESLINES;
	if (last_group_on_screen >= local_top)
		last_group_on_screen = local_top;

	for (i = first_group_on_screen; i < last_group_on_screen; i++) {
		switch (unread[i]) {
		case -2:
			strcpy(new, "?   ");
			break;

		case -1:
			strcpy(new, "-   ");
			break;

		case 0:
			strcpy(new, "    ");
			break;

		default:
			sprintf(new, "%-4d", unread[i]);
		}

		n = my_group[i];
		if (active[n].flag & SUBS)	/* subscribed? */
			subs = ' ';
		else
			subs = 'u';	/* u next to unsubscribed groups */

		printf("   %c %4d  %-35s %s\r\n", subs, i+1,
							active[n].name, new);
	}

	draw_group_arrow();
}


prompt_group_num(ch)
char ch;
{
int num;

	clear_message();

	if ((num = parse_num(ch, "Select group> ")) == -1) {
		clear_message();
		return FALSE;
	}
	num--;		/* index from 0 (internal) vs. 1 (user) */

	if (num >= local_top)
		num = local_top - 1;

	if (num >= first_group_on_screen
	&&  num < last_group_on_screen) {
		erase_group_arrow();
		cur_groupnum = num;
		draw_group_arrow();
	} else {
		cur_groupnum = num;
		group_selection_page();
	}

	return TRUE;
}

erase_group_arrow() {
	erase_arrow(INDEX_TOP + (cur_groupnum-first_group_on_screen) );
}

draw_group_arrow() {
	draw_arrow(INDEX_TOP + (cur_groupnum-first_group_on_screen) );
}

search_group(forward)
int forward;
{
	char buf[LEN+1];
	int i;
	extern char *regcmp();
	extern char *regex();
	char *re;
	char *prompt;

	clear_message();

	if (forward)
		prompt = "/";
	else
		prompt = "?";

	if (!parse_string(prompt, buf))
		return;

	if (strlen(buf))
		strcpy(group_search_string, buf);
	else if (!strlen(group_search_string)) {
		info_message("No search pattern");
		return;
	}

	i = cur_groupnum;

	glob_name(group_search_string, buf);

	if ((re = regcmp(buf, NULL)) == NULL) {
		info_message("Bad search pattern");
		return;
	}

	do {
		if (forward)
			i++;
		else
			i--;

		if (i >= local_top)
			i = 0;
		if (i < 0)
			i = local_top - 1;

		if (regex(re, active[my_group[i]].name) != NULL) {
			if (i >= first_group_on_screen
			&&  i < last_group_on_screen) {
				erase_group_arrow();
				cur_groupnum = i;
				draw_group_arrow();
			} else {
				cur_groupnum = i;
				group_selection_page();
			}
			return;
		}
	} while (i != cur_groupnum);

	info_message("No match");
}


tass_select_help() {

	ClearScreen();
	center_line(0, TASS_HEADER);
	center_line(1, "Group Selection Commands");

	MoveCursor(3, 0);

	printf("4        Select group 4\r\n");
	printf("^D       Page down\r\n");
	printf("^R       Reset .newsrc\r\n");
	printf("^U       Page up\r\n");
	printf("^K       Delete group\r\n");
	printf("^Y       Undelete group\r\n");
	printf("<CR>     Read current group\r\n");
	printf("<TAB>    View next unread group\r\n");
	printf("c        Mark group as all read\r\n");
	printf("g        Choose a new group by name\r\n");
	printf("j        Down a line\r\n");
	printf("k        Up a line\r\n");
	printf("q        Quit\r\n");
	printf("s        Subscribe to current group\r\n");
	printf("u        Unsubscribe to current group\r\n");
	printf("y        Yank in groups that are not in the .newsrc\r\n");
	printf("$        Reread group list from .newsrc\r\n");
	printf("/        Search forward for group\r\n");
	printf("?        Search backward for group\r\n");

	center_line(LINES, "-- hit any key --");
	ReadCh();
}


choose_new_group() {
	char buf[LEN+1];
	char *p;
	int ret;

	if (!parse_string("Newsgroup> ", buf))
		return -1;

	for (p = buf; *p && (*p == ' ' || *p == '\t'); p++) ;
	if (*p == '\0')
		return -1;

	ret = add_group(p, TRUE);
	if (ret < 0)
		info_message("Group not found in active file");

	return ret;
}


/*
 *  Add a group to the selection list (my_group[])
 *  Return the index of my_group[] if group is added or was already
 *  there.  Return -1 if named group is not in active[].
 */

add_group(s, get_unread)
char *s;
int get_unread;			/* look in .newsrc for sequencer unread info? */
{
	long h;
	int i, j;

	{			/* find the hash of the group name */
		char *t = s;

		h = *t++;
		while (*t)
			h = (h * 64 + *t++) % TABLE_SIZE;
	}

	for (i = group_hash[h]; i >= 0; i = active[i].next)
		if (strcmp(s, active[i].name) == 0) {
			for (j = 0; j < local_top; j++)
				if (my_group[j] == i)
					return j;

			active[i].flag &= ~NOTGOT;   /* mark that we got it */
			my_group[local_top] = i;

			if (get_unread)
				unread[local_top] = get_line_unread(s, i);
			else
				unread[local_top] = -2;

			local_top++;
			return local_top - 1;
		}

	return -1;
}


@EOF

chmod 644 select.c

echo x - tass.h
cat >tass.h <<'@EOF'

#define		LIBDIR		"/usr/lib/news"
#define		SPOOLDIR	"/usr/spool/news"
#define		MAILER		"/bin/rmail"

#define		TRUE		1
#define		FALSE		0

#define		LEN		200

#define		INDEX_TOP		4
#define		NOTESLINES		(LINES - INDEX_TOP - 2)
#define		RIGHT_POS		(COLS - 16)
#define		MORE_POS		(COLS - 20)

#define		MAX_FROM	25
#define		MAX_SUBJ	38
#define		TABLE_SIZE	1409

/* #define		MAX_SUBJ	(COLS - 42)	*/


struct header {
	long artnum;
	char subject[MAX_SUBJ];
	char *nore;		/* pointer into subject after Re: */
	char from[MAX_FROM];
	int thread;
	long hash;
	int inthread;
	int unread;		/* has this article been read? */
				/* 0 = read, 1 = unread, 2 = will return */
};

/*
 *  header.artnum:
 *	article number in spool directory for group
 *
 *  header.nore
 *	pointer into header.subject after the Re:'s.
 *
 *  header.hash:
 *	hash of the subject minus the re's.  For fast subject comparison
 *
 *  header.thread:
 *	initially -1
 *	points to another arts[] (struct header): zero and up
 *	-2 means article has expired (wasn't found in file search
 *	of spool directory for the group)
 *
 *  header.inthread:
 *	FALSE for the first article in a thread, TRUE for all
 *	following articles in thread
 *
 *  header.read:
 *	boolean, has this article been read or not
 */

struct group_ent {
	char *name;
	long max;
	long min;
	int next;		/* next active entry in hash chain */
	int flag;
};

#define		NOTGOT		0x01	/* haven't put in my_group yet */
#define		SUBS		0x02	/* subscribed to */


extern int top;
extern struct header *arts;
extern long *base;
extern int max_art;

extern char userid[LEN];
extern char homedir[LEN];
extern char indexdir[LEN];
extern char my_org[LEN];
extern char active_file[LEN];
extern char newsrc[LEN];
extern char newnewsrc[LEN];
extern char delgroups[LEN];
extern int top_base;
extern int LINES, COLS;
extern char *str_save();
extern char *my_malloc();
extern char *my_realloc();
extern int group_hash[TABLE_SIZE];

extern int num_active;
extern struct group_ent *active;
extern int *my_group;
extern int *unread;
extern int max_active;

extern int local_top;
extern char *eat_re();
extern char *nice_time();
extern int update;
extern int inverse_okay;

extern int tass_uid;
extern int tass_gid;
extern int real_uid;
extern int real_gid;
extern int local_index;

extern char *strcpy();
extern char *strncat();
extern char *strncpy();
extern long atol();


#define		ctrl(c)			((c) & 0x1F)

/*
 *  Assertion verifier
 */

#ifdef __STDC__
#define	assert(p)	if(! (p)) asfail(__FILE__, __LINE__, #p); else
#else
#define	assert(p)	if(! (p)) asfail(__FILE__, __LINE__, "p"); else
#endif

#define		TASS_HEADER	"Tass 3.0"

@EOF

chmod 644 tass.h

echo x - time.c
cat >time.c <<'@EOF'

#include	<sys/types.h>
#include	<time.h>


nicedate(timestr, newstr)
char *timestr, *newstr;
{
	int i;

	for (i = 0; i <= 7; i++)
		*newstr++ = timestr[i];
	if (timestr[8] != ' ')
		*newstr++ = timestr[8];
	*newstr++ = timestr[9];
	*newstr++ = ',';
	*newstr++ = ' ';
	for (i = 20;i <= 23; i++)
		*newstr++ = timestr[i];
	*newstr++ = '\0';
}

nicetime(timestr, newstr)
char *timestr, *newstr;
{
	int hours;
	char dayornite[3];

	if (timestr[11] == ' ')
		hours = timestr[12] - '0';
	else
		hours = (timestr[11]-'0')*10 + (timestr[12]-'0');
	if (hours < 12)
		strcpy(dayornite, "am");
	else
		strcpy(dayornite, "pm");
	if (hours >= 13)
		hours -= 12;
	if (!hours)
		hours = 12;
	sprintf(newstr, "%d:%c%c%s", hours, timestr[14],
					timestr[15], dayornite);
}

char *nice_time() {
	char *timestr;
	char the_date[17];
	char the_time[8];
	extern char *ctime();
	long time_now;
	static char buf[25];

	time(&time_now);
	timestr = ctime(&time_now);
	nicedate(timestr, the_date);
	nicetime(timestr, the_time);
	sprintf(buf,"%s  %s", the_date, the_time);
	return(buf);
}

@EOF

chmod 644 time.c

exit 0
-- 
skrenta at blekko.commodore.com



More information about the Alt.sources mailing list