home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume30
/
tin
/
part08
/
art.c
next >
Wrap
C/C++ Source or Header
|
1992-05-20
|
23KB
|
1,088 lines
/*
* Project : tin - a threaded Netnews reader
* Module : art.c
* Author : I.Lea & R.Skrenta
* Created : 01-04-91
* Updated : 12-05-92
* Notes :
* Copyright : (c) Copyright 1991-92 by Iain Lea & Rich Skrenta
* You may freely copy or redistribute this software,
* so long as there is no profit made from its use, sale
* trade or reproduction. You may not change this copy-
* right notice, and it must be included in any copy made
*/
#include "tin.h"
char index_file[PATH_LEN];
char *glob_art_group;
static long last_read_article;
/*
* Construct the pointers to the basenotes of each thread
* arts[] contains every article in the group. inthread is
* set on each article that is after the first article in the
* thread. Articles which have been expired have their thread
* set to -2 (ART_EXPIRED).
*/
void find_base (only_unread)
int only_unread;
{
register int i;
register int j;
top_base = 0;
debug_print_arts ();
if (only_unread) {
for (i = 0; i < top; i++) {
if (IGNORE_ART(i) || arts[i].inthread != FALSE) {
continue;
}
if (top_base >= max_art) {
expand_art ();
}
if (arts[i].unread == ART_UNREAD) {
base[top_base++] = i;
} else {
for (j = i ; j >= 0 ; j = arts[j].thread) {
if (arts[j].unread) {
base[top_base++] = i;
break;
}
}
}
}
} else {
for (i = 0; i < top; i++) {
if (IGNORE_ART(i) || arts[i].inthread != FALSE) {
continue;
}
if (top_base >= max_art) {
expand_art ();
}
base[top_base++] = i;
}
}
}
/*
* Count the number of non-expired articles in arts[]
*/
int num_of_arts ()
{
int sum = 0;
register int i;
for (i = 0; i < top; i++) {
if (arts[i].thread != ART_EXPIRED) {
sum++;
}
}
return sum;
}
/*
* Do we have an entry for article art?
*/
int valid_artnum (art)
long art;
{
register int i;
for (i = 0; i < top; i++)
if (arts[i].artnum == art)
return i;
return -1;
}
/*
* Return TRUE if arts[] contains any expired articles
* (articles we have an entry for which don't have a corresponding
* article file in the spool directory)
*/
int purge_needed ()
{
register int i;
for (i = 0; i < top; i++)
if (arts[i].thread == ART_EXPIRED)
return TRUE;
return FALSE;
}
/*
* Main group indexing routine. Group should be the name of the
* newsgroup, i.e. "comp.unix.amiga". group_path should be the
* same but with the .'s turned into /'s: "comp/unix/amiga"
*
* Will read any existing index, create or incrementally update
* the index by looking at the articles in the spool directory,
* and attempt to write a new index if necessary.
*/
void index_group (group, group_path)
char *group;
char *group_path;
{
int killed = FALSE;
int modified = FALSE;
glob_art_group = group;
set_signals_art ();
if (! update) {
sprintf (msg, txt_group, group);
wait_message (msg);
}
hash_reclaim ();
free_art_array ();
/*
* load articles from index file if it exists
*/
read_index_file (group);
/*
* add any articles to arts[] that are new or were killed
*/
modified = read_group (group, group_path);
if (modified || purge_needed ()) {
write_index_file (group);
}
read_newsrc_line (group);
killed = kill_any_articles (group); /* do after read_newsrc_line() */
make_threads (FALSE);
find_base (show_only_unread);
if ((modified || killed) && ! update) {
clear_message ();
}
}
/*
* Index a group. Assumes any existing index has already been
* loaded.
*/
int read_group (group, group_path)
char *group;
char *group_path;
{
FILE *fp;
int count = 0;
int modified = FALSE;
int respnum;
long art;
register int i;
setup_base (group, group_path); /* load article numbers into base[] */
for (i = 0; i < top_base; i++) { /* for each article # */
art = base[i];
/*
* Do we already have this article in our index? Change thread from
* (ART_EXPIRED) to (ART_NORMAL) if so and skip the header eating.
*/
if ((respnum = valid_artnum (art)) >= 0 || art <= last_read_article) {
if (respnum >= 0) {
arts[respnum].thread = ART_NORMAL;
arts[respnum].unread = ART_UNREAD;
}
continue;
}
if (! modified) {
modified = TRUE; /* we've modified the index */
/* it will need to be re-written */
}
if ((fp = open_header_fp (group_path, art)) == (FILE *) 0) {
continue;
}
/*
* Add article to arts[]
*/
if (top >= max_art)
expand_art();
arts[top].artnum = art;
arts[top].thread = ART_NORMAL;
set_article (&arts[top]);
if (! parse_headers (fp, &arts[top])) {
debug_nntp ("read_group", "FAILED parse_header()");
continue;
}
fclose (fp);
last_read_article = arts[top].artnum; /* used if arts are killed */
top++;
if (++count % MODULO_COUNT_NUM == 0 && ! update) {
#ifndef SLOW_SCREEN_UPDATE
sprintf (msg, txt_indexing_num, group, count);
#else
sprintf (msg, txt_indexing, group);
#endif
wait_message (msg);
}
}
return modified;
}
/*
* Go through the articles in arts[] and use .thread to snake threads
* through them. Use the subject line to construct threads. The
* first article in a thread should have .inthread set to FALSE, the
* rest TRUE. Only do unexprired articles we haven't visited yet
* (arts[].thread == -1 ART_NORMAL).
*/
void make_threads (rethread)
int rethread;
{
extern int cur_groupnum;
register int i;
register int j;
if (!cmd_line) {
if (thread_arts) {
wait_message (txt_threading_arts);
} else {
wait_message (txt_unthreading_arts);
}
}
/*
* .thread & .inthread need to be reset if re-threading arts[]
*/
if (rethread && active[my_group[cur_groupnum]].attribute.thread) {
for (i=0 ; i < top ; i++) {
arts[i].thread = ART_NORMAL;
arts[i].inthread = FALSE;
}
}
switch (sort_art_type) {
case SORT_BY_NOTHING: /* don't sort at all */
qsort ((char *) arts, top, sizeof (struct article_t), artnum_comp);
break;
case SORT_BY_SUBJ_DESCEND:
case SORT_BY_SUBJ_ASCEND:
qsort ((char *) arts, top, sizeof (struct article_t), subj_comp);
break;
case SORT_BY_FROM_DESCEND:
case SORT_BY_FROM_ASCEND:
qsort ((char *) arts, top, sizeof (struct article_t), from_comp);
break;
case SORT_BY_DATE_DESCEND:
case SORT_BY_DATE_ASCEND:
qsort ((char *) arts, top, sizeof (struct article_t), date_comp);
break;
default:
break;
}
if (thread_arts == 0 || active[my_group[cur_groupnum]].attribute.thread == 0) {
return;
}
for (i = 0; i < top; i++) {
if (arts[i].thread != ART_NORMAL || IGNORE_ART(i)) {
continue;
}
for (j = i+1; j < top; j++) {
if (! IGNORE_ART(j) &&
((arts[i].subject == arts[j].subject) ||
((arts[i].part || arts[i].patch) &&
arts[i].archive == arts[j].archive))) {
arts[i].thread = j;
arts[j].inthread = TRUE;
break;
}
}
}
}
int parse_headers (fp, h)
FILE *fp;
struct article_t *h;
{
char buf[HEADER_LEN];
char buf2[HEADER_LEN];
char art_from_addr[LEN];
char art_full_name[LEN];
char *ptr, *ptrline, *s;
int n = 0, len = 0, lineno = 0;
int flag;
int got_subject = FALSE;
int got_from = FALSE;
int got_date = FALSE;
int got_archive = FALSE;
extern int errno;
buf[HEADER_LEN-1] = '\0';
while (fread (buf, sizeof (buf)-1, 1, fp) != 1 && errno == EINTR)
; /* spin on signal interrupts */
if ((n = strlen (buf)) == 0) {
return FALSE;
}
buf[n-1] = '\0';
ptr = buf;
while (1) {
for (ptrline = ptr; *ptr && *ptr != '\n'; ptr++) {
if (((*ptr) & 0xFF) < ' ') {
*ptr = ' ';
}
}
flag = *ptr;
*ptr++ = '\0';
lineno++;
if (! got_from && match_header (ptrline, "From", buf2, HEADER_LEN)) {
parse_from (buf2, art_from_addr, art_full_name);
h->from = hash_str (art_from_addr);
h->name = hash_str (art_full_name);
got_from = TRUE;
} else if (! got_subject && match_header (ptrline, "Subject", buf2, HEADER_LEN)) {
s = eat_re (buf2);
h->subject = hash_str (eat_re (s));
got_subject = TRUE;
} else if (! got_date && match_header (ptrline, "Date", buf2, HEADER_LEN)) {
parse_date (buf2, h->date);
got_date = TRUE;
} else if (match_header (ptrline, "Archive-name", buf2, HEADER_LEN) ||
match_header (ptrline, "Archive-Name", buf2, HEADER_LEN)) {
if ((s = (char *) strchr (buf2, '/')) != NULL) {
if (strncmp (s+1, "part", 4) == 0 ||
strncmp (s+1, "Part", 4) == 0) {
h->part = str_dup (s+5);
len = (int) strlen (h->part);
if (h->part[len-1] == '\n') {
h->part[len-1] = '\0';
}
} else {
if (strncmp (s+1,"patch",5) == 0 ||
strncmp (s+1,"Patch",5) == 0) {
h->patch = str_dup (s+6);
len = (int) strlen (h->patch);
if (h->patch[len-1] == '\n') {
h->patch[len-1] = '\0';
}
}
}
if (h->part || h->patch) {
s = buf2;
while (*s && *s != '/')
s++;
*s = '\0';
s = buf2;
h->archive = hash_str (s);
got_archive = TRUE;
}
}
}
if (! flag || lineno > 25 || got_archive) {
if (got_subject && got_from && got_date) {
debug_print_header (h);
return TRUE;
} else {
return FALSE;
}
}
}
/* NOTREACHED */
}
/*
* Write out an index file. Write the group name first so if
* local indexing is done so we can disambiguate between group
* name hash collisions by looking at the index file.
*/
void write_index_file (group)
char *group;
{
char nam[LEN];
FILE *fp;
int *iptr;
int realnum;
register int i;
set_tin_uid_gid();
sprintf (nam, "%s.%d", index_file, process_id);
if ((fp = fopen (nam, "w")) == NULL) {
perror_message (txt_cannot_open, nam);
return;
}
/*
* dump group header info.
*/
if (sort_art_type != SORT_BY_NOTHING) {
qsort ((char *) arts, top, sizeof (struct article_t), artnum_comp);
}
fprintf (fp, "%s\n", group);
fprintf (fp, "%d\n", num_of_arts ());
if (top <= 0) {
fprintf (fp, "0\n");
} else {
if (last_read_article > arts[top-1].artnum) {
fprintf (fp, "%ld\n", last_read_article);
} else {
fprintf (fp, "%ld\n", arts[top-1].artnum);
}
}
/*
* dump articles
*/
realnum = 0;
for (i = 0; i < top; i++) {
if (arts[i].thread == ART_EXPIRED) {
continue;
}
#ifdef DEBUG
debug_print_header (&arts[i]);
#endif
fprintf(fp, "%ld\n", arts[i].artnum);
iptr = (int *) arts[i].subject;
iptr--;
if (! arts[i].subject) {
fprintf(fp, " \n");
} else if (*iptr < 0 || *iptr > top) {
fprintf(fp, " %s\n", arts[i].subject);
*iptr = realnum;
} else if (*iptr == i) {
fprintf(fp, " %s\n", arts[i].subject);
} else {
fprintf(fp, "%%%d\n", *iptr);
}
iptr = (int *) arts[i].from;
iptr--;
if (! arts[i].from) {
fprintf (fp, " \n");
} else if (*iptr < 0 || *iptr > top) {
fprintf (fp, " %s\n", arts[i].from);
*iptr = realnum;
} else if (*iptr == i) {
fprintf(fp, " %s\n", arts[i].from);
} else {
fprintf(fp, "%%%d\n", *iptr);
}
iptr = (int *) arts[i].name;
iptr--;
if (! arts[i].name) {
fprintf (fp, " \n");
} else if (*iptr < 0 || *iptr > top) {
fprintf (fp, " %s\n", arts[i].name);
*iptr = realnum;
} else if (*iptr == i) {
fprintf(fp, " %s\n", arts[i].name);
} else {
fprintf(fp, "%%%d\n", *iptr);
}
fprintf (fp, "%s\n", arts[i].date);
iptr = (int *) arts[i].archive;
iptr--;
if (! arts[i].archive) {
fprintf (fp, "\n");
} else if (*iptr < 0 || *iptr > top) {
fprintf (fp, " %s\n", arts[i].archive);
*iptr = realnum;
} else if (arts[i].part || arts[i].patch) {
if (*iptr == i) {
fprintf(fp, " %s\n", arts[i].archive);
} else {
fprintf (fp, "%%%d\n", *iptr);
}
} else {
fprintf (fp, "\n");
}
if (! arts[i].part) {
fprintf (fp, " \n");
} else {
fprintf (fp, "%s\n", arts[i].part);
}
if (! arts[i].patch) {
fprintf (fp, " \n");
} else {
fprintf (fp, "%s\n", arts[i].patch);
}
realnum++;
}
fclose (fp);
rename_file (nam, index_file);
chmod (index_file, 0644);
set_real_uid_gid();
if (debug == 2) {
sprintf (msg, "cp %s INDEX", index_file);
system (msg);
}
}
/*
* Read in an index file.
*
* index file header
* 1. newsgroup name (ie. alt.sources)
* 2. number of articles (ie. 26)
* 3. number of last read article (ie. 210)
* 4. Is this a complete/killed index file (ie. COMPLETE/KILLED)
*
* index file record
* 1. article number (ie. 183) [mandatory]
* 2. Subject: line (ie. Which newsreader?) [mandatory]
* 3. From: line (addr) (ie. iain@norisc) [mandatory]
* 4. From: line (name) (ie. Iain Lea) [mandatory]
* 5. Date: of posting (ie. 911231125959) [mandatory]
* 6. Archive: name (ie. compiler) [optional]
* 7. Part number of Archive: name (ie. 01) [optional]
* 8. Patch number of Archive: name (ie. 01) [optional]
*/
int read_index_file (group_name)
char *group_name;
{
int error = 0;
int i, n;
char buf[LEN], *p;
FILE *fp = NULL;
top = 0;
last_read_article = 0L;
if ((fp = open_index_fp (group_name)) == NULL) {
return FALSE;
}
/*
* load header - discard group name, num. of arts in index file after any arts were killed
*/
if (fgets(buf, sizeof buf, fp) == NULL ||
fgets(buf, sizeof buf, fp) == NULL) {
error = 0;
goto corrupt_index;
}
i = atoi (buf);
/*
* num. of last_read_article including any that were killed
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 1;
goto corrupt_index;
}
last_read_article = (long) atol (buf);
/*
* load articles
*/
for (; top < i ; top++) {
if (top >= max_art) {
expand_art ();
}
arts[top].thread = ART_EXPIRED;
set_article (&arts[top]);
/*
* Article no.
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 2;
goto corrupt_index;
}
arts[top].artnum = (long) atol (buf);
/*
* Subject:
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 3;
goto corrupt_index;
}
if (buf[0] == '%') {
n = atoi (&buf[1]);
if (n >= top || n < 0) {
error = 4;
goto corrupt_index;
}
arts[top].subject = arts[n].subject;
} else if (buf[0] == ' ') {
for (p = &buf[1]; *p && *p != '\n'; p++)
continue;
*p = '\0';
arts[top].subject = hash_str (&buf[1]);
} else {
error = 5;
goto corrupt_index;
}
/*
* From: (addr part)
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 6;
goto corrupt_index;
}
if (buf[0] == '%') {
n = atoi (&buf[1]);
if (n >= top || n < 0) {
error = 7;
goto corrupt_index;
}
arts[top].from = arts[n].from;
} else if (buf[0] == ' ') {
for (p = &buf[1]; *p && *p != '\n'; p++)
continue;
*p = '\0';
arts[top].from = hash_str (&buf[1]);
} else {
error = 8;
goto corrupt_index;
}
/*
* From: (full name)
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 9;
goto corrupt_index;
}
if (buf[0] == '%') {
n = atoi (&buf[1]);
if (n > top || n < 0) {
error = 10;
goto corrupt_index;
}
if (n == top) { /* no full name so .name = .from */
arts[top].name = arts[top].from;
} else {
arts[top].name = arts[n].name;
}
} else if (buf[0] == ' ') {
for (p = &buf[1]; *p && *p != '\n'; p++)
continue;
*p = '\0';
arts[top].name = hash_str (&buf[1]);
} else {
error = 11;
goto corrupt_index;
}
/*
* Date:
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 12;
goto corrupt_index;
}
buf[strlen (buf)-1] = '\0';
my_strncpy (arts[top].date, buf, 12);
/*
* Archive-name:
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 13;
goto corrupt_index;
}
if (buf[0] == '\n') {
arts[top].archive = (char *) 0;
} else if (buf[0] == '%') {
n = atoi (&buf[1]);
if (n > top || n < 0) {
error = 14;
goto corrupt_index;
}
arts[top].archive = arts[n].archive;
} else if (buf[0] == ' ') {
for (p = &buf[1]; *p && *p != '\n' ; p++)
continue;
*p = '\0';
arts[top].archive = hash_str (&buf[1]);
} else {
error = 15;
goto corrupt_index;
}
/*
* part no.
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 16;
goto corrupt_index;
}
if (buf[0] != ' ') {
buf[strlen (buf)-1] = '\0';
arts[top].part = str_dup (buf);
}
/*
* patch no.
*/
if (fgets(buf, sizeof buf, fp) == NULL) {
error = 17;
goto corrupt_index;
}
if (buf[0] != ' ') {
buf[strlen (buf)-1] = '\0';
arts[top].patch = str_dup (buf);
}
debug_print_header (&arts[top]);
}
fclose(fp);
return TRUE;
corrupt_index:
if (! update) {
sprintf (msg, txt_corrupt_index, index_file, error, top);
error_message (msg, "");
}
if (debug == 2) {
sprintf (msg, "cp %s INDEX.BAD", index_file);
system (msg);
}
last_read_article = 0L;
if (fp) {
fclose(fp);
}
set_tin_uid_gid();
unlink (index_file);
set_real_uid_gid();
top = 0;
return FALSE;
}
/*
* Look in the local $HOME/RCDIR/INDEXDIR (or wherever) directory for the
* index file for the given group. Hashing the group name gets
* a number. See if that #.1 file exists; if so, read first line.
* Group we want? If no, try #.2. Repeat until no such file or
* we find an existing file that matches our group.
*/
void find_index_file (group)
char *group;
{
char *p;
FILE *fp;
int i = 1;
static char buf[LEN];
char dir[PATH_LEN];
unsigned long h;
h = hash_groupname (group);
if (read_news_via_nntp && xindex_supported) {
sprintf (index_file, "/tmp/xindex.%d", process_id);
return;
}
if (local_index) {
my_strncpy (dir, indexdir, sizeof (dir));
} else {
sprintf (dir, "%s/%s", spooldir, INDEXDIR);
}
while (TRUE) {
sprintf (index_file, "%s/%lu.%d", dir, h, i);
if ((fp = fopen (index_file, "r")) == (FILE *) 0) {
return;
}
if (fgets (buf, sizeof (buf), fp) == (char *) 0) {
fclose (fp);
return;
}
fclose (fp);
for (p = buf; *p && *p != '\n'; p++) {
continue;
}
*p = '\0';
if (strcmp (buf, group) == 0) {
return;
}
i++;
}
}
/*
* Run the index file updater only for the groups we've loaded.
*/
void do_update ()
{
int i, j;
char group_path[PATH_LEN];
char *p;
long beg_epoch, end_epoch;
if (verbose) {
time (&beg_epoch);
}
for (i = 0; i < group_top; i++) {
my_strncpy (group_path, active[my_group[i]].name, sizeof (group_path));
for (p = group_path ; *p ; p++) {
if (*p == '.') {
*p = '/';
}
}
if (verbose) {
printf ("%s %s\n", (catchup ? "Catchup" : "Updating"),
active[my_group[i]].name);
fflush (stdout);
}
index_group (active[my_group[i]].name, group_path);
if (catchup) {
for (j = 0; j < top; j++) {
arts[j].unread = ART_READ;
}
update_newsrc (active[my_group[i]].name, my_group[i], FALSE);
}
}
if (verbose) {
time (&end_epoch);
sprintf (msg, "%s %d groups in %ld seconds\n",
(catchup ? "Caughtup" : "Updated"), group_top, end_epoch - beg_epoch);
wait_message (msg);
}
}
/*
* convert date from ctime format to sortable format
* "24 Jul 91 12:59:59", "Mon, 24 Jul 91 12:59:59" and
* "Mon, 24 Jul 1991 12:59:59" are parsed and produce
* output of the form "910724125959"
*/
char *parse_date (date, str)
char *date;
char *str;
{
char buf[4];
int i = 0;
/* Check for extraneous day-of-week at start of date */
while (isalpha(date[i]) || date[i] == ',' || date[i] == ' ') {
i++;
}
if (date[i+1] == ' ') { /* ie. "2 Aug..." instead of "12 Aug... */
str[4] = '0'; /* day */
str[5] = date[i++];
i++;
} else {
str[4] = date[i++]; /* day */
str[5] = date[i++];
i++;
}
buf[0] = date[i++]; /* month in Jan,Feb,.. form */
buf[1] = date[i++];
buf[2] = date[i++];
buf[3] = '\0';
i++;
str[0] = date[i++]; /* year */
str[1] = date[i++];
if (isdigit(date[i])) { /* 19xx format */
str[0] = date[i++];
str[1] = date[i++];
}
i++;
if (strcmp (buf, "Jan") == 0) { /* convert Jan to 01 etc */
str[2] = '0';
str[3] = '1';
} else if (strcmp (buf, "Feb") == 0) {
str[2] = '0';
str[3] = '2';
} else if (strcmp (buf, "Mar") == 0) {
str[2] = '0';
str[3] = '3';
} else if (strcmp (buf, "Apr") == 0) {
str[2] = '0';
str[3] = '4';
} else if (strcmp (buf, "May") == 0) {
str[2] = '0';
str[3] = '5';
} else if (strcmp (buf, "Jun") == 0) {
str[2] = '0';
str[3] = '6';
} else if (strcmp (buf, "Jul") == 0) {
str[2] = '0';
str[3] = '7';
} else if (strcmp (buf, "Aug") == 0) {
str[2] = '0';
str[3] = '8';
} else if (strcmp (buf, "Sep") == 0) {
str[2] = '0';
str[3] = '9';
} else if (strcmp (buf, "Oct") == 0) {
str[2] = '1';
str[3] = '0';
} else if (strcmp (buf, "Nov") == 0) {
str[2] = '1';
str[3] = '1';
} else if (strcmp (buf, "Dec") == 0) {
str[2] = '1';
str[3] = '2';
} else {
str[2] = '0';
str[3] = '0';
}
str[6] = date[i++]; /* hour */
str[7] = date[i++];
i++;
str[8] = date[i++]; /* minutes */
str[9] = date[i++];
i++;
str[10] = date[i++]; /* seconds */
str[11] = date[i++];
str[12] = '\0'; /* terminate string */
return (str);
}
int artnum_comp (p1, p2)
char *p1;
char *p2;
{
struct article_t *s1 = (struct article_t *) p1;
struct article_t *s2 = (struct article_t *) p2;
/* s1->artnum less than s2->artnum */
if (s1->artnum < s2->artnum) {
return -1;
}
/* s1->artnum greater than s2->artnum */
if (s1->artnum > s2->artnum) {
return 1;
}
return 0;
}
int subj_comp (p1, p2)
char *p1;
char *p2;
{
struct article_t *s1 = (struct article_t *) p1;
struct article_t *s2 = (struct article_t *) p2;
/* return result of strcmp (reversed for descending) */
return (sort_art_type == SORT_BY_SUBJ_ASCEND
? my_stricmp (s1->subject, s2->subject)
: my_stricmp (s2->subject, s1->subject));
}
int from_comp (p1, p2)
char *p1;
char *p2;
{
struct article_t *s1 = (struct article_t *) p1;
struct article_t *s2 = (struct article_t *) p2;
/* return result of strcmp (reversed for descending) */
return (sort_art_type == SORT_BY_FROM_ASCEND
? my_stricmp (s1->from, s2->from)
: my_stricmp (s2->from, s1->from));
}
int date_comp (p1, p2)
char *p1;
char *p2;
{
struct article_t *s1 = (struct article_t *) p1;
struct article_t *s2 = (struct article_t *) p2;
/* return result of strcmp (reversed for descending) */
return (sort_art_type == SORT_BY_DATE_ASCEND
? strcmp (s1->date, s2->date)
: strcmp (s2->date, s1->date));
}
void set_article (art)
struct article_t *art;
{
art->subject = (char *) 0;
art->from = (char *) 0;
art->name = (char *) 0;
art->date[0] = '\0';
art->archive = (char *) 0;
art->part = (char *) 0;
art->patch = (char *) 0;
art->unread = ART_UNREAD;
art->inthread = FALSE;
art->killed = FALSE;
art->tagged = FALSE;
art->hot = FALSE;
}