home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
TELECOM
/
tass.lzh
/
art.c
next >
Wrap
Text File
|
1993-01-24
|
24KB
|
979 lines
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <errno.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
#ifdef OSK
# include <ctype.h>
# include <modes.h>
# include <dir.h>
# define DIR_BUF struct direct
# define D_LENGTH d_namlen
# undef SPOOLDIR
# define SPOOLDIR spooldir
#endif /* OSK */
#ifndef DIR_BUF
# include <sys/types.h>
# include <dirent.h>
# define DIR_BUF struct dirent
# define D_LENGTH d_reclen
#endif
extern char *index ();
extern char *realloc ();
char index_file[LEN+1];
char *glob_art_group;
static int complete_count;
static int complete_expire;
#ifdef SIGTSTP
void
art_susp(i)
int i;
{
Raw(FALSE);
putchar('\n');
signal(SIGTSTP, SIG_DFL);
kill(0, SIGTSTP);
signal(SIGTSTP, art_susp);
Raw(TRUE);
mail_setup();
ClearScreen();
MoveCursor(LINES, 0);
printf("Group %s... ", glob_art_group);
fflush(stdout);
}
#endif
/*
* Convert a string to a long, only look at first n characters
*/
my_atol(s, n)
char *s;
int n;
{
long ret = 0;
while (*s && n--) {
if (*s >= '0' && *s <= '9')
ret = ret * 10 + (*s - '0');
else
return -1;
s++;
}
return ret;
}
# ifdef LOCK_INDEX
/*
* Lock the index when modifieng it (only on global indicies)
*/
static char *
lock_filename (fle)
char *fle;
{
#ifndef OSK
static int siz = -1;
static char *fname = NULL;
int len;
if ((len = strlen (fle)) > siz) {
siz = len;
if (! (fname = realloc (fname, siz + 16)))
return (NULL);
}
sprintf (fname, "%s_lock", fle);
return (fname);
#else
static char fname[64];
unsigned long n;
char *ptr;
for (ptr = fle, n = 0; *ptr; n += *ptr++ & 0xff)
;
sprintf (fname, "/dd/TMP/%u_lock", n);
return (fname);
#endif
}
# ifdef OSK
static void
stop_signals ()
{
sigmask (1);
}
static void
start_signals ()
{
sigmask (-1);
}
# else /* OSK */
typedef int SIGTYPE;
static SIGTYPE (*signals)()[NSIG];
static void
stop_signals ()
{
int t;
for (t = 0; t < NSIG; ++t)
signals[t] = signal (t, SIG_IGN);
}
static void
start_signals ()
{
int t;
for (t = 0; t < NSIG; ++t)
signal (t, signals[t]);
}
# endif /* OSK */
static int lock_fd = -1;
static int
lock_index (fle)
char *fle;
{
int tout;
char *fn;
if (lock_fd != -1)
close (lock_fd);
if (! (fn = lock_filename (fle)))
return (-1);
for (tout = 20; tout > 0; --tout) {
# ifndef OSK
if ((lock_fd = creat (fn, 0444)) == -1)
# else /* OSK */
unlink (fn);
if (((lock_fd = create (fn, S_IWRITE, 033)) == -1) && (errno == E_CEF))
# endif /* OSK */
sleep (1);
else
break;
}
if (lock_fd != -1)
stop_signals ();
return (lock_fd == -1 ? -1 : 0);
}
static void
unlock_index (fle)
char *fle;
{
char *fn;
if (lock_fd != -1) {
close (lock_fd);
lock_fd = -1;
if (fn = lock_filename (fle)) {
# ifndef OSK
chmod (fn, 0666);
# endif /* OSK */
unlink (fn);
}
start_signals ();
}
}
# endif /* LOCK_INDEX */
/*
* 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.
*/
find_base() {
int i;
top_base = 0;
for (i = 0; i < top; i++)
if (!arts[i].inthread && arts[i].thread != -2) {
if (top_base >= max_art)
expand_art();
base[top_base++] = i;
}
}
/*
* Count the number of non-expired articles in arts[]
*/
num_arts() {
int sum = 0;
int i;
for (i = 0; i < top; i++)
if (arts[i].thread != -2)
sum++;
return sum;
}
/*
* Do we have an entry for article art?
*/
valid_artnum(art)
long art;
{
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)
*/
purge_needed() {
int i;
for (i = 0; i < top; i++)
if (arts[i].thread == -2)
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.
*/
index_group(group, group_path)
char *group;
char *group_path;
{
int modified;
glob_art_group = group;
#ifdef SIGTSTP
signal(SIGTSTP, art_susp);
#endif
if (!update) {
clear_message();
MoveCursor(LINES, 0);
printf("Group %s... ", group);
fflush(stdout);
}
if (local_index)
find_local_index(group);
else
sprintf(index_file, "%s/%s/.tindex", SPOOLDIR, group_path);
#ifdef LOCK_INDEX
if (! local_index)
if (lock_index (index_file) < 0)
return -1;
#endif /* LOCK_INDEX */
load_index();
modified = read_group(group_path);
make_threads();
if (modified || purge_needed()) {
#ifdef USE_UID
if (local_index) { /* writing index in home directory */
setuid(real_uid); /* so become them */
setgid(real_gid);
}
#endif /* USE_UID */
dump_index(group);
#ifdef USE_UID
if (local_index) {
setuid(tass_uid);
setgid(tass_gid);
}
#endif /* USE_UID */
}
#ifdef LOCK_INDEX
if (! local_index)
unlock_index (index_file);
#endif /* LOCK_INDEX */
find_base();
if (modified && !update)
clear_message();
}
/*
* 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.
*/
scan_dir(group)
char *group;
{
DIR *d;
DIR_BUF *e;
long art;
char buf[200];
top_base = 0;
sprintf(buf, "%s/%s", SPOOLDIR, group);
d = opendir(buf);
if (d != NULL) {
while ((e = readdir(d)) != NULL) {
#ifdef OSK
e->D_LENGTH = strlen (e->d_name);
#endif /* OSK */
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);
}
/*
* Index a group. Assumes any existing index has already been
* loaded.
*/
read_group(group)
char *group;
{
char buf[200];
int fd;
long art;
int count;
int modified = FALSE;
int respnum;
int i;
scan_dir(group); /* load article numbers into base[] */
count = 0;
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
* -2 to -1 if so and skip the header eating.
*/
if ((respnum = valid_artnum(art)) >= 0) {
arts[respnum].thread = -1;
arts[respnum].unread = 1;
continue;
}
if (!modified) {
modified = TRUE; /* we've modified the index */
/* it will need to be re-written */
#if 0
if (!update) {
MoveCursor(LINES, 0);
fputs("Indexing... ", stdout);
fflush(stdout);
}
#endif
}
/*
* Add article to arts[]
*/
if (top >= max_art)
expand_art();
arts[top].artnum = art;
arts[top].thread = -1;
arts[top].inthread = FALSE;
arts[top].unread = 1;
sprintf(buf, "%s/%s/%ld", SPOOLDIR, group, art);
#ifndef MNEWS
fd = open(buf, 0);
#else /* MNEWS */
#ifndef OSK
if ((fd = open (buf, O_RDONLY)) != -1) {
#else /* OSK */
if ((fd = open (buf, S_IREAD)) != -1) {
#endif /* OSK */
int n;
n = readln (fd, buf, 180);
lseek (fd, 0, 0);
if (n > 0) {
buf[n - 1] = '\0';
if (!strncmp (buf, "%(#)$ ", 6)) {
char sav[200];
strcpy (sav, buf + 6);
sprintf (buf, "%s/%s", spooldir, sav);
close (fd);
#ifndef OSK
fd = open (buf, O_RDONLY);
#else /* OSK */
fd = open (buf, S_IREAD);
#endif /* OSK */
}
}
}
#endif /* MNEWS */
if (fd < 0) {
fprintf(stderr, "can't open article %s: ", buf);
perror("");
continue;
}
if (!parse_headers(fd, &arts[top]))
continue;
top++;
close(fd);
if (++count % 10 == 0 && !update) {
printf("\b\b\b\b%4d", count);
fflush(stdout);
}
}
complete_count += count;
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).
*/
make_threads() {
int i;
int j;
for (i = 0; i < top; i++) {
if (arts[i].thread == -1)
for (j = i+1; j < top; j++)
if (arts[i].hash == arts[j].hash
&& arts[j].thread != -2
&& strncmp(arts[i].nore, arts[j].nore, 10) == 0) {
arts[i].thread = j;
arts[j].inthread = TRUE;
break;
}
}
}
/*
* Return a pointer into s eliminating any leading Re:'s. Example:
*
* Re: Reorganization of misc.jobs
* ^ ^
*/
char *
eat_re(s)
char *s;
{
#if 1
while (*s == 'r' || *s == 'R') {
if (*(s+1) == 'e' || *(s+1) == 'E') {
if (*(s+2) == ':')
s += 3;
else if ((*(s+2) == '^') && isdigit (*(s+3))) {
s += 4;
if (*s == ':')
++s;
} else
break;
} else
break;
while (isspace (*s))
++s;
}
#else
while (*s == 'R') {
if (strncmp(s, "Re: ", 4) == 0)
s += 4;
else if (strncmp(s, "Re:", 3) == 0)
s += 3;
else if (strncmp(s, "Re^2: ", 6) == 0)
s += 6;
else
break;
}
#endif
return s;
}
/*
* Hash the subjects (after eating the Re's off) for a quicker
* thread search later. We store the hashes for subjects in the
* index file for speed.
*/
long
hash_s(s)
char *s;
{
long h = 0;
while (*s)
h = h + (*s++ & 0xff);
return h;
}
parse_headers(fd, h)
int fd;
struct header *h;
{
char buf[1024];
char *p, *q;
char flag;
char *ptr;
int found_from, found_subj;
int n;
if ((n = read(fd, buf, 1024)) <= 0)
return FALSE;
buf[n > 1023 ? 1023 : n] = '\0';
p = buf;
while (p = index (p, '\n'))
if (*(p + 1) == '\n') {
*p = '\0';
break;
} else
++p;
p = buf;
h->from[0] = '\0';
h->subject[0] = '\0';
h->nore = h->subject;
h->hash = 0;
found_from = FALSE;
found_subj = FALSE;
while (1) {
q = p;
while (*p && *p != '\n') {
if ((unsigned char) *p & 0x7F < 32)
*p = ' ';
p++;
}
flag = *p;
*p++ = '\0';
if ((!found_from) && (strncmp(q, "From: ", 6) == 0)) {
if (ptr = index (&q[6], '('))
++ptr;
else
ptr = &q[6];
strncpy (h->from, ptr, MAX_FROM);
h->from[MAX_FROM-1] = '\0';
if (ptr = index (h->from, ')'))
*ptr = '\0';
} else if ((!found_subj) && (strncmp(q, "Subject: ", 9) == 0)) {
h->hash = hash_s(eat_re(&q[9]));
strncpy(h->subject, &q[9], MAX_SUBJ);
h->subject[MAX_SUBJ-1] = '\0';
h->nore = eat_re(h->subject);
}
if ((!flag) || (found_from && found_subj))
break;
}
return TRUE;
}
/*
* Write out a .tindex file. Write the group name first so if
* local indexing is done we can disambiguate between group name
* hash collisions by looking at the index file.
*/
void
dump_index(group)
char *group;
{
int i;
char buf[200];
FILE *fp;
char *p, *q;
long l;
#ifndef USE_UID
char lockfn[64];
int lockfd;
if (! local_index) {
#ifndef OSK
sprintf (lockfn, "/tmp/%-.10s", group);
lockfd = creat (lockfn, 0, 0444);
#else /* OSK */
sprintf (lockfn, "/dd/TMP/%-.20s", group);
for (p = lockfn; *p; ++p)
if (((unsigned char) *p >= 0x80) || ((!isalnum (*p)) && (*p != '/')))
*p = '_';
lockfd = create (lockfn, 0, 033);
#endif /* OSK */
if (lockfd < 0) {
sleep (1);
return;
}
}
#endif /* USE_UID */
fp = fopen(index_file, "w");
if (fp == NULL)
#ifndef USE_UID
goto dump_index_finish;
#else /* USE_UID */
return;
#endif /* USE_UID */
fprintf(fp, "%s\n", group);
fprintf(fp, "%d\n", num_arts());
for (i = 0; i < top; i++)
if (arts[i].thread != -2) {
p = arts[i].nore;
q = arts[i].subject;
l = p - q;
fprintf(fp, "%ld\n%s\n%s\n%ld\n%ld\n",
arts[i].artnum,
arts[i].subject,
arts[i].from,
arts[i].hash,
#if 0
(long) arts[i].nore - (long) arts[i].subject);
#else
l);
#endif
} else
++complete_expire;
fclose(fp);
#ifdef USE_UID
#ifndef OSK
chmod(index_file, 0644);
#else /* OSK */
chmod(index_file, 013);
#endif /* OSK */
#else /* USE_UID */
dump_index_finish:
if (local_index)
#ifndef OSK
chmod(index_file, 0644);
#else /* OSK */
chmod(index_file, 013);
#endif /* OSK */
else if (lockfd != -1) {
close (lockfd);
#ifndef OSK
chmod (lockfn, 0666);
#endif /* OSK */
unlink (lockfn);
#ifndef OSK
chmod(index_file, 0666);
#else /* OSK */
chmod(index_file, 033);
#endif /* OSK */
}
#endif /* USE_UID */
}
/*
* strncpy that stops at a newline and null terminates
*/
my_strncpy(p, q, n)
char *p;
char *q;
int n;
{
while (n--) {
if (!*q || *q == '\n')
break;
*p++ = *q++;
}
*p = '\0';
}
/*
* Read in a .tindex file.
*/
void
load_index()
{
int i;
long j;
char buf[200];
FILE *fp;
int first = TRUE;
top = 0;
fp = fopen(index_file, "r");
if (fp == NULL)
return;
if (fgets(buf, 200, fp) == NULL
|| fgets(buf, 200, fp) == NULL) {
fprintf(stderr, "one\n");
goto corrupt_index;
}
i = atol(buf);
while (top < i) {
if (top >= max_art)
expand_art();
arts[top].thread = -2;
arts[top].inthread = FALSE;
if (fgets(buf, 200, fp) == NULL) {
fprintf(stderr, "two\n");
goto corrupt_index;
}
arts[top].artnum = atol(buf);
if (fgets(buf, 200, fp) == NULL) {
fprintf(stderr, "three\n");
goto corrupt_index;
}
my_strncpy(arts[top].subject, buf, MAX_SUBJ-1);
if (fgets(buf, 200, fp) == NULL) {
fprintf(stderr, "four\n");
goto corrupt_index;
}
my_strncpy(arts[top].from, buf, MAX_FROM-1);
if (fgets(buf, 200, fp) == NULL) {
fprintf(stderr, "five\n");
goto corrupt_index;
}
arts[top].hash = atol(buf);
if (fgets(buf, 200, fp) == NULL) {
fprintf(stderr, "six\n");
goto corrupt_index;
}
j = atol(buf);
#if 0
if (j < 0 || j > 100) {
#if 0
goto corrupt_index;
#else
arts[top].nore = eat_re(arts[top].subject);
#endif
} else
arts[top].nore = arts[top].subject + j;
#else
arts[top].nore = eat_re(arts[top].subject);
#endif
top++;
}
fclose(fp);
return;
corrupt_index:
fprintf(stderr, "index file %s corrupt\n", index_file);
fprintf(stderr, "top = %d\n", top);
exit(1);
unlink(index_file);
top = 0;
}
/*
* Look in the local $HOME/.tindex (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_local_index(group)
char *group;
{
unsigned long h;
static char buf[200];
int i;
char *p;
FILE *fp;
{
char *t = group;
h = *t++;
while (*t)
h = (h << 1) + (*t++ & 0xff);
}
i = 1;
while (1) {
sprintf(index_file, "%s/%lu.%d", indexdir, h, i);
fp = fopen(index_file, "r");
if (fp == NULL)
return;
if (fgets(buf, 200, fp) == NULL) {
fclose(fp);
return;
}
fclose(fp);
for (p = buf; *p && *p != '\n'; p++) ;
*p = '\0';
if (strcmp(buf, group) == 0)
return;
i++;
}
}
/*
* Run the index file updater only for the groups we've loaded.
*/
do_update() {
int i;
char group_path[200];
char *p;
#ifdef OSK
int len = 0;
#endif /* OSK */
time_t tim;
struct tm *tt;
complete_count = 0;
complete_expire = 0;
for (i = 0; i < local_top; i++) {
strcpy(group_path, active[my_group[i]].name);
for (p = group_path; *p; p++)
#ifndef OSK
if (*p == '.')
*p = '/';
#else /* OSK */
{
if (*p == '.') {
*p = '/';
len = 0;
} else if ((! isalnum (*p)) && (! index ("$_", *p)))
*p = '_';
if (++len > 26)
while (*(p + 1) && (*(p + 1) != '.'))
++p;
}
#endif /* OSK */
index_group(active[my_group[i]].name, group_path);
}
time (&tim);
tt = localtime (&tim);
printf ("[%02d.%02d.%04d %2d:%2d] Collected %d articles",
tt -> tm_mday,
tt -> tm_mon + 1,
tt -> tm_year + 1900,
tt -> tm_hour,
tt -> tm_min,
complete_count);
if (complete_expire)
printf (" (%d articles expired)", complete_expire);
putchar ('\n');
}