home *** CD-ROM | disk | FTP | other *** search
- /*
- SNEWS 2.0
-
- snews - a simple threaded news reader
-
-
- Copyright (C) 1991 John McCombs, Christchurch, NEW ZEALAND
- john@ahuriri.gen.nz
- PO Box 2708, Christchurch, NEW ZEALAND
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 1, as
- published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- See the file COPYING, which contains a copy of the GNU General
- Public License.
-
- */
-
- #include "defs.h"
- #include "snews.h"
-
-
- INFO my_stuff;
- char iobuf[IOBUFSIZE];
- char search_text[256];
-
-
- /*------------------------------- main --------------------------------*/
- void main(void)
- {
- ACTIVE *gp, *head;
- int done;
-
- ignore_signals();
-
- printf("loading config...\n");
- if (load_stuff()) {
-
- printf("loading active...\n");
- head = load_active_file();
- printf("loading read list...\n");
- load_read_list();
- printf("loading history...\n");
- load_history_list();
-
- scrinit();
- clrscr();
-
- done = FALSE;
- gp = NULL;
-
- while (!done) {
- if ((gp = select_group(head, gp)) != NULL) {
- done |= read_group(gp);
- } else {
- done = TRUE;
- }
- }
-
- screxit();
- clrscr();
-
- free_hist_list();
- save_read_list();
- close_active_file();
-
- } else {
- fprintf(stderr, "Couldn't find necessary item in the .rc files\n");
- }
- }
-
-
-
-
- /*-------------------------- find which group to read -----------------------*/
- ACTIVE *select_group(ACTIVE *head, ACTIVE *current)
- {
- /*
- * Present the list of groups, and allow him to move up and down with
- * the arrow and PgUp and PgDn keys.
- */
-
- ACTIVE *top; /* newsgroup at the top of the page */
- ACTIVE *this; /* current newsgroup */
- ACTIVE *tmp_ng;
- int exit_code; /* why we are exiting the loop */
-
- int ch, i, j, articles, unread;
-
- this = (current == NULL) ? head : current;
-
- top = head;
- exit_code = 0;
-
- show_groups(&top, this, TRUE, head);
-
- while (exit_code == 0) {
-
- ch = getch();
- switch (ch) {
-
- case 0 :
- case 0xE0 :
-
- ch = getch();
- switch (ch) {
-
- case F1 :
- show_help(HELP_GROUP);
- show_groups(&top, this, TRUE, head);
- break;
-
- case UP_ARR :
- if (this->last != NULL) this = this->last;
- break;
-
- case DN_ARR :
- if (this->next != NULL) this = this->next;
- break;
-
- case PGUP :
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this->last == NULL) break;
- this = this->last;
- }
- break;
-
- case PGDN :
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this->next == NULL) break;
- this = this->next;
- }
- break;
-
- case HOME :
- top = this = head;
- show_groups(&top, this, TRUE, head);
- break;
-
- case END :
- this = head;
- while (this->next != NULL)
- this = this->next;
- break;
- }
- break;
-
- case 'p' :
- case 'P' :
- post(NULL, this->group);
- show_groups(&top, this, TRUE, head);
- break;
-
- case 'h' :
- case 'H' :
- show_help(HELP_GROUP);
- show_groups(&top, this, TRUE, head);
- break;
-
- case 'c' :
- case 'C' :
- mark_group_as_read(this);
- show_groups(&top, this, TRUE, head);
- break;
-
- case TAB :
- tmp_ng = this->next;
- unread = 0;
- while (tmp_ng != NULL) {
- articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
- for (j = 0; j < articles; j++) {
- if ( *((tmp_ng->read_list)+j) == FALSE)
- unread++;
- }
- if (unread > 0) break;
- tmp_ng = tmp_ng->next;
- }
- if (unread > 0) {
- this = tmp_ng;
- } else {
- message("-- No more articles to read --");
- }
- break;
-
- case ENTER :
- exit_code = EX_DONE;
- break;
-
- case ESCAPE :
- exit_code = EX_QUIT;
- break;
- };
- if (exit_code == 0)
- show_groups(&top, this, FALSE, head);
- }
-
- if (exit_code == EX_DONE)
- return(this);
- else
- return(NULL);
-
- }
-
-
-
- /*---------------------------- help screen ----------------------------------*/
-
- void help(char *text)
- {
- printf("%*s", _columns / 2 - 40, "");
-
- while (*text) {
- if (*text == '\n')
- clreol(), _line++;
- putch(*text++);
- }
- }
-
- void show_help(int h)
- {
- char *type[] = {"Group Help", "Thread Help", "Article Help"};
- char *msg = COPYRIGHT;
- int x;
-
- gotoxy(1,1);
- textbackground(LIGHTGRAY); textcolor(BLACK);
- printf("%-18s %*s", type[h], _columns - 19, VERSION);
- gotoxy(1,2);
- x = _columns / 2 - strlen(msg) / 2;
- printf("%*s%s", x, "", msg);
- clreol();
- textbackground(BLACK); textcolor(LIGHTGRAY);
- putch('\n');
-
- help("\n");
- help(" PageUp - move display up one page\n");
- help(" PageDn - move display down one page\n");
- help(" Home - move to top of list/article\n");
- help(" End - move to bottom of list/article\n");
- help(" Up - move up one line\n");
- help(" Down - move down one line\n");
-
- switch (h) {
- case HELP_GROUP :
- help("\n");
- help(" Tab - go to next newgroup with unread articles\n");
- help(" Enter - enter selected newsgroup\n\n");
- help(" c - mark all articles in selected group as read\n");
- help(" p - post article to selected group\n");
- break;
-
- case HELP_THREAD :
- help("\n");
- help(" Tab - go to next thread with unread articles or\n");
- help(" enter this thread if it is already selected\n");
- help(" and move to the first unread article in the thread\n");
- help(" Enter - read the selected thread from the beginning\n");
- help(" Backspace - go to the last article in the selected thread\n\n");
- help(" c - mark all articles in selected thread as read\n");
- help(" s - save selected thread to disk\n");
- help(" p - post article to this group\n\n");
- help(" + - search for text in subjects\n");
- help(" / - search for text in article bodies\n");
- break;
-
- case HELP_ARTICLES:
- help(" Left - scroll right\n");
- help(" Right - scroll left\n\n");
- help(" Tab - go to next unread article\n");
- help(" Enter - go to next article in thread\n");
- help(" Backspace - go to previous article in thread\n\n");
- help(" s - save this article to disk\n");
- help(" p - post new article to current group\n");
- help(" f - post follow-up to this article\n");
- help(" r - reply to sender of this article via e-mail\n");
- help(" m - mail this article to someone\n");
- help(" + / - search for text in following articles\n");
- help(" - ? - search for text in preceding articles\n");
- break;
- };
-
- clreos();
- message("-- Press any key to continue --");
- getch();
-
- }
-
-
-
-
- /*-------------------- show the list of active groups -----------------------*/
- void show_groups(ACTIVE **top, ACTIVE *this, int force, ACTIVE *head)
- {
- /*
- * This routine takes 'top', a pointer to the first line on the screen
- * and 'this' a pointer to where we want to be, and updates the screen.
- * A maker to this is maintained, and the screen is repainted, where
- * necessary
- */
-
- static last_y;
- static last_index;
- int i, ur;
- ACTIVE *that;
- char buf[32];
-
- /*
- * If 'this' is above the 'top' or it is more than a screen length below,
- * or'this and 'top' are both zero, ie first time repaint the screen
- */
- /* if ((((*top)->index == 0) && (this->index == 0)) || force ||
- ((*top)->index > this->index) ||
- (this->index - (*top)->index) > PAGE_LENGTH-1) { */
- if ( force ||
- ((*top)->index > this->index) ||
- (this->index - (*top)->index) > PAGE_LENGTH-1) {
-
- gotoxy(1,1);
- textbackground(LIGHTGRAY); textcolor(BLACK);
- sprintf(buf, "Group %d of %d", last_index + 1, head->groups);
- printf("Select Group %*s", _columns - 13, buf);
- gotoxy(1,2);
- clreol();
- textbackground(BLACK); textcolor(LIGHTGRAY);
-
- /* now adjust the top */
- *top = this;
- for (i = 0; i < PAGE_LENGTH/2; i++) {
- if ((*top)->last == NULL) break;
- *top = (*top)->last;
- }
-
- that = *top;
- for (i = 0; i < PAGE_LENGTH+1; i++) {
- gotoxy(1, i+PAGE_HEADER);
- if (that == NULL) break;
- ur = count_unread_in_group(that);
- if (ur > 0)
- printf(" %4d %4ld %s", ur,
- that->hi_num - that->lo_num, that->group);
- else
- printf(" %4ld %s",
- that->hi_num - that->lo_num, that->group);
- clreol();
- that = that->next;
- }
-
- clreos();
- last_y = this->index - (*top)->index;
- gotoxy(2, last_y + PAGE_HEADER);
- puts("->");
- last_index = this->index;
-
- } else {
-
- gotoxy(2, last_y + PAGE_HEADER);
- puts(" ");
-
- last_y += (this->index - last_index);
- gotoxy(2, last_y + PAGE_HEADER);
- puts("->");
- last_index = this->index;
-
- }
-
- gotoxy(_columns - 16,1);
- textbackground(LIGHTGRAY); textcolor(BLACK);
- sprintf(buf, "Group %d of %d", last_index + 1, head->groups);
- printf("%17s", buf);
- textbackground(BLACK); textcolor(LIGHTGRAY);
-
- message("ESC=quit TAB=next unread group ENTER=read group F1=help");
- }
-
-
- /*--------------------------- process message -------------------------------*/
- int read_group(ACTIVE *gp)
- {
- /*
- * We now have newsgroup. Access the directory and try to read
- * the newsgroup articles, extracting the headers.
- */
-
- ARTICLE *start;
-
- if (gp->lo_num < gp->hi_num) {
- start = get_headers(gp);
- select_thread(gp, start);
- free_header(start);
- }
-
- return(0);
- }
-
-
- /*-------------------- show the list of active groups -----------------------*/
- void show_threads(ACTIVE *gp, ARTICLE **top, ARTICLE *this, int force,
- ARTICLE *head)
- {
- /*
- * This routine takes 'top', a pointer to the first line on the screen
- * and 'this' a pointer to where we want to be, and updates the screen.
- * A maker to this is maintained, and the screen is repainted, where
- * necessary
- */
-
- static last_y;
- static last_index;
- int i;
- ARTICLE *that;
- int ur;
- char buf[32];
-
- /*
- * If 'this' is above the 'top' or it is more than a screen length below,
- * or'this and 'top' are both zero, ie first time repaint the screen
- */
- /* if ((((*top)->index == 0) && (this->index == 0)) || force ||
- ((*top)->index > this->index) ||
- (this->index - (*top)->index) > PAGE_LENGTH-1) { */
- if ( force ||
- ((*top)->index > this->index) ||
- (this->index - (*top)->index) > PAGE_LENGTH-1) {
-
- for (that = head, ur = 0; that; that = that->next)
- ur += count_unread_in_thread(gp, that);
-
- gotoxy(1,1);
- textbackground(LIGHTGRAY); textcolor(BLACK);
- sprintf(buf, "Thread %d of %d", last_index + 1, gp->threads);
- printf("Select Thread %*s", _columns - 14, buf);
- gotoxy(1,2);
- sprintf(buf, "%ld Articles, %d unread",
- gp->hi_num - gp->lo_num, ur);
- printf("Group: %-*.*s %26s", _columns - 34, _columns - 34,
- gp->group, buf);
- textbackground(BLACK); textcolor(LIGHTGRAY);
-
- /* now adjust the top */
- *top = this;
- for (i = 0; i < PAGE_LENGTH/2; i++) {
- if ((*top)->last == NULL) break;
- *top = (*top)->last;
- }
-
- that = *top;
- for (i = 0; i < PAGE_LENGTH+1; i++) {
- gotoxy(1, i+PAGE_HEADER);
- if (that == NULL) break;
- ur = count_unread_in_thread(gp, that);
- if (ur > 0)
- printf(" %4d %4d %s", ur,
- that->num_articles, that->header);
- else
- printf(" %4d %s",
- that->num_articles, that->header);
- clreol();
- that = that->next;
- }
-
- clreos();
-
- last_y = this->index - (*top)->index;
- gotoxy(2, last_y + PAGE_HEADER);
- puts("->");
- last_index = this->index;
-
- } else {
-
- gotoxy(2, last_y + PAGE_HEADER);
- puts(" ");
-
- last_y += (this->index - last_index);
- gotoxy(2, last_y + PAGE_HEADER);
- puts("->");
- last_index = this->index;
-
- gotoxy(_columns - 17,1);
- textbackground(LIGHTGRAY); textcolor(BLACK);
- sprintf(buf, "Thread %d of %d", last_index + 1, gp->threads);
- printf("%18s", buf);
- textbackground(BLACK); textcolor(LIGHTGRAY);
-
- }
-
- message("ESC=select group TAB=next unread ENTER=next article F1=help");
- }
-
-
-
- /*--------------- search through threads ------------------------------------*/
- ARTICLE *search_thread(ACTIVE *gp, ARTICLE *head, int search_body)
- {
- int offset = 0, found = FALSE, irq = FALSE;
- ARTICLE *this;
- ART_ID *art;
- TEXT *tx;
- LINE *text;
- char *fn;
- char prompt[128], pattern[128];
-
- if (head == NULL || head->next == NULL)
- return head;
-
- sprintf(prompt, "Search %s for? [%s] ",
- search_body ? "articles" : "subjects", search_text);
- lmessage(prompt);
- if (gets(pattern) == NULL)
- return head;
-
- if (strlen(pattern) > 0)
- strcpy(search_text, pattern);
- else
- strcpy(pattern, search_text);
-
- strlwr(pattern);
-
- if (search_body) {
-
- message("*** searching - please wait (press ESC to abort) ***");
- tflush();
-
- for (this = head->next; this != NULL; this = this->next) {
-
- /* for each article */
- for (art = this->art_num; art != NULL; art = art->next_art) {
-
- fn = make_news_group_name(gp->group);
- tx = load_article(fn, art->art_off);
-
- for (text = tx->start; !found && text != NULL;
- text = text->next) {
- strlwr(text->data);
- found = (strstr(text->data, pattern) != NULL);
- }
-
- free_article(tx);
-
- if (kbhit())
- irq = (getch() == 27);
-
- if (found || irq)
- break;
- }
-
- if (found || irq)
- break;
- }
-
- } else {
-
- for (this = head->next; this != NULL; this = this->next) {
-
- strcpy(prompt, this->header);
- strlwr(prompt);
- found = (strstr(prompt, pattern) != NULL);
-
- if (found)
- break;
- }
-
- }
-
- if (!found && !irq) {
- sprintf(prompt, "*** '%s' not found - press any key ***", pattern);
- message(prompt);
- getch();
- }
-
- return found ? this : head;
- }
-
-
-
- /*-------------------------- find which thread to read ----------------------*/
- void select_thread(ACTIVE *gp, ARTICLE *head)
- {
- /*
- * Present the list of threads, and allow him to move up and down with
- * the arrow and PgUp and PgDn keys.
- */
-
- ARTICLE *top; /* thread at the top of the page */
- ARTICLE *this; /* current thread */
- ARTICLE *th;
- ART_ID *art;
- int exit_code; /* why we are exiting the loop */
-
- int ch, i, idx, hit, a_ct;
-
- this = head;
- top = head;
- exit_code = 0;
-
- show_threads(gp, &top, this, TRUE, head);
-
- while (exit_code == 0) {
-
- ch = getch();
- switch (ch) {
-
- case 0 :
- case 0xE0 :
- ch = getch();
-
- switch (ch) {
-
- case F1 :
- show_help(HELP_THREAD);
- show_threads(gp, &top, this, TRUE, head);
- break;
-
- case UP_ARR :
- if (this->last != NULL) this = this->last;
- break;
-
- case DN_ARR :
- if (this->next != NULL) this = this->next;
- break;
-
- case PGUP :
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this->last == NULL) break;
- this = this->last;
- }
- break;
-
- case PGDN :
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this->next == NULL) break;
- this = this->next;
- }
- break;
-
- case HOME :
- top = this = head;
- show_threads(gp, &top, this, TRUE, head);
- break;
- case END :
- this = head;
- while (this->next != NULL)
- this = this->next;
- break;
- }
- break;
-
- case 's' :
- case 'S' :
- save_thread_to_disk(gp, this);
- break;
-
- case 'p' :
- case 'P' :
- post(NULL, gp->group);
- show_threads(gp, &top, this, TRUE, head);
- break;
-
- case 'h' :
- case 'H' :
- show_help(HELP_THREAD);
- show_threads(gp, &top, this, TRUE, head);
- break;
-
- case TAB :
- /*
- * Go to the next unread article. Work through each
- * thread, looking at each article to see if it's been
- * read
- */
-
- /* for each thread */
- th = this;
- hit = FALSE;
- while (th != NULL) {
-
- art = th->art_num;
- a_ct = 0;
-
- /* for each article */
- while (art != NULL) {
- idx = (int)(art->id - gp->lo_num - 1);
- a_ct++;
- if ( *((gp->read_list)+idx) == FALSE) {
- hit = TRUE;
- break;
- }
- art = art->next_art;
- }
- if (hit) break;
- th = th->next;
- }
-
- if (hit) {
- if (this == th )
- read_thread(gp, this, art, a_ct);
- this = th;
- show_threads(gp, &top, this, TRUE, head);
- }
- break;
-
- case 'c' :
- case 'C' :
- mark_thread_as_read(gp, this);
- show_threads(gp, &top, this, TRUE, head);
- break;
-
- case ENTER :
- read_thread(gp, this, this->art_num, 1);
- show_threads(gp, &top, this, TRUE, head);
- break;
-
- case BACKSP :
- art = this->art_num;
- a_ct = 1;
- while (art->next_art != NULL) {
- a_ct++;
- art = art->next_art;
- }
- read_thread(gp, this, art, a_ct);
- show_threads(gp, &top, this, TRUE, head);
- break;
-
- case '+' :
- case '/' :
- this = search_thread(gp, this, ch == '/');
- show_threads(gp, &top, this, TRUE, head);
- break;
-
- case ESCAPE :
- exit_code = EX_QUIT;
- break;
- };
- if (exit_code == 0)
- show_threads(gp, &top, this, FALSE, head);
- }
-
-
- }
-
-
-
-
- /*------------------------ save a thread ------------------------------*/
-
- int cmp(TEXT **art1, TEXT **art2)
- {
- char *subj1 = (*art1) -> subject, *subj2 = (*art2) -> subject;
- while (isspace(*subj1)) subj1++;
- while (isspace(*subj2)) subj2++;
- return strcmp(subj1, subj2);
- }
-
- void save_thread_to_disk(ACTIVE *gp, ARTICLE *this)
- {
- ART_ID *id;
- char *fn;
- TEXT *tx;
- LINE *ln;
- char fnx[256];
- int ch, art, idx;
- TEXT *text[MAXART];
- FILE *tmp = NULL;
- time_t now;
- struct tm *tmnow;
- char timestr[64];
-
- lmessage("Enter filename? ");
- gets(fnx);
-
- if (access(fnx, 0) == 0) {
-
- message("File exists - append(y/n)? ");
- while (((ch = getch()) != 'y') && (ch != 'n'));
- if (ch == 'y') {
- if ((tmp = fopen(fnx, "at")) == NULL) {
- message("*** cannot open file for appending - "
- "please any key ***");
- getch();
- }
- }
-
- } else {
-
- if ((tmp = fopen(fnx, "wt")) == NULL) {
- message("*** cannot open file for output - press any key ***");
- getch();
- }
-
- setvbuf(tmp, iobuf, _IOFBF, IOBUFSIZE);
- }
-
- if (tmp != NULL) {
-
- fn = make_news_group_name(gp->group);
-
- id = this->art_num;
- idx = 0;
-
- while (id != NULL && idx < MAXART) {
- text[idx++] = load_article(fn, id->art_off);
- id = id->next_art;
- }
-
- qsort(text, idx, sizeof(TEXT *), cmp);
-
- time(&now);
- tmnow = localtime(&now);
- strftime(timestr, sizeof(timestr), "%a %b %d %H:%M:%S %z %Y", tmnow);
-
- for ( art = 0; art < idx; art++ ) {
-
- tx = text[art];
-
- ln = tx->top;
- while (ln != NULL && strncmp(ln->data, "Path: ", 6)) {
- ln = ln->next;
- }
- strcpy(fn, ln->data + 6);
- for ( ch = strlen(fn); fn[ch - 1] == '\n' || fn[ch - 1] == '\r'; ch--)
- fn[ch - 1] = 0;
- fprintf(tmp, "From %s %s\n", fn, timestr);
-
- ln = tx->top;
- while (ln != NULL) {
- fputs(ln->data, tmp);
- ln = ln->next;
- }
-
- fputs("\n\n", tmp);
- free_article(tx);
- }
-
- fclose(tmp);
- }
- }
-
-
-
- /*------------------------ search through articles --------------------*/
- int search_message(char *fn, ART_ID *current, int forward)
- {
- int offset = 0, found = FALSE;
- TEXT *tx;
- LINE *text;
- char prompt[128], pattern[128];
-
- sprintf(prompt, "Search %s for? [%s] ",
- forward ? "forward" : "backward", search_text);
- lmessage(prompt);
- if (gets(pattern) == NULL)
- return 0;
-
- if (strlen(pattern) > 0)
- strcpy(search_text, pattern);
- else
- strcpy(pattern, search_text);
-
- strlwr(pattern);
-
- for (;;) {
-
- current = forward ? current->next_art : current->last_art;
- if (current == NULL)
- break;
-
- offset++;
- tx = load_article(fn, current->art_off);
-
- for (text = tx->start; !found && text != NULL; text = text->next) {
- strlwr(text->data);
- found = (strstr(text->data, pattern) != NULL);
- }
-
- free_article(tx);
-
- if (found)
- break;
- }
-
- if (!found) {
- sprintf(prompt, "*** '%s' not found - press any key ***", pattern);
- message(prompt);
- getch();
- }
-
- return found ? offset : 0;
- }
-
-
- /*------------------------ read a thread ------------------------------*/
- int read_thread(ACTIVE *gp, ARTICLE *this, ART_ID *first, int a_ct)
- {
- ART_ID *id;
- char *fn;
- int idx, res;
- TEXT *tx;
- char author[128], msg_id[128];
- CROSS_POSTS *h, *h0;
- ACTIVE *gx;
-
-
- fn = make_news_group_name(gp->group);
-
- id = first;
-
- while (id != NULL) {
-
- tx = load_article(fn, id->art_off);
-
- res = read_article(gp, tx, a_ct, this->num_articles);
-
- /* mark this article as read */
- idx = (int) ((id->id) - gp->lo_num - 1);
- (gp->read_list)[idx] = TRUE;
-
- /* mark the crossposts */
- get_his_stuff(tx, author, msg_id);
- if ((h0 = look_up_history(msg_id, gp->group)) != NULL) {
-
- h = h0;
- while (h != NULL) {
- gx = find_news_group(h->group);
- idx = (int) ((h->art_num) - gx->lo_num - 1);
- if ( 0 <= idx && idx < gx->hi_num )
- (gx->read_list)[idx] = TRUE;
- h = h->next;
- }
-
- free_cross_post_list(h0);
- }
-
- free_article(tx);
-
- if (res == EX_QUIT) break;
-
- if (res == EX_NEXT_UNREAD) {
-
- while (id != NULL) {
- idx = (int)(id->id - gp->lo_num - 1);
- if ( *((gp->read_list)+idx) == FALSE) {
- break;
- }
- a_ct++;
- id = id->next_art;
- }
-
- } else if (res == EX_SEARCH_FORW) {
- res = search_message(fn, id, 1);
- while (res--) {
- a_ct++;
- id = id->next_art;
- }
- } else if (res == EX_SEARCH_BACKW) {
- res = search_message(fn, id, 0);
- while (res--) {
- a_ct--;
- id = id->last_art;
- }
- } else if (res == EX_PREVIOUS) {
- a_ct--;
- id = id->last_art;
- } else {
- a_ct++;
- id = id->next_art;
- }
- }
-
- return(res);
- }
-
-
-
-
- /*------------------------- read the headers --------------------------*/
-
- int strip_off_part(char *str)
- {
- char *ptr = str + strlen(str);
-
- /* strip off (case-insensitive) things like:
- - "Part01/10"
- - "Part 01/10"
- - "Part 01 of 10"
- - "[1/10]"
- - "(1 of 10)"
- - "1 of 10"
- - "Patch02a/04"
- - "Patch20"
- */
-
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- if ( ptr > str && (ptr[-1] == ')' || ptr[-1] == ']') )
- ptr--;
-
- while ( ptr > str && isdigit(ptr[-1]) )
- ptr--;
-
- if ( !isdigit(*ptr) )
- return 0;
-
- if ( ptr > str && ptr[-1] == '/' )
- ptr--;
- else if ( ptr > str + 3 && strnicmp(ptr - 4, " of ", 4) == 0 )
- ptr -= 4;
- else if ( ptr > str + 4 && strnicmp(ptr - 5, "Patch", 5) == 0 )
- {
- ptr -= 5;
- goto label;
- }
- else if ( ptr > str + 5 && strnicmp(ptr - 6, "Patch ", 6) == 0 )
- {
- ptr -= 6;
- goto label;
- }
- else
- return 0;
-
- if ( ptr > str && 'a' <= ptr[-1] && ptr[-1] <= 'z' )
- ptr--;
-
- while ( ptr > str && isdigit(ptr[-1]) )
- ptr--;
-
- if ( !isdigit(*ptr) )
- return 0;
-
- if ( ptr > str && (ptr[-1] == '(' || ptr[-1] == '[') )
- ptr--;
-
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- if ( ptr > str + 3 && strnicmp(ptr - 4, "Part", 4) == 0 )
- ptr -= 4;
-
- label:
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- if ( ptr > str && ptr[-1] == ',' )
- ptr--;
- else if ( ptr > str && ptr[-1] == ':' )
- ptr--;
-
- *ptr = 0;
- return 1;
- }
-
- char *skip_vi(char *str)
- {
- char *ptr = str;
-
- /* skip things like "v02i0027: " */
-
- while ( isspace(*ptr) )
- ptr++;
-
- if ( *ptr++ != 'v' )
- return str;
-
- if ( !isdigit(*ptr) )
- return str;
-
- while ( isdigit(*ptr) )
- ptr++;
-
- if ( *ptr++ != 'i' )
- return str;
-
- if ( !isdigit(*ptr) )
- return str;
-
- while ( isdigit(*ptr) )
- ptr++;
-
- if ( *ptr++ != ':' )
- return str;
-
- if ( *ptr++ != ' ' )
- return str;
-
- while ( isspace(*ptr) )
- ptr++;
-
- return ptr;
- }
-
- int smartcmp(char *str1, char *str2)
- {
- char s1[256], s2[256];
-
- strcpy(s1, str1);
- strcpy(s2, str2);
-
- if ( strip_off_part(s1) && strip_off_part(s2) )
- {
- str1 = skip_vi(s1);
- str2 = skip_vi(s2);
- }
-
- return stricmp(str1, str2);
- }
-
- ARTICLE *get_headers(ACTIVE *gp)
- {
- /*
- * Read the files and get the headers
- */
-
- char *fn;
- char buf[MAXLINE], fnx[256], *buf_p;
- long g, n_read;
- FILE *tmp_file;
-
- ARTICLE *start, *that, *tmp;
- ARTICLE **ptr;
- ART_ID *art_this, *new;
- int ct_art, cmp;
-
- n_read = 0;
- ct_art = 0;
- start = NULL;
-
- fn = make_news_group_name(gp->group);
- sprintf(fnx, "%s.IDX", fn);
-
- if ((tmp_file = flockopen(fnx, "rb")) != NULL) {
-
- setvbuf(tmp_file, iobuf, _IOFBF, IOBUFSIZE);
-
- for (g = gp->lo_num+1; g <= gp->hi_num; g++) {
-
- if ((n_read++ % 100) == 0) {
- gotoxy(1,PAGE_SIZE);
- textbackground(LIGHTGRAY); textcolor(BLACK);
- printf("%d articles processed", n_read-1);
- clreol();
- textbackground(BLACK); textcolor(LIGHTGRAY);
- tflush();
- }
-
- /*
- * Read the index
- * Search the linked list for the subject
- * - allocate a new subject if necessary
- * - add to the existing list
- */
-
- if (fgets(buf, sizeof(buf), tmp_file) == NULL) {
- gotoxy(1,PAGE_SIZE);
- fprintf(stderr, "\nsnews: index file is corrupt\n");
- exit(1);
- }
-
- /* check all is in sync */
- if (g != atol(buf+9)) {
- gotoxy(1,PAGE_SIZE);
- fprintf(stderr, "\nsnews: article %ld found when %ld"
- "expected\n", atol(buf+9), g);
- exit(1);
- }
-
- /* skip the two eight digit numbers and the 9 and the spaces */
- buf_p = buf+28;
- eat_gunk(buf_p);
-
- for ( tmp = start; tmp != NULL; ) {
-
- cmp = smartcmp(buf_p, tmp->header);
-
- if (cmp > 0) {
- if (tmp->left)
- tmp = tmp->left;
- else {
- ptr = &(tmp->left);
- tmp = NULL;
- break;
- }
- }
- else if (cmp < 0) {
- if (tmp->right)
- tmp = tmp->right;
- else {
- ptr = &(tmp->right);
- tmp = NULL;
- break;
- }
- }
- else {
- /* found this subject */
- tmp->num_articles++;
-
- /* allocate new article number */
- new = xmalloc(sizeof (ART_ID));
- new->id = g;
- new->art_off = atol(buf);
-
- /* place it at the end */
- art_this = tmp->art_num;
- while (art_this->next_art != NULL) {
- art_this = art_this->next_art;
- }
- art_this->next_art = new;
-
- new->last_art = art_this;
- new->next_art = NULL;
-
- break;
- }
-
- }
-
- if (tmp == NULL) {
- /* not found - allocate new thread */
-
- if (start == NULL) {
- start = xmalloc(sizeof (ARTICLE));
- that = start;
- that->last = NULL;
- } else {
- ct_art++;
- that->next = xmalloc(sizeof (ARTICLE));
- that->next->last = that;
- that = that->next;
- *ptr = that;
- }
-
- that->next = NULL;
- that->index = ct_art;
- that->left = that->right = NULL;
-
- /* store article data */
- that->header = xmalloc(strlen(buf_p) + 1);
- strcpy(that->header, buf_p);
- that->num_articles = 1;
-
- that->art_num = xmalloc(sizeof (ART_ID));
- that->art_num->last_art = NULL;
- that->art_num->next_art = NULL;
- that->art_num->id = g;
- that->art_num->art_off = atol(buf);
-
- }
- }
-
- fclose(tmp_file);
-
- } else {
- gotoxy(1,PAGE_SIZE);
- fprintf(stderr, "\nsnews: can't open index file %s\n", fnx);
- exit(1);
- }
-
- gp->threads = ct_art + 1;
-
- return(start);
- }
-
-
-
- /*------------------------ clean up subject line ----------------------------*/
- void eat_gunk(char *buf)
- {
- /*
- * This routine take the header line, and strips the
- * word header word, 'Re:', and any extra blanks
- */
-
- char *p, tmp[MAXLINE];
-
- /* strip the Subject: etc off the front */
- while ( /*(strstr(buf, "Re:") != NULL) ||*/
- (strnicmp(buf, "From:", 5) == 0) ||
- (strnicmp(buf, "Path:", 5) == 0) ||
- (strnicmp(buf, "Subject:", 8) == 0) ||
- (strnicmp(buf, "Organization:", 13) == 0) ||
- (strnicmp(buf, "Organisation:", 13) == 0) ||
- (strnicmp(buf, "Followup-To:", 12) == 0)) {
- p = strchr(buf, ':');
- strcpy(buf, p+1);
- }
-
- strcpy(tmp, buf);
- *buf = '\x00';
- p = strtok(tmp, " \n\r");
-
- while (p != NULL) {
-
- if (stricmp(p, "Re:") != 0) {
- strcat(buf, p);
- strcat(buf, " ");
- }
- p = strtok(NULL, " \n\r");
- }
-
- if (strlen(buf) <= 1)
- strcpy(buf, "** no subject **");
- }
-
-
-
-
- /*-------------------- release the subject structures ---------------------*/
- void free_header(ARTICLE *start)
- {
- /*
- * Work our way through the subject structure releasing all the
- * memory
- */
-
- ARTICLE *a, *t;
- ART_ID *u, *v;
-
- a = start;
-
- while (a != NULL) {
-
- u = a->art_num;
- while (u != NULL) {
- v = u->next_art;
- free(u);
- u = v;
- }
-
- t = a->next;
- free(a->header);
- free(a);
- a = t;
- }
- }
-
-
- /*------------------- count unread articles in a thread --------------------*/
- int count_unread_in_thread(ACTIVE *gp, ARTICLE *a)
- {
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- */
-
- ART_ID *id;
- int ct, idx;
-
- ct = 0;
- id = a->art_num;
-
- while (id != NULL) {
- idx = (int)(id->id - gp->lo_num - 1);
- if (*((gp->read_list)+idx) == FALSE)
- ct++;
- id = id->next_art;
- }
-
- return(ct);
- }
-
-
-
- /*------------------- count unread articles in a group --------------------*/
- int count_unread_in_group(ACTIVE *gp)
- {
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- */
-
- int articles, ct, i;
-
- ct = 0;
- articles = (int)(gp->hi_num - gp->lo_num);
-
- if (gp->read_list)
- for (i = 0; i < articles; i++) {
- if (*((gp->read_list)+i) == FALSE)
- ct++;
- }
-
- return(ct);
- }
-
-
-
- /*------------------- mark articles as read ---------------------------------*/
- void mark_group_as_read(ACTIVE *gp)
- {
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- */
-
- int articles, i, ch;
-
- message("Mark all articles in this group as read (y/n)? ");
- while (((ch = getch()) != 'y') && (ch != 'n') && (ch != ESCAPE));
-
- if (ch == 'y') {
-
- articles = (int)(gp->hi_num - gp->lo_num);
-
- for (i = 0; i < articles; i++)
- *((gp->read_list)+i) = TRUE;
-
- }
-
- }
-
-
- void mark_thread_as_read(ACTIVE *gp, ARTICLE *a)
- {
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- */
-
- ART_ID *id;
- int idx, ch;
-
- message("Mark all articles in this thread as read (y/n)? ");
- while (((ch = getch()) != 'y') && (ch != 'n') && (ch != ESCAPE));
-
- if (ch == 'y') {
-
- id = a->art_num;
-
- while (id != NULL) {
- idx = (int)(id->id - gp->lo_num - 1);
- *((gp->read_list)+idx) = TRUE;
- id = id->next_art;
- }
- }
-
- }
-
-
- /*-------------------------- status message ---------------------------------*/
- void message(char *msg)
- {
- int x;
-
- x = _columns / 2 - (strlen(msg)/2);
- gotoxy(1,PAGE_SIZE);
- textbackground(LIGHTGRAY); textcolor(BLACK);
- printf("%*s%s", x, "", msg);
- clreol();
- textbackground(BLACK); textcolor(LIGHTGRAY);
- }
-
-
- /*-------------------------- status message ---------------------------------*/
- void lmessage(char *msg)
- {
- gotoxy(1,PAGE_SIZE);
- textbackground(LIGHTGRAY); textcolor(BLACK);
- printf("%s", msg);
- clreol();
- textbackground(BLACK); textcolor(LIGHTGRAY);
- }
-