Tass 3.2 newsreader part 3/3
Rich Skrenta
skrenta at blekko.commodore.com
Thu Apr 18 03:02:04 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 screen.c select.c spool_open.c
#
echo x - page.c
cat >page.c <<'@EOF'
#include <stdio.h>
#include <signal.h>
#include <sys/types.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 */
long note_size; /* stat size in bytes of article */
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);
#ifdef BSD
sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
#endif
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;
int i;
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 'a': /* author search forward */
case 'A': /* author search backward */
i = (ch == 'a');
n = search_author(respnum, i, group);
if (n < 0)
break;
respnum = n;
goto restart;
break;
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;
redraw_page(respnum, group);
/* 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_size > 0) {
percent = note_mark[note_page] * 100 / note_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];
char *p;
extern FILE *open_art_fp();
note_page = 0;
note_fp = open_art_fp(group_path, art);
if (note_fp == NULL) {
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';
for (p = buf; *p && *p != '\n'; p++)
if (((*p)&0x7F) < 32)
*p = ' ';
*p = '\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;
{
char *p;
for (p = str; *p; p++)
if (((*p) & 0x7F) < 32)
*p = ' ';
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';
}
tass_page_help() {
char title[100];
sprintf(title, "%s, Article Pager Commands", TASS_HEADER);
ClearScreen();
center_line(0, title);
MoveCursor(2, 0);
printf("\t4\tRead response 4 in this thread (0 is basenote)\r\n");
printf("\t<CR>\tSkip to next base article\r\n");
printf("\t<TAB>\tAdvance to next page or unread article\r\n");
printf("\taA\tAuthor search forward (A=backward)\r\n");
printf("\tb\tBack a page\r\n");
printf("\tfF\tPost a followup (F=include text)\r\n");
printf("\tH\tShow article headers\r\n");
printf("\ti\tReturn to index page\r\n");
printf("\tkK\tMark article (K=thread) as read & advance to next unread\r\n");
printf("\tm\tMail this article to someone\r\n");
printf("\tnN\tSkip to the next (N=unread) article)\r\n");
printf("\tpP\tGo to the previous (P=unread) article\r\n");
printf("\tq\tQuit\r\n");
printf("\trR\tReply through mail (R=include text) to author\r\n");
printf("\tsS\tSave article (S=thread) to file\r\n");
printf("\tt\tReturn to group selection page\r\n");
printf("\tz\tMark article as unread\r\n");
printf("\t^R\tRedisplay first page of article\r\n");
printf("\t%%, ^X\tToggle rot-13 decoding for this article\r\n");
printf("\t-\tShow last article\r\n");
printf("\t|\tPipe article into command\r\n");
center_line(LINES, "-- hit any key --");
ReadCh();
}
yank_to_addr(orig, addr)
char *orig;
char *addr;
{
char *p;
for (p = orig; *p; p++)
if (((*p) & 0x7F) < 32)
*p = ' ';
while (*addr)
addr++;
while (*orig) {
while (*orig && (*orig == ' ' || *orig == '"' || *orig == ','))
orig++;
*addr++ = ' ';
while (*orig && (*orig != ' ' && *orig != ',' && *orig != '"'))
*addr++ = *orig++;
while (*orig && (*orig == ' ' || *orig == '"' || *orig == ','))
orig++;
if (*orig == '(') {
while (*orig && *orig != ')')
orig++;
if (*orig == ')')
orig++;
}
}
*addr = '\0';
}
/*
* 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];
char new_mail_to[LEN];
char *p;
*new_mail_to = '\0';
fp = fopen(nam, "r");
if (fp == NULL) {
fprintf(stderr, "reopen of %s failed\n", nam);
return;
}
while (fgets(buf, 1024, fp) != NULL) {
for (p = buf; *p && *p != '\n'; p++) ;
*p = '\0';
if (*buf == '\0')
break;
if (strncmp(buf, "To: ", 4) == 0) {
strncpy(buf2, &buf[4], LEN);
buf2[LEN-1] = '\0';
yank_to_addr(buf2, new_mail_to);
} else if (strncmp(buf, "Cc: ", 4) == 0) {
strncpy(buf2, &buf[4], LEN);
buf2[LEN-1] = '\0';
yank_to_addr(buf2, new_mail_to);
}
}
fclose(fp);
if (new_mail_to[0] == ' ')
strcpy(mail_to, &new_mail_to[1]);
else
strcpy(mail_to, new_mail_to);
}
mail_to_someone() {
char nam[100];
FILE *fp;
char ch;
char buf[200];
char mail_to[LEN+1];
char subj[LEN+1];
if (!parse_string("Mail article to: ", mail_to))
return;
if (mail_to[0] == '\0')
return;
setuid(real_uid);
setgid(real_gid);
sprintf(nam, "%s/.letter", homedir);
if ((fp = fopen(nam, "w")) == NULL) {
fprintf(stderr, "can't open %s: ", nam);
perror("");
setuid(tass_uid);
setgid(tass_gid);
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, "");
add_signature(fp, TRUE);
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':
setuid(tass_uid);
setgid(tass_gid);
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("\r\nMailing to %s...", mail_to);
fflush(stdout);
sprintf(buf, "%s %s < %s", MAILER,
mail_to, nam);
if (invoke_cmd(buf)) {
printf("Message sent\r\n");
fflush(stdout);
goto mail_to_someone_done;
} else {
printf("Command failed: %s\r\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("");
setuid(tass_uid);
setgid(tass_gid);
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, "> ");
}
add_signature(fp, TRUE);
fclose(fp);
ch = 'e';
while (1) {
switch (ch) {
case 'e':
invoke_editor(nam);
break;
case 'a':
setuid(tass_uid);
setgid(tass_gid);
return FALSE;
case 's':
strcpy(mail_to, note_from_addr);
find_new_to(nam, mail_to);
printf("\r\nMailing to %s... ", mail_to);
fflush(stdout);
sprintf(buf, "%s %s < %s", MAILER, mail_to, nam);
if (invoke_cmd(buf)) {
printf("Message sent\r\n");
fflush(stdout);
goto mail_to_author_done;
} else {
printf("Command failed: %s\r\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("");
setuid(tass_uid);
setgid(tass_gid);
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, "> ");
}
add_signature(fp, FALSE);
fclose(fp);
ch = 'e';
while (1) {
switch (ch) {
case 'e':
invoke_editor(nam);
break;
case 'a':
setuid(tass_uid);
setgid(tass_gid);
return FALSE;
case 'p':
printf("Posting... ");
fflush(stdout);
sprintf(buf, "%s/inews -h < %s", LIBDIR, nam);
if (invoke_cmd(buf)) {
printf("article posted\r\n");
fflush(stdout);
goto post_response_done;
} else {
printf("article rejected\r\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 - 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);
#ifdef BSD
sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
#endif
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]);
MoveCursor(INDEX_TOP + (cur_groupnum -
first_group_on_screen), 47);
printf(" ");
MoveCursor(LINES, 0);
fflush(stdout);
/* 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 '\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 ' ':
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];
char buf2[LEN+1];
int i;
int len;
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 string");
return;
}
i = cur_groupnum;
make_lower(group_search_string, buf);
len = strlen(buf);
do {
if (forward)
i++;
else
i--;
if (i >= local_top)
i = 0;
if (i < 0)
i = local_top - 1;
make_lower(active[my_group[i]].name, buf2);
if (match(buf, buf2, len)) {
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() {
char title[100];
sprintf(title, "%s, Group Selection Commands", TASS_HEADER);
ClearScreen();
center_line(0, title);
MoveCursor(2, 0);
printf("\t4\tSelect group 4\r\n");
printf("\t^D\tPage down\r\n");
printf("\t^R\tReset .newsrc\r\n");
printf("\t^U\tPage up\r\n");
printf("\t^K\tDelete group\r\n");
printf("\t^Y\tUndelete group\r\n");
printf("\t<CR>\tRead current group\r\n");
printf("\t<TAB>\tView next unread group\r\n");
printf("\tc\tMark group as all read\r\n");
printf("\tg\tChoose a new group by name\r\n");
printf("\tj\tDown a line\r\n");
printf("\tk\tUp a line\r\n");
printf("\tq\tQuit\r\n");
printf("\ts\tSubscribe to current group\r\n");
printf("\tu\tUnsubscribe to current group\r\n");
printf("\ty\tYank in groups that are not in the .newsrc\r\n");
printf("\t$\tReread group list from .newsrc\r\n");
printf("\t/?\tGroup search forward (?=backward)\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;
extern long hash_groupname();
h = hash_groupname(s);
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 - spool_open.c
cat >spool_open.c <<'@EOF'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "tass.h"
/* Hopefully one of these is right for you. */
#ifdef BSD
# include <sys/types.h>
# include <sys/dir.h>
# define DIR_BUF struct direct
# define D_LENGTH d_namlen
#endif
#ifdef M_XENIX
# include <sys/ndir.h>
# define DIR_BUF struct direct
# define D_LENGTH d_namlen
#endif
#ifndef DIR_BUF
# include <sys/types.h>
# include <dirent.h>
# define DIR_BUF struct dirent
# define D_LENGTH d_reclen
#endif
char *
is_remote() {
return "";
}
nntp_startup() {
}
nntp_finish() {
}
FILE *open_active_fp() {
FILE *fp;
fp = fopen(active_file, "r");
if (fp == NULL) {
fprintf(stderr, "can't open %s: ", active_file);
perror("");
exit(1);
}
return fp;
}
FILE *
open_art_fp(group_path, art)
char *group_path;
long art;
{
char buf[LEN];
struct stat sb;
extern long note_size;
sprintf(buf, "%s/%s/%ld", SPOOLDIR, group_path, art);
if (stat(buf, &sb) < 0)
note_size = 0;
else
note_size = sb.st_size;
return fopen(buf, "r");
}
open_header_fd(group_path, art)
char *group_path;
long art;
{
char buf[LEN];
sprintf(buf, "%s/%s/%ld", SPOOLDIR, group_path, art);
return open(buf, 0);
}
/*
* Longword comparison routine for the qsort()
*/
base_comp(a, b)
long *a;
long *b;
{
if (*a < *b)
return -1;
if (*a > *b)
return 1;
return 0;
}
/*
* Read the article numbers existing in a group's spool directory
* into base[] and sort them. base_top is one past top.
*/
setup_base(group, group_path)
char *group;
char *group_path;
{
DIR *d;
DIR_BUF *e;
long art;
char buf[200];
top_base = 0;
sprintf(buf, "%s/%s", SPOOLDIR, group_path);
if (access(buf, 4) != 0)
return;
d = opendir(buf);
if (d != NULL) {
while ((e = readdir(d)) != NULL) {
art = my_atol(e->d_name, e->D_LENGTH);
if (art >= 0) {
if (top_base >= max_art)
expand_art();
base[top_base++] = art;
}
}
closedir(d);
qsort(base, top_base, sizeof(long), base_comp);
}
}
@EOF
chmod 644 spool_open.c
exit 0
More information about the Alt.sources
mailing list