skrenta@blekko.commodore.com (Rich Skrenta) (04/18/91)
# 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