Tass 3.2 newsreader part 2/3
Rich Skrenta
skrenta at blekko.commodore.com
Thu Apr 18 03:01:20 AEST 1991
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# This archive contains:
# mail.c main.c misc.c nntp.h
# nntp_open.c prompt.c tass.h time.c
#
echo x - mail.c
cat >mail.c <<'@EOF'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#define TRUE 1
#define FALSE 0
char *mailbox_name = NULL;
off_t mailbox_size;
/*
* Record size of mailbox so we can detect if new mail has arrived
*/
mail_setup() {
struct stat buf;
extern char *getenv();
if (mailbox_name == NULL)
mailbox_name = getenv("MAIL");
if (mailbox_name == NULL)
mailbox_size = 0;
else {
if (stat(mailbox_name, &buf) >= 0)
mailbox_size = buf.st_size;
else
mailbox_size = 0;
}
}
/*
* Return TRUE if new mail has arrived
*/
mail_check() {
struct stat buf;
if (mailbox_name != NULL
&& stat(mailbox_name, &buf) >= 0
&& mailbox_size < buf.st_size)
return TRUE;
return FALSE;
}
@EOF
chmod 640 mail.c
echo x - main.c
cat >main.c <<'@EOF'
/*
* Tass, a visual Usenet news reader
* (c) Copyright 1990 by Rich Skrenta
*
* Distribution agreement:
*
* 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 copyright notice,
* and it must be included prominently in any copy made.
*/
#include <stdio.h>
#include <signal.h>
#include <termio.h> /* for struct winsize */
#ifdef SCO_UNIX
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/ptem.h>
#endif
#include "tass.h"
int LINES, COLS;
int max_active;
struct group_ent *active; /* active file */
int group_hash[TABLE_SIZE]; /* group name --> active[] */
int *my_group; /* .newsrc --> active[] */
int *unread; /* highest art read in group */
int num_active; /* one past top of active */
int local_top; /* one past top of my_group */
int update = FALSE; /* update index files only mode */
struct header *arts;
long *base;
int max_art;
int top = 0;
int top_base;
int tass_uid;
int tass_gid;
int real_uid;
int real_gid;
int local_index; /* do private indexing? */
char *cvers = "Tass 3.2 (c) Copyright 1991 by Rich Skrenta. All rights reserved";
#ifdef SIGTSTP
void
main_susp(i)
int i;
{
Raw(FALSE);
putchar('\n');
signal(SIGTSTP, SIG_DFL);
kill(0, SIGTSTP);
signal(SIGTSTP, main_susp);
mail_setup();
Raw(TRUE);
}
#endif
main(argc, argv)
int argc;
char **argv;
{
extern int optind, opterr;
extern char *optarg;
int errflag = 0;
int i;
int c;
extern char group_search_string[];
extern char author_search_string[];
extern char subject_search_string[];
extern char *is_remote();
group_search_string[0] = '\0';
author_search_string[0] = '\0';
subject_search_string[0] = '\0';
hash_init();
for (i = 0; i < TABLE_SIZE; i++)
group_hash[i] = -1;
signal(SIGPIPE, SIG_IGN);
#ifdef SIGTSTP
signal(SIGTSTP, main_susp);
#endif
tass_uid = geteuid();
tass_gid = getegid();
real_uid = getuid();
real_gid = getgid();
init_selfinfo(); /* set up char *'s: homedir, newsrc, etc. */
init_alloc(); /* allocate initial array sizes */
if (tass_uid == real_uid) { /* run out of someone's account */
local_index = TRUE; /* index in their home directory */
mkdir(indexdir, 0755);
} else /* we're setuid, index in /usr/spool/news */
local_index = FALSE;
while ((c = getopt(argc, argv, "f:u")) != -1) {
switch(c) {
case 'f':
strcpy(newsrc, optarg);
break;
case 'u':
update = TRUE;
break;
case '?':
default:
errflag++;
}
}
if (errflag) {
fprintf(stderr, "usage: tass [options] [newsgroups]\n");
fprintf(stderr, " -f file use file instead of $HOME/.newsrc\n");
fprintf(stderr, " -u update index files only\n");
exit(1);
}
if (!update)
printf("Tass 3.2%s\n", is_remote());
nntp_startup(); /* connect to server if we're using nntp */
read_active(); /* load the active file into active[] */
if (optind < argc) {
while (optind < argc) {
if (add_group(argv[optind], TRUE) < 0)
fprintf(stderr,
"group %s not found in active file\n",
argv[optind]);
optind++;
}
} else
read_newsrc(TRUE);
if (update) { /* index file updater only */
do_update();
exit(0);
}
if (InitScreen() == FALSE) {
fprintf(stderr,"Screen initialization failed\n");
exit(1);
}
ScreenSize(&LINES, &COLS);
Raw(TRUE);
#ifdef TIOCGWINSZ
{
struct winsize win;
if (ioctl(0, TIOCGWINSZ, &win) == 0) {
if (win.ws_row != 0)
LINES = win.ws_row - 1;
if (win.ws_col != 0)
COLS = win.ws_col;
}
}
#endif
mail_setup(); /* record mailbox size for "you have mail" */
selection_index();
tass_done(0);
}
tass_done(ret)
int ret;
{
nntp_finish(); /* close connection if we're using nntp */
MoveCursor(LINES, 0);
printf("\r\n");
Raw(FALSE);
exit(ret);
}
/*
* Dynamic table management
* These settings are memory conservative: small initial allocations
* and a 50% expansion on table overflow. A fast vm system with
* much memory might want to start with higher initial allocations
* and a 100% expansion on overflow, especially for the arts[] array.
*/
init_alloc() {
max_active = 100; /* initial alloc */
active = (struct group_ent *) my_malloc(sizeof(*active) * max_active);
my_group = (int *) my_malloc(sizeof(int) * max_active);
unread = (int *) my_malloc(sizeof(int) * max_active);
max_art = 300; /* initial alloc */
arts = (struct header *) my_malloc(sizeof(*arts) * max_art);
base = (long *) my_malloc(sizeof(long) * max_art);
}
expand_art() {
max_art += max_art / 2; /* increase by 50% */
arts = (struct header *) my_realloc(arts, sizeof(*arts) * max_art);
base = (long *) my_realloc(base, sizeof(long) * max_art);
}
expand_active() {
max_active += max_active / 2; /* increase by 50% */
active = (struct group_ent *) my_realloc(active,
sizeof(*active) * max_active);
my_group = (int *) my_realloc(my_group, sizeof(int) * max_active);
unread = (int *) my_realloc(unread, sizeof(int) * max_active);
}
/*
* Load the active file into active[]
*/
read_active()
{
FILE *fp;
char *p, *q;
char buf[200];
long h;
int i;
extern long hash_groupname();
FILE *open_active_fp();
num_active = 0;
fp = open_active_fp();
if (fp == NULL) {
fprintf(stderr, "can't get active file\n");
exit(1);
}
while (fgets(buf, 200, fp) != NULL) {
for (p = buf; *p && *p != ' '; p++) ;
if (*p != ' ') {
fprintf(stderr, "active file corrupt\n");
continue;
}
*p++ = '\0';
if (num_active >= max_active)
expand_active();
h = hash_groupname(buf);
if (group_hash[h] == -1)
group_hash[h] = num_active;
else { /* hash linked list chaining */
for (i = group_hash[h]; active[i].next >= 0;
i = active[i].next) {
if (strcmp(active[i].name, buf) == 0)
goto read_active_continue;
/* kill dups */
}
if (strcmp(active[i].name, buf) == 0)
goto read_active_continue;
active[i].next = num_active;
}
for (q = p; *q && *q != ' '; q++) ;
if (*q != ' ') {
fprintf(stderr, "active file corrupt\n");
continue;
}
active[num_active].name = str_save(buf);
active[num_active].max = atol(p);
active[num_active].min = atol(q);
active[num_active].next = -1; /* hash chaining */
active[num_active].flag = NOTGOT; /* not in my_group[] yet */
num_active++;
read_active_continue:;
}
fclose(fp);
}
/*
* Read $HOME/.newsrc into my_group[]. my_group[] ints point to
* active[] entries. Sub_only determines whether we just read
* subscribed groups or all of them.
*/
read_newsrc(sub_only)
int sub_only; /* TRUE=subscribed groups only, FALSE=all groups */
{
FILE *fp;
char *p;
char buf[8192];
char c;
int i;
local_top = 0;
fp = fopen(newsrc, "r");
if (fp == NULL) { /* attempt to make a .newsrc */
for (i = 0; i < num_active; i++) {
if (local_top >= max_active)
expand_active();
my_group[local_top] = i;
active[i].flag = 0;
unread[local_top] = -1;
local_top++;
}
write_newsrc();
return;
}
while (fgets(buf, 8192, fp) != NULL) {
p = buf;
while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
p++;
c = *p;
*p++ = '\0';
if (c == '!' && sub_only)
continue; /* unsubscribed */
if ((i = add_group(buf, FALSE)) < 0) {
fprintf(stderr, "group %s not found in active file\n", buf);
continue;
}
if (c != '!') /* if we're subscribed to it */
active[my_group[i]].flag |= SUBS;
unread[i] = parse_unread(p, my_group[i]);
}
fclose(fp);
}
/*
* Write a new newsrc from my_group[] and active[]
* Used to a create a new .newsrc if there isn't one already, or when
* the newsrc is reset.
*/
write_newsrc() {
FILE *fp;
int i;
setuid(real_uid); /* become the user to write in his */
setgid(real_gid); /* home directory */
fp = fopen(newsrc, "w");
if (fp == NULL)
goto write_newsrc_done;
for (i = 0; i < num_active; i++)
fprintf(fp, "%s: \n", active[i].name);
fclose(fp);
write_newsrc_done:
setuid(tass_uid);
setgid(tass_gid);
}
/*
* Load the sequencer rang lists and mark arts[] according to the
* .newsrc info for a particular group. i.e. rec.arts.comics: 1-94,97
*/
read_newsrc_line(group)
char *group;
{
FILE *fp;
char buf[8192];
char *p;
fp = fopen(newsrc, "r");
if (fp == NULL)
return;
while (fgets(buf, 8192, fp) != NULL) {
p = buf;
while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
p++;
*p++ = '\0';
if (strcmp(buf, group) != 0)
continue;
parse_seq(p);
break;
}
fclose(fp);
}
/*
* For our current group, update the sequencer information in .newsrc
*/
update_newsrc(group, groupnum)
char *group;
int groupnum; /* index into active[] for this group */
{
FILE *fp;
FILE *newfp;
char buf[8192];
char *p;
char c;
int gotit = FALSE;
setuid(real_uid);
setgid(real_gid);
fp = fopen(newsrc, "r");
newfp = fopen(newnewsrc, "w");
if (newfp == NULL)
goto update_done;
if (fp != NULL) {
while (fgets(buf, 8192, fp) != NULL) {
for (p = buf; *p; p++)
if (*p == '\n') {
*p = '\0';
break;
}
p = buf;
while (*p && *p != ' ' && *p != ':' && *p != '!')
p++;
c = *p;
if (c != '\0')
*p++ = '\0';
if (c != '!')
c = ':';
if (strcmp(buf, group) == 0) {
fprintf(newfp, "%s%c ", buf, c);
gotit = TRUE;
print_seq(newfp, groupnum);
fprintf(newfp, "\n");
} else
fprintf(newfp, "%s%c%s\n", buf, c, p);
}
fclose(fp);
}
fclose(newfp);
unlink(newsrc);
link(newnewsrc, newsrc);
unlink(newnewsrc);
update_done:
setuid(tass_uid);
setgid(tass_gid);
}
/*
* Subscribe/unsubscribe to a group in .newsrc. ch should either be
* '!' to unsubscribe or ':' to subscribe. num is the group's index
* in active[].
*/
subscribe(group, ch, num, out_seq)
char *group;
char ch;
int num;
int out_seq; /* output sequencer info? */
{
FILE *fp;
FILE *newfp;
char buf[8192];
char *p;
char c;
int gotit = FALSE;
if (ch == '!')
active[num].flag &= ~SUBS;
else
active[num].flag |= SUBS;
setuid(real_uid);
setgid(real_gid);
fp = fopen(newsrc, "r");
newfp = fopen(newnewsrc, "w");
if (newfp == NULL)
goto subscribe_done;
if (fp != NULL) {
while (fgets(buf, 8192, fp) != NULL) {
for (p = buf; *p; p++)
if (*p == '\n') {
*p = '\0';
break;
}
p = buf;
while (*p && *p != ' ' && *p != ':' && *p != '!')
p++;
c = *p;
if (c != '\0')
*p++ = '\0';
if (c != '!')
c = ':';
if (strcmp(buf, group) == 0) {
fprintf(newfp, "%s%c%s\n", buf, ch, p);
gotit = TRUE;
} else
fprintf(newfp, "%s%c%s\n", buf, c, p);
}
fclose(fp);
}
if (!gotit) {
if (out_seq) {
fprintf(newfp, "%s%c ", group, ch);
print_seq(newfp, num);
fprintf(newfp, "\n");
} else
fprintf(newfp, "%s%c\n", group, ch);
}
fclose(newfp);
unlink(newsrc);
link(newnewsrc, newsrc);
unlink(newnewsrc);
subscribe_done:
setuid(tass_uid);
setgid(tass_gid);
}
print_seq(fp, groupnum)
FILE *fp;
int groupnum; /* index into active[] for this group */
{
int i;
int flag = FALSE;
if (top <= 0) {
if (active[groupnum].min > 1) {
fprintf(fp, "1-%ld", active[groupnum].min);
fflush(fp);
}
return;
}
i = 0;
if (arts[0].artnum > 1) {
for (; i < top && !arts[i].unread; i++) ;
if (i > 0)
fprintf(fp, "1-%ld", arts[i-1].artnum);
else
fprintf(fp, "1-%ld", arts[0].artnum - 1);
flag = TRUE;
}
for (; i < top; i++) {
if (!arts[i].unread) {
if (flag)
fprintf(fp, ",");
else
flag = TRUE;
fprintf(fp, "%ld", arts[i].artnum);
if (i+1 < top && !arts[i+1].unread) {
while (i+1 < top && !arts[i+1].unread)
i++;
fprintf(fp, "-%ld", arts[i].artnum);
}
}
}
if (!flag && active[groupnum].min > 1)
fprintf(fp, "1-%ld", active[groupnum].min);
fflush(fp);
}
parse_seq(s)
char *s;
{
long low, high;
int i;
while (*s) {
while (*s && (*s < '0' || *s > '9'))
s++;
if (*s && *s >= '0' && *s <= '9') {
low = atol(s);
while (*s && *s >= '0' && *s <= '9')
s++;
if (*s == '-') {
s++;
high = atol(s);
while (*s && *s >= '0' && *s <= '9')
s++;
} else
high = low;
for (i = 0; i < top; i++)
if (arts[i].artnum >= low &&
arts[i].artnum <= high)
arts[i].unread = 0;
}
}
}
parse_unread(s, groupnum)
char *s;
int groupnum; /* index for group in active[] */
{
long low, high;
long last_high;
int i;
int sum = 0;
int gotone = FALSE;
int n;
/*
* Read the first range from the .newsrc sequencer information. If the
* top of the first range is higher than what the active file claims is
* the bottom, use it as the new bottom instead
*/
high = 0;
if (*s) {
while (*s && (*s < '0' || *s > '9'))
s++;
if (*s && *s >= '0' && *s <= '9') {
low = atol(s);
while (*s && *s >= '0' && *s <= '9')
s++;
if (*s == '-') {
s++;
high = atol(s);
while (*s && *s >= '0' && *s <= '9')
s++;
} else
high = low;
gotone = TRUE;
}
}
if (high < active[groupnum].min)
high = active[groupnum].min;
while (*s) {
last_high = high;
while (*s && (*s < '0' || *s > '9'))
s++;
if (*s && *s >= '0' && *s <= '9') {
low = atol(s);
while (*s && *s >= '0' && *s <= '9')
s++;
if (*s == '-') {
s++;
high = atol(s);
while (*s && *s >= '0' && *s <= '9')
s++;
} else
high = low;
if (low > last_high) /* otherwise seq out of order */
sum += (low - last_high) - 1;
}
}
if (gotone) {
if (active[groupnum].max > high)
sum += active[groupnum].max - high;
return sum;
}
n = (int) (active[groupnum].max - active[groupnum].min);
if (n < 2)
return 0;
return -1;
}
get_line_unread(group, groupnum)
char *group;
int groupnum; /* index for group in active[] */
{
FILE *fp;
char buf[8192];
char *p;
int ret = -1;
fp = fopen(newsrc, "r");
if (fp == NULL)
return -1;
while (fgets(buf, 8192, fp) != NULL) {
p = buf;
while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
p++;
*p++ = '\0';
if (strcmp(buf, group) != 0)
continue;
ret = parse_unread(p, groupnum);
break;
}
fclose(fp);
return ret;
}
reset_newsrc()
{
FILE *fp;
FILE *newfp;
char buf[8192];
char *p;
char c;
int gotit = FALSE;
int i;
setuid(real_uid);
setgid(real_gid);
fp = fopen(newsrc, "r");
newfp = fopen(newnewsrc, "w");
if (newfp == NULL)
goto update_done;
if (fp != NULL) {
while (fgets(buf, 8192, fp) != NULL) {
for (p = buf; *p && *p != '\n'; p++) ;
*p = '\0';
p = buf;
while (*p && *p != ' ' && *p != ':' && *p != '!')
p++;
c = *p;
if (c != '\0')
*p++ = '\0';
if (c != '!')
c = ':';
fprintf(newfp, "%s%c\n", buf, c);
}
fclose(fp);
}
fclose(newfp);
unlink(newsrc);
link(newnewsrc, newsrc);
unlink(newnewsrc);
update_done:
setuid(tass_uid);
setgid(tass_gid);
for (i = 0; i < local_top; i++)
unread[i] = -1;
}
delete_group(group)
char *group;
{
FILE *fp;
FILE *newfp;
char buf[8192];
char *p;
char c;
int gotit = FALSE;
FILE *del;
setuid(real_uid);
setgid(real_gid);
fp = fopen(newsrc, "r");
newfp = fopen(newnewsrc, "w");
if (newfp == NULL)
goto del_done;
del = fopen(delgroups, "a+");
if (del == NULL)
goto del_done;
if (fp != NULL) {
while (fgets(buf, 8192, fp) != NULL) {
for (p = buf; *p && *p != '\n'; p++) ;
*p = '\0';
p = buf;
while (*p && *p != ' ' && *p != ':' && *p != '!')
p++;
c = *p;
if (c != '\0')
*p++ = '\0';
if (c != '!')
c = ':';
if (strcmp(buf, group) == 0) {
fprintf(del, "%s%c%s\n", buf, c, p);
gotit = TRUE;
} else
fprintf(newfp, "%s%c%s\n", buf, c, p);
}
fclose(fp);
}
fclose(newfp);
if (!gotit)
fprintf(del, "%s! \n", group);
fclose(del);
unlink(newsrc);
link(newnewsrc, newsrc);
unlink(newnewsrc);
del_done:
setuid(tass_uid);
setgid(tass_gid);
}
undel_group() {
FILE *del;
FILE *newfp;
FILE *fp;
char buf[2][8192];
char *p;
int which = 0;
long h;
extern int cur_groupnum;
int i, j;
char c;
setuid(real_uid);
setgid(real_gid);
del = fopen(delgroups, "r");
if (del == NULL) {
setuid(tass_uid);
setgid(tass_gid);
return FALSE;
}
unlink(delgroups);
newfp = fopen(delgroups, "w");
if (newfp == NULL) {
setuid(tass_uid);
setgid(tass_gid);
return FALSE;
}
buf[0][0] = '\0';
buf[1][0] = '\0';
while (fgets(buf[which], 8192, del) != NULL) {
which = !which;
if (*buf[which])
fputs(buf[which], newfp);
}
fclose(del);
fclose(newfp);
which = !which;
if (!*buf[which]) {
setuid(tass_uid);
setgid(tass_gid);
return FALSE;
}
for (p = buf[which]; *p && *p != '\n'; p++) ;
*p = '\0';
p = buf[which];
while (*p && *p != ' ' && *p != ':' && *p != '!')
p++;
c = *p;
if (c != '\0')
*p++ = '\0';
if (c != '!')
c = ':';
{ /* find the hash of the group name */
char *t = buf[which];
h = *t++;
while (*t)
h = (h * 64 + *t++) % TABLE_SIZE;
}
for (i = group_hash[h]; i >= 0; i = active[i].next) {
if (strcmp(buf[which], active[i].name) == 0) {
for (j = 0; j < local_top; j++)
if (my_group[j] == i) {
setuid(tass_uid);
setgid(tass_gid);
return j;
}
active[i].flag &= ~NOTGOT; /* mark that we got it */
if (c != '!')
active[i].flag |= SUBS;
if (local_top >= max_active)
expand_active();
local_top++;
for (j = local_top; j > cur_groupnum; j--) {
my_group[j] = my_group[j-1];
unread[j] = unread[j-1];
}
my_group[cur_groupnum] = i;
unread[cur_groupnum] = parse_unread(p, i);
fp = fopen(newsrc, "r");
if (fp == NULL) {
setuid(tass_uid);
setgid(tass_gid);
return FALSE;
}
newfp = fopen(newnewsrc, "w");
if (newfp == NULL) {
fclose(fp);
setuid(tass_uid);
setgid(tass_gid);
return FALSE;
}
i = 0;
while (fgets(buf[!which], 8192, fp) != NULL) {
for (p = buf[!which]; *p && *p != '\n'; p++) ;
*p = '\0';
p = buf[!which];
while (*p && *p!=' ' && *p != ':' && *p != '!')
p++;
c = *p;
if (c != '\0')
*p++ = '\0';
if (c != '!')
c = ':';
while (i < cur_groupnum) {
if (strcmp(buf[!which],
active[my_group[i]].name) == 0) {
fprintf(newfp, "%s%c%s\n",
buf[!which], c, p);
goto foo_cont;
}
i++;
}
fprintf(newfp, "%s%c%s\n", buf[which], c, p);
fprintf(newfp, "%s%c%s\n", buf[!which], c, p);
break;
foo_cont:;
}
while (fgets(buf[!which], 8192, fp) != NULL)
fputs(buf[!which], newfp);
fclose(newfp);
fclose(fp);
unlink(newsrc);
link(newnewsrc, newsrc);
unlink(newnewsrc);
setuid(tass_uid);
setgid(tass_gid);
return TRUE;
}
}
setuid(tass_uid);
setgid(tass_gid);
return FALSE;
}
mark_group_read(group, groupnum)
char *group;
int groupnum; /* index into active[] for this group */
{
FILE *fp;
FILE *newfp;
char buf[8192];
char *p;
char c;
int gotit = FALSE;
if (active[groupnum].max < 2)
return;
setuid(real_uid);
setgid(real_gid);
fp = fopen(newsrc, "r");
newfp = fopen(newnewsrc, "w");
if (newfp == NULL)
goto mark_group_read_done;
if (fp != NULL) {
while (fgets(buf, 8192, fp) != NULL) {
for (p = buf; *p; p++)
if (*p == '\n') {
*p = '\0';
break;
}
p = buf;
while (*p && *p != ' ' && *p != ':' && *p != '!')
p++;
c = *p;
if (c != '\0')
*p++ = '\0';
if (c != '!')
c = ':';
if (strcmp(buf, group) == 0) {
fprintf(newfp, "%s%c 1-%ld\n", buf, c,
active[groupnum].max);
gotit = TRUE;
} else
fprintf(newfp, "%s%c%s\n", buf, c, p);
}
fclose(fp);
}
fclose(newfp);
unlink(newsrc);
link(newnewsrc, newsrc);
unlink(newnewsrc);
mark_group_read_done:
setuid(tass_uid);
setgid(tass_gid);
}
long
hash_groupname(buf) /* hash group name for fast lookup later */
char *buf;
{
char *t = buf;
unsigned long h;
h = *t++;
while (*t)
h = ((h << 1) ^ *t++) % TABLE_SIZE;
/* h = (h * 64 + *t++) % TABLE_SIZE; */
return h;
}
#ifdef M_XENIX
mkdir(path, mode)
char *path;
int mode;
{
char buf[200];
sprintf(buf, "mkdir %s", path);
system(buf);
chmod(path, mode);
}
#endif
@EOF
chmod 644 main.c
echo x - misc.c
cat >misc.c <<'@EOF'
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "tass.h"
char active_file[LEN];
char homedir[LEN];
char userid[LEN];
char delgroups[LEN];
char newsrc[LEN];
char newnewsrc[LEN];
char indexdir[LEN];
char my_org[LEN]; /* organization */
char sig[LEN];
char signature[LEN];
/*
* Which base note (an index into base[]) does a respnum
* (an index into arts[]) corresponsd to?
*
* In other words, base[] points to an entry in arts[] which is
* the head of a thread, linked with arts[].thread. For any q: arts[q],
* find i such that base[i]->arts[n]->arts[o]->...->arts[q]
*/
which_base(n)
int n;
{
int i, j;
for (i = 0; i < top_base; i++)
for (j = base[i]; j >= 0; j = arts[j].thread)
if (j == n)
return i;
fprintf(stderr, "can't find base article\n");
return 0;
}
/*
* Find how deep in a thread a response is. Start counting at zero
*/
which_resp(n)
int n;
{
int i, j;
int num = 0;
i = which_base(n);
for (j = base[i]; j != -1; j = arts[j].thread)
if (j == n)
break;
else
num++;
return num;
}
/*
* Given an index into base[], find the number of responses for
* that basenote
*/
nresp(n)
int n;
{
int i;
int oldi = -3;
int sum = 0;
assert(n < top_base);
for (i = base[n]; i != -1; i = arts[i].thread) {
assert(i != -2);
assert(i != oldi);
oldi = i;
sum++;
}
return sum - 1;
}
asfail(file, line, cond)
char *file;
int line;
char *cond;
{
fprintf(stderr, "tass: assertion failure: %s (%d): %s\n",
file, line, cond);
exit(1);
}
/*
* init_selfinfo
* Deterimines users home directory, userid, and a path
* for an rc file in the home directory
*/
init_selfinfo()
{
struct passwd *myentry;
extern struct passwd *getpwuid();
struct stat sb;
char nam[LEN];
char *p;
extern char *getenv();
FILE *fp;
myentry = getpwuid(getuid());
strcpy(userid, myentry->pw_name);
strcpy(homedir, myentry->pw_dir);
sprintf(signature, "%s/.signature", homedir);
sprintf(sig, "%s/.Sig", homedir);
sprintf(newsrc, "%s/.newsrc", homedir);
sprintf(newnewsrc, "%s/.newnewsrc", homedir);
sprintf(delgroups, "%s/.delgroups", homedir);
sprintf(indexdir, "%s/.tindx", homedir);
sprintf(active_file, "%s/active", LIBDIR);
if (stat(active_file, &sb) >= 0)
goto got_active;
/*
* I hate forgetting to define LIBDIR correctly. Guess a
* couple of likely places if it's not where LIBDIR says it is.
*/
strcpy(active_file, "/usr/lib/news/active");
if (stat(active_file, &sb) >= 0)
goto got_active;
strcpy(active_file, "/usr/local/lib/news/active");
if (stat(active_file, &sb) >= 0)
goto got_active;
strcpy(active_file, "/usr/public/lib/news/active");
if (stat(active_file, &sb) >= 0)
goto got_active;
/*
* Oh well. Revert to what LIBDIR says it is to produce a
* useful error message when read_active() fails later.
*/
sprintf(active_file, "%s/active", LIBDIR);
got_active:
*my_org = '\0';
p = getenv("ORGANIZATION");
if (p != NULL) {
strcpy(my_org, p);
goto got_org;
}
sprintf(nam, "%s/organization", LIBDIR);
fp = fopen(nam, "r");
if (fp == NULL) {
sprintf(nam, "/usr/lib/news/organization");
fp = fopen(nam, "r");
}
if (fp == NULL) {
sprintf(nam, "/usr/local/lib/news/organization");
fp = fopen(nam, "r");
}
if (fp == NULL) {
sprintf(nam, "/usr/public/lib/news/organization");
fp = fopen(nam, "r");
}
if (fp == NULL) {
sprintf(nam, "/etc/organization");
fp = fopen(nam, "r");
}
if (fp != NULL) {
if (fgets(my_org, LEN, fp) != NULL) {
for (p = my_org; *p && *p != '\n'; p++) ;
*p = '\0';
}
fclose(fp);
}
got_org:;
}
char *
my_malloc(size)
unsigned size;
{
char *p;
extern char *malloc();
p = malloc(size);
if (p == NULL) {
fprintf(stderr, "tass: out of memory\n");
exit(1);
}
return p;
}
char *
my_realloc(p, size)
char *p;
unsigned size;
{
extern char *malloc();
extern char *realloc();
if (p == NULL)
p = malloc(size);
else
p = realloc(p, size);
if (p == NULL) {
fprintf(stderr, "tass: out of memory\n");
exit(1);
}
return p;
}
char *
str_save(s)
char *s;
{
char *p;
assert(s != NULL);
p = my_malloc(strlen(s) + 1);
strcpy(p, s);
return(p);
}
copy_fp(a, b, prefix)
FILE *a;
FILE *b;
char *prefix;
{
char buf[8192];
while (fgets(buf, 8192, a) != NULL)
fprintf(b, "%s%s", prefix, buf);
}
char *
get_val(env, def)
char *env; /* Environment variable we're looking for */
char *def; /* Default value if no environ value found */
{
extern char *getenv();
char *ptr;
if ((ptr = getenv(env)) != NULL)
return(ptr);
else
return(def);
}
invoke_editor(nam)
char *nam;
{
char buf[200];
static int first = TRUE;
static char editor[200];
int ret;
if (first) {
strcpy(editor, get_val("EDITOR", DEF_EDITOR));
first = FALSE;
}
sprintf(buf, "%s %s", editor, nam);
printf("\r%s\n", buf);
ret = invoke_cmd(buf);
setuid(real_uid);
setgid(real_gid);
return ret;
}
invoke_cmd(nam)
char *nam;
{
int ret;
#ifdef SIGTSTP
void (*susp)();
#endif
Raw(FALSE);
setuid(real_uid);
setgid(real_gid);
#ifdef SIGTSTP
susp = signal(SIGTSTP, SIG_DFL);
#endif
ret = system(nam);
#ifdef SIGTSTP
signal(SIGTSTP, susp);
#endif
setuid(tass_uid);
setgid(tass_gid);
Raw(TRUE);
return ret == 0;
}
shell_escape() {
char shell[LEN];
char *p;
#ifdef SIGTSTP
void (*susp)();
#endif
if (!parse_string("!", shell))
strcpy(shell, get_val("SHELL", "/bin/sh"));
for (p = shell; *p && (*p == ' ' || *p == '\t'); p++) ;
if (!*p)
strcpy(shell, get_val("SHELL", "/bin/sh"));
Raw(FALSE);
setuid(real_uid);
setgid(real_gid);
fputs("\r\n", stdout);
#ifdef SIGTSTP
susp = signal(SIGTSTP, SIG_DFL);
#endif
system(p);
#ifdef SIGTSTP
signal(SIGTSTP, susp);
#endif
setuid(tass_uid);
setgid(tass_gid);
Raw(TRUE);
continue_prompt();
mail_setup();
}
/*
* Find the previous response. Go to the last response in the previous
* thread if we go past the beginning of this thread.
*/
prev_response(n)
int n;
{
int resp;
int i;
resp = which_resp(n);
if (resp > 0)
return choose_resp( which_base(n), resp-1 );
i = which_base(n) - 1;
if (i < 0)
return -1;
return choose_resp( i, nresp(i) );
}
/*
* Find the next response. Go to the next basenote if there
* are no more responses in this thread
*/
next_response(n)
int n;
{
int i;
if (arts[n].thread >= 0)
return arts[n].thread;
i = which_base(n) + 1;
if (i >= top_base)
return -1;
return base[i];
}
/*
* Given a respnum (index into arts[]), find the respnum of the
* next basenote
*/
next_basenote(n)
int n;
{
int i;
i = which_base(n) + 1;
if (i >= top_base)
return -1;
return base[i];
}
/*
* Find the next unread response in this group
*/
next_unread(n)
int n;
{
while (n >= 0) {
if (arts[n].unread == 1)
return n;
n = next_response(n);
}
return -1;
}
/*
* Find the previous unread response in this thread
*/
prev_unread(n)
int n;
{
while (n >= 0) {
if (arts[n].unread == 1)
return n;
n = prev_response(n);
}
return -1;
}
add_signature(fp, flag)
FILE *fp;
int flag;
{
FILE *sigf;
sigf = fopen(signature, "r");
if (sigf != NULL) {
if (flag) {
fprintf(fp, "\n--\n");
copy_fp(sigf, fp, "");
}
fclose(sigf);
return;
}
sigf = fopen(sig, "r");
if (sigf != NULL) {
fprintf(fp, "\n--\n");
copy_fp(sigf, fp, "");
fclose(sigf);
}
}
make_lower(s, t)
char *s;
char *t;
{
while (*s) {
if (isupper(*s))
*t = tolower(*s);
else
*t = *s;
s++;
t++;
}
*t = 0;
}
match(s, t, n)
char *s;
char *t;
int n;
{
while (*t) {
if (*s == *t && strncmp(s, t, n) == 0)
return TRUE;
t++;
}
return FALSE;
}
@EOF
chmod 644 misc.c
echo x - nntp.h
cat >nntp.h <<'@EOF'
/* nntp.h -- nntp support for tass */
/* Changed a bit so nntp knows about Tass */
/*
* This file is originally from the nntp 1.5 source,
* but modified a bit
*/
#define NNTP_SERVER "/etc/nntpserver"
/*
* External routine declarations
*/
extern char *getserverbyfile();
extern int server_init();
extern int get_tcp_socket();
extern int handle_server_response();
extern void put_server();
extern int get_server();
extern void close_server();
/*
* External file descriptors for the server connection
*/
extern FILE *ser_wr_fp;
extern FILE *ser_wr_fp;
/*
* Response codes for NNTP server
*
* @(#)nntp.h 1.7 (Berkeley) 1/11/88
*
* First digit:
*
* 1xx Informative message
* 2xx Command ok
* 3xx Command ok so far, continue
* 4xx Command was correct, but couldn't be performed
* for some specified reason.
* 5xx Command unimplemented, incorrect, or a
* program error has occured.
*
* Second digit:
*
* x0x Connection, setup, miscellaneous
* x1x Newsgroup selection
* x2x Article selection
* x3x Distribution
* x4x Posting
*/
#define CHAR_INF '1'
#define CHAR_OK '2'
#define CHAR_CONT '3'
#define CHAR_ERR '4'
#define CHAR_FATAL '5'
#define INF_HELP 100 /* Help text on way */
#define INF_DEBUG 199 /* Debug output */
#define OK_CANPOST 200 /* Hello; you can post */
#define OK_NOPOST 201 /* Hello; you can't post */
#define OK_SLAVE 202 /* Slave status noted */
#define OK_GOODBYE 205 /* Closing connection */
#define OK_GROUP 211 /* Group selected */
#define OK_GROUPS 215 /* Newsgroups follow */
#define OK_TASSINDEX 218 /* Tass index follows */
#define OK_ARTICLE 220 /* Article (head & body) follows */
#define OK_HEAD 221 /* Head follows */
#define OK_BODY 222 /* Body follows */
#define OK_NOTEXT 223 /* No text sent -- stat, next, last */
#define OK_NEWNEWS 230 /* New articles by message-id follow */
#define OK_NEWGROUPS 231 /* New newsgroups follow */
#define OK_XFERED 235 /* Article transferred successfully */
#define OK_POSTED 240 /* Article posted successfully */
#define CONT_XFER 335 /* Continue to send article */
#define CONT_POST 340 /* Continue to post article */
#define ERR_GOODBYE 400 /* Have to hang up for some reason */
#define ERR_NOGROUP 411 /* No such newsgroup */
#define ERR_NCING 412 /* Not currently in newsgroup */
#define ERR_NOTASS 418 /* No tass index for this group */
#define ERR_NOCRNT 420 /* No current article selected */
#define ERR_NONEXT 421 /* No next article in this group */
#define ERR_NOPREV 422 /* No previous article in this group */
#define ERR_NOARTIG 423 /* No such article in this group */
#define ERR_NOART 430 /* No such article at all */
#define ERR_GOTIT 435 /* Already got that article, don't send */
#define ERR_XFERFAIL 436 /* Transfer failed */
#define ERR_XFERRJCT 437 /* Article rejected, don't resend */
#define ERR_NOPOST 440 /* Posting not allowed */
#define ERR_POSTFAIL 441 /* Posting failed */
#define ERR_COMMAND 500 /* Command not recognized */
#define ERR_CMDSYN 501 /* Command syntax error */
#define ERR_ACCESS 502 /* Access to server denied */
#define ERR_FAULT 503 /* Program fault, command not performed */
/* RFC 977 defines this; don't change it. */
#define NNTP_STRLEN 512
@EOF
chmod 644 nntp.h
echo x - nntp_open.c
cat >nntp_open.c <<'@EOF'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "nntp.h"
#include "tass.h"
char *
is_remote() {
return " (remote)";
}
nntp_startup() {
char *server_name;
int ret;
extern char *getenv();
server_name = getserverbyfile(NNTP_SERVER);
if (server_name == NULL) {
fprintf(stderr, "Can't get nntp server name\n");
fprintf(stderr, "Either put the name in the file %s, or put\n",
NNTP_SERVER);
fprintf(stderr, "it in the environment variable NNTPSERVER\n");
exit(1);
}
ret = server_init(server_name);
switch (ret) {
case OK_CANPOST:
case OK_NOPOST:
break;
case -1:
fprintf(stderr, "failed to connect to server\n");
exit(1);
default:
fprintf(stderr, "rejected by server, nntp error %d\n", ret);
exit(1);
}
}
nntp_finish() {
close_server();
}
/*
* get_respcode
* get a response code from the server and return it to the caller
*/
int get_respcode() {
char line[NNTP_STRLEN];
if (get_server(line, NNTP_STRLEN) == -1) {
fprintf(stderr, "connection to server broken\n");
tass_done(1);
}
return atoi(line);
}
stuff_nntp(fnam)
char *fnam;
{
FILE *fp;
char line[NNTP_STRLEN];
extern char *mktemp();
struct stat sb;
extern long note_size;
strcpy(fnam, "/tmp/tass_nntpXXXXXX");
mktemp(fnam);
fp = fopen(fnam, "w");
if (fp == NULL) {
fprintf(stderr, "stuff_nntp: can't open %s: ", fnam);
perror("");
return FALSE;
}
while (1) {
if (get_server(line, NNTP_STRLEN) == -1) {
fprintf(stderr, "connection to server broken\n");
tass_done(1);
}
if (strcmp(line, ".") == 0)
break; /* end of text */
strcat(line, "\n");
if (line[0] == '.') /* reduce leading .'s */
fputs(&line[1], fp);
else
fputs(line, fp);
}
fclose(fp);
if (stat(fnam, &sb) < 0)
note_size = 0;
else
note_size = sb.st_size;
return TRUE;
}
FILE *
nntp_to_fp() {
char fnam[LEN];
FILE *fp;
if (!stuff_nntp(fnam))
return NULL;
fp = fopen(fnam, "r");
if (fp == NULL) {
fprintf(stderr, "nntp_to_fp: can't reopen %s: ", fnam);
perror("");
return NULL;
}
unlink(fnam);
return fp;
}
nntp_to_fd() {
char fnam[LEN];
int fd;
if (!stuff_nntp(fnam))
return NULL;
fd = open(fnam, 0);
if (fd == NULL) {
fprintf(stderr, "nntp_to_fd: can't reopen %s: ", fnam);
perror("");
return -1;
}
unlink(fnam);
return fd;
}
FILE *
open_active_fp() {
put_server("list");
if (get_respcode() != OK_GROUPS)
return NULL;
return nntp_to_fp();
}
FILE *
open_art_fp(group_path, art)
char *group_path;
long art;
{
char buf[LEN];
sprintf(buf, "article %ld", art);
put_server(buf);
if (get_respcode() != OK_ARTICLE)
return NULL;
return nntp_to_fp();
}
open_header_fd(group_path, art)
char *group_path;
long art;
{
char buf[LEN];
sprintf(buf, "head %ld", art);
put_server(buf);
if (get_respcode() != OK_HEAD)
return -1;
return nntp_to_fd();
}
setup_base(group, group_path)
char *group;
char *group_path;
{
char buf[LEN];
char line[NNTP_STRLEN];
long start, last, dummy, count;
top_base = 0;
sprintf(buf, "group %s", group);
put_server(buf);
if (get_server(line, NNTP_STRLEN) == -1) {
fprintf(stderr, "connection to server broken\n");
tass_done(1);
}
if (atoi(line) != OK_GROUP)
return;
sscanf(line,"%ld %ld %ld %ld", &dummy, &count, &start, &last);
if (last - count > start)
start = last - count;
while (start <= last) {
if (top_base >= max_art)
expand_art();
base[top_base++] = start++;
}
}
@EOF
chmod 644 nntp_open.c
echo x - prompt.c
cat >prompt.c <<'@EOF'
#include <stdio.h>
#include "tass.h"
/*
* parse_num
* get a number from the user
* Return -1 if missing or bad number typed
*/
parse_num(ch, prompt)
char ch;
char *prompt;
{
char buf[40];
int len;
int i;
int num;
MoveCursor(LINES,0);
printf("%s %c",prompt,ch);
fflush(stdout);
buf[0] = ch;
buf[1] = '\0';
len = 1;
ch = ReadCh();
while (ch != '\n'&& ch != '\r') {
if (ch >= '0' && ch <= '9' && len < 4) {
buf[len++] = ch;
buf[len] = '\0';
putchar(ch);
} else if (ch == 8 || ch == 127) {
if (len) {
len--;
buf[len] = '\0';
putchar('\b');
putchar(' ');
putchar('\b');
} else {
MoveCursor(LINES, 0);
CleartoEOLN();
return(-1);
}
} else if (ch == 21) { /* control-U */
for (i = len;i>0;i--) {
putchar('\b');
putchar(' ');
putchar('\b');
}
buf[0] = '\0';
len = 0;
} else
putchar(7);
fflush(stdout);
ch = ReadCh();
}
MoveCursor(LINES, 0);
CleartoEOLN();
if (len) {
num = atoi(buf);
return(num);
} else
return(-1);
}
/*
* parse_string
* get a string from the user
* Return TRUE if a valid string was typed, FALSE otherwise
*/
parse_string(prompt, buf)
char *prompt;
char *buf;
{
int len;
int i;
char ch;
clear_message();
MoveCursor(LINES,0);
printf("%s", prompt);
fflush(stdout);
buf[0] = '\0';
len = 0;
ch = ReadCh();
while (ch != '\n' && ch != '\r') {
if (ch >= ' ' && len < 60) {
buf[len++] = ch;
buf[len] = '\0';
putchar(ch);
} else if (ch == 8 || ch == 127) {
if (len) {
len--;
buf[len] = '\0';
putchar('\b');
putchar(' ');
putchar('\b');
} else {
MoveCursor(LINES, 0);
CleartoEOLN();
return(FALSE);
}
} else if (ch == 21) { /* control-U */
for (i = len;i>0;i--) {
putchar('\b');
putchar(' ');
putchar('\b');
}
buf[0] = '\0';
len = 0;
} else
putchar(7);
fflush(stdout);
ch = ReadCh();
}
MoveCursor(LINES,0);
CleartoEOLN();
return TRUE;
}
prompt_yn(prompt)
char *prompt;
{
char ch;
clear_message();
MoveCursor(LINES,0);
printf("%s", prompt);
fflush(stdout);
ch = ReadCh();
clear_message();
if (ch == 'y' || ch == 'Y')
return TRUE;
return FALSE;
}
continue_prompt() {
printf("-Hit return to continue-");
fflush(stdout);
ReadCh();
}
@EOF
chmod 644 prompt.c
echo x - tass.h
cat >tass.h <<'@EOF'
#define LIBDIR "/usr/lib/news"
#define SPOOLDIR "/usr/spool/news"
#define MAILER "/bin/rmail"
#define DEF_EDITOR "/usr/bin/vi"
#define TRUE 1
#define FALSE 0
#define LEN 200
#define INDEX_TOP 4
#define NOTESLINES (LINES - INDEX_TOP - 2)
#define RIGHT_POS (COLS - 16)
#define MORE_POS (COLS - 20)
#define MAX_FROM 25
#define MAX_SUBJ 38
#define TABLE_SIZE 1409 /* should be prime */
/* #define MAX_SUBJ (COLS - 42) */
struct header {
long artnum;
char *subject;
char *from;
int thread;
int inthread;
int unread; /* has this article been read? */
/* 0 = read, 1 = unread, 2 = will return */
};
/*
* header.artnum:
* article number in spool directory for group
*
* header.thread:
* initially -1
* points to another arts[] (struct header): zero and up
* -2 means article has expired (wasn't found in file search
* of spool directory for the group)
*
* header.inthread:
* FALSE for the first article in a thread, TRUE for all
* following articles in thread
*
* header.read:
* boolean, has this article been read or not
*/
struct group_ent {
char *name;
long max;
long min;
int next; /* next active entry in hash chain */
int flag;
};
#define NOTGOT 0x01 /* haven't put in my_group yet */
#define SUBS 0x02 /* subscribed to */
extern int top;
extern struct header *arts;
extern long *base;
extern int max_art;
extern char sig[LEN];
extern char signature[LEN];
extern char userid[LEN];
extern char homedir[LEN];
extern char indexdir[LEN];
extern char my_org[LEN];
extern char active_file[LEN];
extern char newsrc[LEN];
extern char newnewsrc[LEN];
extern char delgroups[LEN];
extern int top_base;
extern int LINES, COLS;
extern char *str_save();
extern char *my_malloc();
extern char *my_realloc();
extern int group_hash[TABLE_SIZE];
extern int num_active;
extern struct group_ent *active;
extern int *my_group;
extern int *unread;
extern int max_active;
extern int local_top;
extern char *eat_re();
extern char *nice_time();
extern int update;
extern int inverse_okay;
extern int tass_uid;
extern int tass_gid;
extern int real_uid;
extern int real_gid;
extern int local_index;
extern char *strcpy();
extern char *strncat();
extern char *strncpy();
extern long atol();
#define ctrl(c) ((c) & 0x1F)
/*
* Assertion verifier
*/
#ifdef __STDC__
#define assert(p) if(! (p)) asfail(__FILE__, __LINE__, #p); else
#else
#define assert(p) if(! (p)) asfail(__FILE__, __LINE__, "p"); else
#endif
#define TASS_HEADER "Tass 3.2"
@EOF
chmod 644 tass.h
echo x - time.c
cat >time.c <<'@EOF'
#include <sys/types.h>
#include <time.h>
nicedate(timestr, newstr)
char *timestr, *newstr;
{
int i;
for (i = 0; i <= 7; i++)
*newstr++ = timestr[i];
if (timestr[8] != ' ')
*newstr++ = timestr[8];
*newstr++ = timestr[9];
*newstr++ = ',';
*newstr++ = ' ';
for (i = 20;i <= 23; i++)
*newstr++ = timestr[i];
*newstr++ = '\0';
}
nicetime(timestr, newstr)
char *timestr, *newstr;
{
int hours;
char dayornite[3];
if (timestr[11] == ' ')
hours = timestr[12] - '0';
else
hours = (timestr[11]-'0')*10 + (timestr[12]-'0');
if (hours < 12)
strcpy(dayornite, "am");
else
strcpy(dayornite, "pm");
if (hours >= 13)
hours -= 12;
if (!hours)
hours = 12;
sprintf(newstr, "%d:%c%c%s", hours, timestr[14],
timestr[15], dayornite);
}
char *nice_time() {
char *timestr;
char the_date[17];
char the_time[8];
extern char *ctime();
long time_now;
static char buf[25];
time(&time_now);
timestr = ctime(&time_now);
nicedate(timestr, the_date);
nicetime(timestr, the_time);
sprintf(buf,"%s %s", the_date, the_time);
return(buf);
}
@EOF
chmod 644 time.c
exit 0
More information about the Alt.sources
mailing list