Thread View: de.comp.sources.os9
1 messages
1 total messages
Started by Frank Kaefer
Thu, 24 Dec 1992 13:05
Tass for OS-9 Part01/03
Author: Frank Kaefer
Date: Thu, 24 Dec 1992 13:05
Date: Thu, 24 Dec 1992 13:05
2372 lines
51129 bytes
51129 bytes
Submitted-by: Ulrich Dessauer <ud@Nightmare.ddt.sub.org>
Archive-name: tass/part01
: ----- Cut here ----- Cut here ----- Cut here ----- Cut here -----
: Use sh filename to extract shell archive
: This shell archive contains following files:
: 'MANIFEST 660 bytes'
: 'COPYRIGHT 356 bytes'
: 'Makefile 679 bytes'
: 'art.c 16520 bytes'
: 'curses.c 8952 bytes'
: 'group.c 16732 bytes'
:
if test -f 'MANIFEST' ; then
echo 'File MANIFEST already exists, overwriting it'
del 'MANIFEST'
fi
echo Extracting \"'MANIFEST'\"
sed "s/^X//" >'MANIFEST' <<'__END__OF__THIS__FILE__'
XFilename Part Description
X----------------------------------------------------
XMANIFEST 1 This shipping list
XCOPYRIGHT 1
XMakefile 1
Xart.c 1
Xcurses.c 1
Xgroup.c 1
Xmail.c 2
Xmain.c 2
Xmisc.c 2
Xpage.c 2
Xprompt.c 3
Xscreen.c 3
Xselect.c 3
Xsigs.c 3
Xtass.h 3
Xtime.c 3
__END__OF__THIS__FILE__
if test -f 'COPYRIGHT' ; then
echo 'File COPYRIGHT already exists, overwriting it'
del 'COPYRIGHT'
fi
echo Extracting \"'COPYRIGHT'\"
sed "s/^X//" >'COPYRIGHT' <<'__END__OF__THIS__FILE__'
X/*
X * Tass, a visual Usenet news reader
X * (c) Copyright 1990 by Rich Skrenta
X *
X * Distribution agreement:
X *
X * You may freely copy or redistribute this software, so long
X * as there is no profit made from its use, sale, trade or
X * reproduction. You may not change this copyright notice,
X * and it must be included prominently in any copy made.
X */
__END__OF__THIS__FILE__
if test -f 'Makefile' ; then
echo 'File Makefile already exists, overwriting it'
del 'Makefile'
fi
echo Extracting \"'Makefile'\"
sed "s/^X//" >'Makefile' <<'__END__OF__THIS__FILE__'
XG= -g
XCC= cc
XCFLAGS= -qt=/dd/TMP $G -DREGEXP -DAPPEND_SIG -DMNEWS -DINCSTR \
X -DLOCK_INDEX -DRAND_SIG
XLIBS= -l=/dd/LIB/8bit.l -l=/dd/LIB/termlib.l -l=/dd/LIB/os9lib.l
X
X
XOBJECTS = curses.r art.r group.r mail.r main.r misc.r page.r \
X prompt.r screen.r select.r time.r sigs.r
X
X/h0/ETC/CMDS/tass: $(OBJECTS)
X $(CC) $(CFLAGS) -m=20 -f=$@ $(OBJECTS) $(LIBS)
X
Xshar:
X -mv -f ../tass.shar ../tass.shar-
X shar -v [A-Z]* *.[ch] > ../tass.shar
X
X
Xart.r: art.c tass.h
Xcurses.r: curses.c
Xgroup.r: group.c tass.h
Xmail.r: mail.c
Xmain.r: main.c tass.h
Xmisc.r: misc.c tass.h
Xpage.r: page.c tass.h
Xprompt.r: prompt.c tass.h
Xscreen.r: screen.c tass.h
Xselect.r: select.c tass.h
Xtime.r: time.c
__END__OF__THIS__FILE__
if test -f 'art.c' ; then
echo 'File art.c already exists, overwriting it'
del 'art.c'
fi
echo Extracting \"'art.c'\"
sed "s/^X//" >'art.c' <<'__END__OF__THIS__FILE__'
X
X
X#include <stdio.h>
X#include <time.h>
X#include <signal.h>
X#include <errno.h>
X#include "tass.h"
X
X
X/* Hopefully one of these is right for you. */
X
X#ifdef BSD
X# include <sys/types.h>
X# include <sys/dir.h>
X# define DIR_BUF struct direct
X# define D_LENGTH d_namlen
X#endif
X#ifdef M_XENIX
X# include <sys/ndir.h>
X# define DIR_BUF struct direct
X# define D_LENGTH d_namlen
X#endif
X#ifdef OSK
X# include <ctype.h>
X# include <modes.h>
X# include <dir.h>
X# define DIR_BUF struct direct
X# define D_LENGTH d_namlen
X# undef SPOOLDIR
X# define SPOOLDIR spooldir
X#endif /* OSK */
X#ifndef DIR_BUF
X# include <sys/types.h>
X# include <dirent.h>
X# define DIR_BUF struct dirent
X# define D_LENGTH d_reclen
X#endif
X
Xextern char *index ();
Xextern char *realloc ();
X
Xchar index_file[LEN+1];
Xchar *glob_art_group;
X
Xstatic int complete_count;
Xstatic int complete_expire;
X
X#ifdef SIGTSTP
Xvoid
Xart_susp(i)
Xint i;
X{
X
X Raw(FALSE);
X putchar('\n');
X signal(SIGTSTP, SIG_DFL);
X kill(0, SIGTSTP);
X
X signal(SIGTSTP, art_susp);
X Raw(TRUE);
X
X mail_setup();
X ClearScreen();
X MoveCursor(LINES, 0);
X printf("Group %s... ", glob_art_group);
X fflush(stdout);
X}
X#endif
X
X
X/*
X * Convert a string to a long, only look at first n characters
X */
X
Xmy_atol(s, n)
Xchar *s;
Xint n;
X{
X long ret = 0;
X
X while (*s && n--) {
X if (*s >= '0' && *s <= '9')
X ret = ret * 10 + (*s - '0');
X else
X return -1;
X s++;
X }
X
X return ret;
X}
X
X# ifdef LOCK_INDEX
X/*
X * Lock the index when modifieng it (only on global indicies)
X */
Xstatic char *
Xlock_filename (fle)
Xchar *fle;
X{
X#ifndef OSK
X static int siz = -1;
X static char *fname = NULL;
X int len;
X
X if ((len = strlen (fle)) > siz) {
X siz = len;
X if (! (fname = realloc (fname, siz + 16)))
X return (NULL);
X }
X sprintf (fname, "%s_lock", fle);
X return (fname);
X#else
X static char fname[64];
X unsigned long n;
X char *ptr;
X
X for (ptr = fle, n = 0; *ptr; n += *ptr++ & 0xff)
X ;
X sprintf (fname, "/dd/TMP/%u_lock", n);
X return (fname);
X#endif
X}
X
X# ifdef OSK
Xstatic void
Xstop_signals ()
X{
X sigmask (1);
X}
X
Xstatic void
Xstart_signals ()
X{
X sigmask (-1);
X}
X# else /* OSK */
Xtypedef int SIGTYPE;
X
Xstatic SIGTYPE (*signals)()[NSIG];
X
Xstatic void
Xstop_signals ()
X{
X int t;
X
X for (t = 0; t < NSIG; ++t)
X signals[t] = signal (t, SIG_IGN);
X}
X
Xstatic void
Xstart_signals ()
X{
X int t;
X
X for (t = 0; t < NSIG; ++t)
X signal (t, signals[t]);
X}
X# endif /* OSK */
X
Xstatic int lock_fd = -1;
X
Xstatic int
Xlock_index (fle)
Xchar *fle;
X{
X int tout;
X char *fn;
X
X if (lock_fd != -1)
X close (lock_fd);
X if (! (fn = lock_filename (fle)))
X return (-1);
X for (tout = 20; tout > 0; --tout) {
X# ifndef OSK
X if ((lock_fd = creat (fn, 0444)) == -1)
X# else /* OSK */
X unlink (fn);
X if (((lock_fd = create (fn, S_IWRITE, 033)) == -1) && (errno == E_CEF))
X# endif /* OSK */
X sleep (1);
X else
X break;
X }
X if (lock_fd != -1)
X stop_signals ();
X return (lock_fd == -1 ? -1 : 0);
X}
X
Xstatic void
Xunlock_index (fle)
Xchar *fle;
X{
X char *fn;
X
X if (lock_fd != -1) {
X close (lock_fd);
X lock_fd = -1;
X if (fn = lock_filename (fle)) {
X# ifndef OSK
X chmod (fn, 0666);
X# endif /* OSK */
X unlink (fn);
X }
X start_signals ();
X }
X}
X# endif /* LOCK_INDEX */
X
X/*
X * Construct the pointers to the basenotes of each thread
X * arts[] contains every article in the group. inthread is
X * set on each article that is after the first article in the
X * thread. Articles which have been expired have their thread
X * set to -2.
X */
X
Xfind_base() {
X int i;
X
X top_base = 0;
X
X for (i = 0; i < top; i++)
X if (!arts[i].inthread && arts[i].thread != -2) {
X if (top_base >= max_art)
X expand_art();
X base[top_base++] = i;
X }
X}
X
X
X/*
X * Count the number of non-expired articles in arts[]
X */
X
Xnum_arts() {
X int sum = 0;
X int i;
X
X for (i = 0; i < top; i++)
X if (arts[i].thread != -2)
X sum++;
X
X return sum;
X}
X
X
X/*
X * Do we have an entry for article art?
X */
X
Xvalid_artnum(art)
Xlong art;
X{
X int i;
X
X for (i = 0; i < top; i++)
X if (arts[i].artnum == art)
X return i;
X
X return -1;
X}
X
X
X/*
X * Return TRUE if arts[] contains any expired articles
X * (articles we have an entry for which don't have a corresponding
X * article file in the spool directory)
X */
X
Xpurge_needed() {
X int i;
X
X for (i = 0; i < top; i++)
X if (arts[i].thread == -2)
X return TRUE;
X
X return FALSE;
X}
X
X
X/*
X * Main group indexing routine. Group should be the name of the
X * newsgroup, i.e. "comp.unix.amiga". group_path should be the
X * same but with the .'s turned into /'s: "comp/unix/amiga"
X *
X * Will read any existing index, create or incrementally update
X * the index by looking at the articles in the spool directory,
X * and attempt to write a new index if necessary.
X */
X
Xindex_group(group, group_path)
Xchar *group;
Xchar *group_path;
X{
X int modified;
X
X glob_art_group = group;
X
X#ifdef SIGTSTP
X signal(SIGTSTP, art_susp);
X#endif
X
X if (!update) {
X clear_message();
X MoveCursor(LINES, 0);
X printf("Group %s... ", group);
X fflush(stdout);
X }
X
X if (local_index)
X find_local_index(group);
X else
X sprintf(index_file, "%s/%s/.tindex", SPOOLDIR, group_path);
X
X#ifdef LOCK_INDEX
X if (! local_index)
X if (lock_index (index_file) < 0)
X return -1;
X#endif /* LOCK_INDEX */
X
X load_index();
X modified = read_group(group_path);
X make_threads();
X if (modified || purge_needed()) {
X#ifdef USE_UID
X if (local_index) { /* writing index in home directory */
X setuid(real_uid); /* so become them */
X setgid(real_gid);
X }
X#endif /* USE_UID */
X dump_index(group);
X#ifdef USE_UID
X if (local_index) {
X setuid(tass_uid);
X setgid(tass_gid);
X }
X#endif /* USE_UID */
X }
X
X#ifdef LOCK_INDEX
X if (! local_index)
X unlock_index (index_file);
X#endif /* LOCK_INDEX */
X
X find_base();
X
X if (modified && !update)
X clear_message();
X}
X
X
X/*
X * Longword comparison routine for the qsort()
X */
X
Xbase_comp(a, b)
Xlong *a;
Xlong *b;
X{
X
X if (*a < *b)
X return -1;
X if (*a > *b)
X return 1;
X return 0;
X}
X
X
X/*
X * Read the article numbers existing in a group's spool directory
X * into base[] and sort them. base_top is one past top.
X */
X
Xscan_dir(group)
Xchar *group;
X{
X DIR *d;
X DIR_BUF *e;
X long art;
X char buf[200];
X
X top_base = 0;
X
X sprintf(buf, "%s/%s", SPOOLDIR, group);
X
X d = opendir(buf);
X if (d != NULL) {
X while ((e = readdir(d)) != NULL) {
X#ifdef OSK
X e->D_LENGTH = strlen (e->d_name);
X#endif /* OSK */
X art = my_atol(e->d_name, e->D_LENGTH);
X if (art >= 0) {
X if (top_base >= max_art)
X expand_art();
X base[top_base++] = art;
X }
X }
X closedir(d);
X }
X
X qsort(base, top_base, sizeof(long), base_comp);
X}
X
X
X/*
X * Index a group. Assumes any existing index has already been
X * loaded.
X */
X
Xread_group(group)
Xchar *group;
X{
X char buf[200];
X int fd;
X long art;
X int count;
X int modified = FALSE;
X int respnum;
X int i;
X
X scan_dir(group); /* load article numbers into base[] */
X
X count = 0;
X
X for (i = 0; i < top_base; i++) { /* for each article # */
X art = base[i];
X
X/*
X * Do we already have this article in our index? Change thread from
X * -2 to -1 if so and skip the header eating.
X */
X
X if ((respnum = valid_artnum(art)) >= 0) {
X arts[respnum].thread = -1;
X arts[respnum].unread = 1;
X continue;
X }
X
X if (!modified) {
X modified = TRUE; /* we've modified the index */
X /* it will need to be re-written */
X#if 0
X if (!update) {
X MoveCursor(LINES, 0);
X fputs("Indexing... ", stdout);
X fflush(stdout);
X }
X#endif
X }
X
X/*
X * Add article to arts[]
X */
X if (top >= max_art)
X expand_art();
X
X arts[top].artnum = art;
X arts[top].thread = -1;
X arts[top].inthread = FALSE;
X arts[top].unread = 1;
X
X sprintf(buf, "%s/%s/%ld", SPOOLDIR, group, art);
X#ifndef MNEWS
X fd = open(buf, 0);
X#else /* MNEWS */
X#ifndef OSK
X if ((fd = open (buf, O_RDONLY)) != -1) {
X#else /* OSK */
X if ((fd = open (buf, S_IREAD)) != -1) {
X#endif /* OSK */
X int n;
X n = readln (fd, buf, 180);
X lseek (fd, 0, 0);
X if (n > 0) {
X buf[n - 1] = '\0';
X if (!strncmp (buf, "%(#)$ ", 6)) {
X char sav[200];
X strcpy (sav, buf + 6);
X sprintf (buf, "%s/%s", spooldir, sav);
X close (fd);
X#ifndef OSK
X fd = open (buf, O_RDONLY);
X#else /* OSK */
X fd = open (buf, S_IREAD);
X#endif /* OSK */
X }
X }
X }
X#endif /* MNEWS */
X
X if (fd < 0) {
X fprintf(stderr, "can't open article %s: ", buf);
X perror("");
X continue;
X }
X
X if (!parse_headers(fd, &arts[top]))
X continue;
X top++;
X close(fd);
X
X if (++count % 10 == 0 && !update) {
X printf("\b\b\b\b%4d", count);
X fflush(stdout);
X }
X }
X
X complete_count += count;
X
X return modified;
X}
X
X
X/*
X * Go through the articles in arts[] and use .thread to snake threads
X * through them. Use the subject line to construct threads. The
X * first article in a thread should have .inthread set to FALSE, the
X * rest TRUE. Only do unexprired articles we haven't visited yet
X * (arts[].thread == -1).
X */
X
Xmake_threads() {
X int i;
X int j;
X
X for (i = 0; i < top; i++) {
X if (arts[i].thread == -1)
X for (j = i+1; j < top; j++)
X if (arts[i].hash == arts[j].hash
X && arts[j].thread != -2
X && strncmp(arts[i].nore, arts[j].nore, 10) == 0) {
X arts[i].thread = j;
X arts[j].inthread = TRUE;
X break;
X }
X }
X}
X
X
X/*
X * Return a pointer into s eliminating any leading Re:'s. Example:
X *
X * Re: Reorganization of misc.jobs
X * ^ ^
X */
X
Xchar *
Xeat_re(s)
Xchar *s;
X{
X
X#if 1
X while (*s == 'r' || *s == 'R') {
X if (*(s+1) == 'e' || *(s+1) == 'E') {
X if (*(s+2) == ':')
X s += 3;
X else if ((*(s+2) == '^') && isdigit (*(s+3))) {
X s += 4;
X if (*s == ':')
X ++s;
X } else
X break;
X } else
X break;
X while (isspace (*s))
X ++s;
X }
X#else
X while (*s == 'R') {
X if (strncmp(s, "Re: ", 4) == 0)
X s += 4;
X else if (strncmp(s, "Re:", 3) == 0)
X s += 3;
X else if (strncmp(s, "Re^2: ", 6) == 0)
X s += 6;
X else
X break;
X }
X#endif
X
X return s;
X}
X
X
X/*
X * Hash the subjects (after eating the Re's off) for a quicker
X * thread search later. We store the hashes for subjects in the
X * index file for speed.
X */
X
Xlong
Xhash_s(s)
Xchar *s;
X{
X long h = 0;
X
X while (*s)
X h = h + (*s++ & 0xff);
X
X return h;
X}
X
X
Xparse_headers(fd, h)
Xint fd;
Xstruct header *h;
X{
X char buf[1024];
X char *p, *q;
X char flag;
X char *ptr;
X int found_from, found_subj;
X int n;
X
X if ((n = read(fd, buf, 1024)) <= 0)
X return FALSE;
X
X buf[n > 1023 ? 1023 : n] = '\0';
X
X p = buf;
X while (p = index (p, '\n'))
X if (*(p + 1) == '\n') {
X *p = '\0';
X break;
X } else
X ++p;
X
X p = buf;
X
X h->from[0] = '\0';
X h->subject[0] = '\0';
X h->nore = h->subject;
X h->hash = 0;
X
X found_from = FALSE;
X found_subj = FALSE;
X while (1) {
X q = p;
X while (*p && *p != '\n') {
X if ((unsigned char) *p & 0x7F < 32)
X *p = ' ';
X p++;
X }
X flag = *p;
X *p++ = '\0';
X
X if ((!found_from) && (strncmp(q, "From: ", 6) == 0)) {
X if (ptr = index (&q[6], '('))
X ++ptr;
X else
X ptr = &q[6];
X strncpy (h->from, ptr, MAX_FROM);
X h->from[MAX_FROM-1] = '\0';
X if (ptr = index (h->from, ')'))
X *ptr = '\0';
X } else if ((!found_subj) && (strncmp(q, "Subject: ", 9) == 0)) {
X h->hash = hash_s(eat_re(&q[9]));
X strncpy(h->subject, &q[9], MAX_SUBJ);
X h->subject[MAX_SUBJ-1] = '\0';
X h->nore = eat_re(h->subject);
X }
X
X if ((!flag) || (found_from && found_subj))
X break;
X }
X
X return TRUE;
X}
X
X
X/*
X * Write out a .tindex file. Write the group name first so if
X * local indexing is done we can disambiguate between group name
X * hash collisions by looking at the index file.
X */
Xvoid
Xdump_index(group)
Xchar *group;
X{
X int i;
X char buf[200];
X FILE *fp;
X char *p, *q;
X long l;
X#ifndef USE_UID
X char lockfn[64];
X int lockfd;
X
X if (! local_index) {
X#ifndef OSK
X sprintf (lockfn, "/tmp/%-.10s", group);
X lockfd = creat (lockfn, 0, 0444);
X#else /* OSK */
X sprintf (lockfn, "/dd/TMP/%-.20s", group);
X for (p = lockfn; *p; ++p)
X if (((unsigned char) *p >= 0x80) || ((!isalnum (*p)) && (*p != '/')))
X *p = '_';
X lockfd = create (lockfn, 0, 033);
X#endif /* OSK */
X if (lockfd < 0) {
X sleep (1);
X return;
X }
X }
X#endif /* USE_UID */
X
X fp = fopen(index_file, "w");
X if (fp == NULL)
X#ifndef USE_UID
X goto dump_index_finish;
X#else /* USE_UID */
X return;
X#endif /* USE_UID */
X
X fprintf(fp, "%s\n", group);
X fprintf(fp, "%d\n", num_arts());
X for (i = 0; i < top; i++)
X if (arts[i].thread != -2) {
X p = arts[i].nore;
X q = arts[i].subject;
X l = p - q;
X fprintf(fp, "%ld\n%s\n%s\n%ld\n%ld\n",
X arts[i].artnum,
X arts[i].subject,
X arts[i].from,
X arts[i].hash,
X#if 0
X (long) arts[i].nore - (long) arts[i].subject);
X#else
X l);
X#endif
X } else
X ++complete_expire;
X
X fclose(fp);
X#ifdef USE_UID
X#ifndef OSK
X chmod(index_file, 0644);
X#else /* OSK */
X chmod(index_file, 013);
X#endif /* OSK */
X#else /* USE_UID */
Xdump_index_finish:
X if (local_index)
X#ifndef OSK
X chmod(index_file, 0644);
X#else /* OSK */
X chmod(index_file, 013);
X#endif /* OSK */
X else if (lockfd != -1) {
X close (lockfd);
X#ifndef OSK
X chmod (lockfn, 0666);
X#endif /* OSK */
X unlink (lockfn);
X#ifndef OSK
X chmod(index_file, 0666);
X#else /* OSK */
X chmod(index_file, 033);
X#endif /* OSK */
X }
X#endif /* USE_UID */
X}
X
X
X/*
X * strncpy that stops at a newline and null terminates
X */
X
Xmy_strncpy(p, q, n)
Xchar *p;
Xchar *q;
Xint n;
X{
X
X while (n--) {
X if (!*q || *q == '\n')
X break;
X *p++ = *q++;
X }
X *p = '\0';
X}
X
X
X/*
X * Read in a .tindex file.
X */
Xvoid
Xload_index()
X{
X int i;
X long j;
X char buf[200];
X FILE *fp;
X int first = TRUE;
X
X top = 0;
X
X fp = fopen(index_file, "r");
X if (fp == NULL)
X return;
X
X if (fgets(buf, 200, fp) == NULL
X || fgets(buf, 200, fp) == NULL) {
X fprintf(stderr, "one\n");
X goto corrupt_index;
X }
X
X i = atol(buf);
X while (top < i) {
X if (top >= max_art)
X expand_art();
X
X arts[top].thread = -2;
X arts[top].inthread = FALSE;
X
X if (fgets(buf, 200, fp) == NULL) {
X fprintf(stderr, "two\n");
X goto corrupt_index;
X }
X arts[top].artnum = atol(buf);
X
X if (fgets(buf, 200, fp) == NULL) {
X fprintf(stderr, "three\n");
X goto corrupt_index;
X }
X
X my_strncpy(arts[top].subject, buf, MAX_SUBJ-1);
X
X if (fgets(buf, 200, fp) == NULL) {
X fprintf(stderr, "four\n");
X goto corrupt_index;
X }
X my_strncpy(arts[top].from, buf, MAX_FROM-1);
X
X if (fgets(buf, 200, fp) == NULL) {
X fprintf(stderr, "five\n");
X goto corrupt_index;
X }
X arts[top].hash = atol(buf);
X
X if (fgets(buf, 200, fp) == NULL) {
X fprintf(stderr, "six\n");
X goto corrupt_index;
X }
X
X j = atol(buf);
X#if 0
X if (j < 0 || j > 100) {
X#if 0
X goto corrupt_index;
X#else
X arts[top].nore = eat_re(arts[top].subject);
X#endif
X } else
X arts[top].nore = arts[top].subject + j;
X#else
X arts[top].nore = eat_re(arts[top].subject);
X#endif
X
X top++;
X }
X
X fclose(fp);
X return;
X
Xcorrupt_index:
X fprintf(stderr, "index file %s corrupt\n", index_file);
X fprintf(stderr, "top = %d\n", top);
X exit(1);
X unlink(index_file);
X top = 0;
X}
X
X
X/*
X * Look in the local $HOME/.tindex (or wherever) directory for the
X * index file for the given group. Hashing the group name gets
X * a number. See if that #.1 file exists; if so, read first line.
X * Group we want? If no, try #.2. Repeat until no such file or
X * we find an existing file that matches our group.
X */
Xvoid
Xfind_local_index(group)
Xchar *group;
X{
X unsigned long h;
X static char buf[200];
X int i;
X char *p;
X FILE *fp;
X
X {
X char *t = group;
X
X h = *t++;
X while (*t)
X h = (h << 1) + (*t++ & 0xff);
X }
X
X i = 1;
X while (1) {
X sprintf(index_file, "%s/%lu.%d", indexdir, h, i);
X fp = fopen(index_file, "r");
X if (fp == NULL)
X return;
X
X if (fgets(buf, 200, fp) == NULL) {
X fclose(fp);
X return;
X }
X fclose(fp);
X
X for (p = buf; *p && *p != '\n'; p++) ;
X *p = '\0';
X
X if (strcmp(buf, group) == 0)
X return;
X
X i++;
X }
X}
X
X
X/*
X * Run the index file updater only for the groups we've loaded.
X */
X
Xdo_update() {
X int i;
X char group_path[200];
X char *p;
X#ifdef OSK
X int len = 0;
X#endif /* OSK */
X time_t tim;
X struct tm *tt;
X
X complete_count = 0;
X complete_expire = 0;
X
X for (i = 0; i < local_top; i++) {
X strcpy(group_path, active[my_group[i]].name);
X for (p = group_path; *p; p++)
X#ifndef OSK
X if (*p == '.')
X *p = '/';
X#else /* OSK */
X {
X if (*p == '.') {
X *p = '/';
X len = 0;
X } else if ((! isalnum (*p)) && (! index ("$_", *p)))
X *p = '_';
X if (++len > 26)
X while (*(p + 1) && (*(p + 1) != '.'))
X ++p;
X }
X#endif /* OSK */
X
X index_group(active[my_group[i]].name, group_path);
X }
X
X time (&tim);
X tt = localtime (&tim);
X printf ("[%02d.%02d.%04d %2d:%2d] Collected %d articles",
X tt -> tm_mday,
X tt -> tm_mon + 1,
X tt -> tm_year + 1900,
X tt -> tm_hour,
X tt -> tm_min,
X complete_count);
X
X if (complete_expire)
X printf (" (%d articles expired)", complete_expire);
X putchar ('\n');
X}
__END__OF__THIS__FILE__
if test -f 'curses.c' ; then
echo 'File curses.c already exists, overwriting it'
del 'curses.c'
fi
echo Extracting \"'curses.c'\"
sed "s/^X//" >'curses.c' <<'__END__OF__THIS__FILE__'
X
X/*
X * This is a screen management library borrowed with permission from the
X * Elm mail system (a great mailer--I highly recommend it!).
X *
X * I've hacked this library to only provide what Tass needs.
X *
X * Original copyright follows:
X */
X
X/*******************************************************************************
X * The Elm Mail System - $Revision: 2.1 $ $State: Exp $
X *
X * Copyright (c) 1986 Dave Taylor
X ******************************************************************************/
X
X#include <stdio.h>
X#ifndef OSK
X#include <curses.h>
X#include <sys/ioctl.h>
X#else /* OSK */
X#include <termcap.h>
X#include <sgstat.h>
Xchar PC_, *UP, *BC;
Xshort ospeed;
X#endif /* OSK */
X#include "tass.h"
X
X#define BACKSPACE '\b'
X#define VERY_LONG_STRING 2500
X
Xint LINES=23;
Xint COLS=80;
X
Xint inverse_okay = TRUE;
X
X/*
X#ifdef BSD
X# ifndef BSD4_1
X# include <sgtty.h>
X# else
X# include <termio.h>
X# endif
X# else
X# include <termio.h>
X#endif
X*/
X
X#include <ctype.h>
X
X/*
X#ifdef BSD
X#undef tolower
X#endif
X*/
X
X#define TTYIN 0
X
X#ifdef SHORTNAMES
X# define _clearinverse _clrinv
X# define _cleartoeoln _clrtoeoln
X# define _cleartoeos _clr2eos
X#endif
X
X#ifndef BSD
X#ifndef OSK
Xstruct termio _raw_tty,
X _original_tty;
X#else /* OSK */
Xstruct sgbuf _raw_tty,
X _original_tty;
X#endif /* OSK */
X#else
X#define TCGETA TIOCGETP
X#define TCSETAW TIOCSETP
X
Xstruct sgttyb _raw_tty,
X _original_tty;
X#endif
X
Xstatic int _inraw = 0; /* are we IN rawmode? */
X
X#define DEFAULT_LINES_ON_TERMINAL 24
X#define DEFAULT_COLUMNS_ON_TERMINAL 80
X
Xstatic int _memory_locked = 0; /* are we IN memlock?? */
X
Xstatic int _intransmit; /* are we transmitting keys? */
X
Xstatic
Xchar *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos,
X *_setinverse, *_clearinverse, *_setunderline, *_clearunderline;
X
X# define K_UP 0
X# define K_DOWN 1
X# define K_LEFT 2
X# define K_RIGHT 3
X# define K_PUP 4
X# define K_PDOWN 5
X
X# define K_MAX 6
X
Xstatic
Xchar *_spckey[K_MAX];
Xstatic
Xchar *_spckeytc[K_MAX] = {
X "ku", "kd", "kl", "kr", "kP", "kN"
X};
X
Xstatic
Xint _lines,_columns;
X
Xstatic char _terminal[1024]; /* Storage for terminal entry */
Xstatic char _capabilities[1024]; /* String for cursor motion */
X
Xstatic char *ptr = _capabilities; /* for buffering */
X
Xint outchar(); /* char output for tputs */
Xchar *tgetstr(), /* Get termcap capability */
X *tgoto(); /* and the goto stuff */
X
XInitScreen()
X{
Xint tgetent(), /* get termcap entry */
X err;
Xchar termname[40];
Xchar *strcpy(), *getenv();
Xint i;
X
X if (getenv("TERM") == NULL) {
X fprintf(stderr,
X "TERM variable not set; Tass requires screen capabilities\n");
X return(FALSE);
X }
X if (strcpy(termname, getenv("TERM")) == NULL) {
X fprintf(stderr,"Can't get TERM variable\n");
X return(FALSE);
X }
X if ((err = tgetent(_terminal, termname)) != 1) {
X fprintf(stderr,"Can't get entry for TERM\n");
X return(FALSE);
X }
X
X /* load in all those pesky values */
X _clearscreen = tgetstr("cl", &ptr);
X _moveto = tgetstr("cm", &ptr);
X _cleartoeoln = tgetstr("ce", &ptr);
X _cleartoeos = tgetstr("cd", &ptr);
X _lines = tgetnum("li");
X _columns = tgetnum("co");
X _setinverse = tgetstr("so", &ptr);
X _clearinverse = tgetstr("se", &ptr);
X _setunderline = tgetstr("us", &ptr);
X _clearunderline = tgetstr("ue", &ptr);
X
X if (!_clearscreen) {
X fprintf(stderr,
X "Terminal must have clearscreen (cl) capability\n");
X return(FALSE);
X }
X if (!_moveto) {
X fprintf(stderr,
X "Terminal must have cursor motion (cm)\n");
X return(FALSE);
X }
X if (!_cleartoeoln) {
X fprintf(stderr,
X "Terminal must have clear to end-of-line (ce)\n");
X return(FALSE);
X }
X if (!_cleartoeos) {
X fprintf(stderr,
X "Terminal must have clear to end-of-screen (cd)\n");
X return(FALSE);
X }
X if (_lines == -1)
X _lines = DEFAULT_LINES_ON_TERMINAL;
X if (_columns == -1)
X _columns = DEFAULT_COLUMNS_ON_TERMINAL;
X
X for (i = 0; i < K_MAX; ++i)
X _spckey[i] = tgetstr (_spckeytc[i], &ptr);
X
X return(TRUE);
X}
X
XScreenSize(lines, columns)
Xint *lines, *columns;
X{
X /** returns the number of lines and columns on the display. **/
X
X if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL;
X if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL;
X
X *lines = _lines - 1; /* assume index from zero*/
X *columns = _columns; /* assume index from one */
X}
X
XClearScreen()
X{
X /* clear the screen: returns -1 if not capable */
X
X tputs(_clearscreen, 1, outchar);
X fflush(stdout); /* clear the output buffer */
X}
X
XMoveCursor(row, col)
Xint row, col;
X{
X /** move cursor to the specified row column on the screen.
X 0,0 is the top left! **/
X
X char *stuff, *tgoto();
X
X stuff = tgoto(_moveto, col, row);
X tputs(stuff, 1, outchar);
X fflush(stdout);
X}
X
XCleartoEOLN()
X{
X /** clear to end of line **/
X
X tputs(_cleartoeoln, 1, outchar);
X fflush(stdout); /* clear the output buffer */
X}
X
XCleartoEOS()
X{
X /** clear to end of screen **/
X
X tputs(_cleartoeos, 1, outchar);
X fflush(stdout); /* clear the output buffer */
X}
X
XStartInverse()
X{
X /** set inverse video mode **/
X
X if (_setinverse && inverse_okay)
X tputs(_setinverse, 1, outchar);
X/* fflush(stdout); */
X}
X
X
XEndInverse()
X{
X /** compliment of startinverse **/
X
X if (_clearinverse && inverse_okay)
X tputs(_clearinverse, 1, outchar);
X/* fflush(stdout); */
X}
X
X#if 0
XStartUnderline()
X{
X /** start underline mode **/
X
X if (!_setunderline)
X return(-1);
X
X tputs(_setunderline, 1, outchar);
X fflush(stdout);
X return(0);
X}
X
X
XEndUnderline()
X{
X /** the compliment of start underline mode **/
X
X if (!_clearunderline)
X return(-1);
X
X tputs(_clearunderline, 1, outchar);
X fflush(stdout);
X return(0);
X}
X#endif
X
XRawState()
X{
X /** returns either 1 or 0, for ON or OFF **/
X
X return( _inraw );
X}
X
XRaw(state)
Xint state;
X{
X /** state is either TRUE or FALSE, as indicated by call **/
X
X if (state == FALSE && _inraw) {
X#ifndef OSK
X (void) ioctl(TTYIN, TCSETAW, &_original_tty);
X#else /* OSK */
X (void) _ss_opt (TTYIN, &_original_tty);
X#endif /* OSK */
X _inraw = 0;
X }
X else if (state == TRUE && ! _inraw) {
X
X#ifndef OSK
X (void) ioctl(TTYIN, TCGETA, &_original_tty); /** current setting **/
X
X (void) ioctl(TTYIN, TCGETA, &_raw_tty); /** again! **/
X#ifdef BSD
X _raw_tty.sg_flags &= ~(ECHO | CRMOD); /* echo off */
X _raw_tty.sg_flags |= CBREAK; /* raw on */
X#else
X _raw_tty.c_lflag &= ~(ICANON | ECHO); /* noecho raw mode */
X
X _raw_tty.c_cc[VMIN] = '\01'; /* minimum # of chars to queue */
X _raw_tty.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
X
X#endif
X (void) ioctl(TTYIN, TCSETAW, &_raw_tty);
X#else /* OSK */
X (void) _gs_opt (TTYIN, &_original_tty);
X _raw_tty = _original_tty;
X _raw_tty.sg_echo = 0;
X _raw_tty.sg_pause = 0;
X _raw_tty.sg_alf = 0;
X _raw_tty.sg_psch = 0;
X _raw_tty.sg_eofch = 0;
X
X (void) _ss_opt (TTYIN, &_raw_tty);
X#endif /* OSK */
X
X _inraw = 1;
X }
X}
X
Xint
XReadCh()
X{
X /** read a character with Raw mode set! **/
X
X register int result;
X char ch;
X register int i, j, n;
X register char *spc;
X
X spc = NULL;
X do {
X if (spc) {
X#ifdef OSK
X tsleep (4);
X if (_gs_rdy (0) < 1)
X tsleep (50 | (1 << 31));
X if (_gs_rdy (0) < 1)
X#else /* OSK */
X#ifdef FIONREAD
X#define CH_READ FIONREAD
X#else /* FIONREAD */
X#ifdef TCRDCHK
X#define CH_READ TCRDCHK
X#else /* TCRDCHK */
X Panic!
X#endif /* TCRDCHK */
X#endif /* FIONREAD */
X int tmp;
X
X ioctl (0, CH_READ, &tmp);
X if (tmp < 1)
X nap (200);
X ioctl (0, CH_READ, &tmp);
X if (tmp < 1)
X#endif /* OSK */
X spc = NULL;
X }
X if ((result = read(0, &ch, 1)) != 1)
X break;
X if (!spc) {
X for (i = 0; i < K_MAX; ++i)
X if (_spckey[i] && (_spckey[i][0] == ch)) {
X spc = _spckey[i];
X n = 1;
X break;
X }
X } else if (ch != spc[n]) {
X for (j = 0; j < K_MAX; ++j)
X if (!_spckey[j])
X continue;
X else if (spc[0]) {
X if ((!strncmp (spc, _spckey[j], n - 1)) && (_spckey[j][n] == ch))
X break;
X } else if (spc[0] == _spckey[j][0]) {
X if (n > 1) {
X if ((!strncmp (spc + 1, _spckey[j] + 1, n - 2)) && (_spckey[j][n] == ch))
X break;
X } else if (_spckey[j][n] == ch)
X break;
X }
X if (j < K_MAX) {
X spc = _spckey[j];
X i = j;
X ++n;
X } else {
X spc = NULL;
X result = -1;
X }
X } else
X ++n;
X if (spc && (!spc[n])) {
X spc = NULL;
X switch (i) {
X case K_UP:
X ch = ctrl('P');
X break;
X case K_DOWN:
X ch = ctrl('N');
X break;
X case K_LEFT:
X ch = ctrl('B');
X break;
X case K_RIGHT:
X ch = ctrl('F');
X break;
X case K_PUP:
X ch = ctrl('Z');
X break;
X case K_PDOWN:
X ch = ctrl('V');
X break;
X }
X }
X } while (spc || (result < 0));
X return((result <= 0 ) ? EOF : ch & 0x7F);
X}
X
X
Xoutchar(c)
Xchar c;
X{
X /** output the given character. From tputs... **/
X /** Note: this CANNOT be a macro! **/
X
X putc(c, stdout);
X}
X
X#ifdef NO_NAP
Xnap (n)
Xint n;
X{
X int t;
X
X for (t = 100 * n; t > 0; --t)
X ;
X}
X#endif /* NO_NAP */
__END__OF__THIS__FILE__
if test -f 'group.c' ; then
echo 'File group.c already exists, overwriting it'
del 'group.c'
fi
echo Extracting \"'group.c'\"
sed "s/^X//" >'group.c' <<'__END__OF__THIS__FILE__'
X
X
X#include <stdio.h>
X#include <signal.h>
X#ifdef REGEXP
X#include <regexp.h>
X#endif /* REGEXP */
X#ifdef OSK
X#include <ctype.h>
X#endif /* OSK */
X#include "tass.h"
X
X#ifdef GET_DIST
Xextern char *index ();
X#endif /* GET_DIST */
X
Xint index_point;
Xint first_subj_on_screen;
Xint last_subj_on_screen;
Xchar subject_search_string[LEN+1]; /* last search pattern */
Xextern int cur_groupnum;
Xextern int last_resp; /* page.c */
Xextern int this_resp; /* page.c */
Xextern int space_mode; /* select.c */
Xextern char *cvers;
X
Xchar *glob_group;
X
X
X#ifdef SIGTSTP
Xvoid
Xgroup_susp(i)
Xint i;
X{
X
X Raw(FALSE);
X putchar('\n');
X signal(SIGTSTP, SIG_DFL);
X kill(0, SIGTSTP);
X
X signal(SIGTSTP, group_susp);
X Raw(TRUE);
X mail_setup();
X show_group_page(glob_group, TRUE);
X}
X#endif
X
X
Xgroup_page(group)
Xchar *group;
X{
X char ch;
X int i, n;
X char group_path[200];
X char *p;
X char buf[200];
X
X glob_group = group;
X
X strcpy(group_path, group); /* turn comp.unix.amiga into */
X for (p = group_path; *p; p++) /* comp/unix/amiga */
X if (*p == '.')
X *p = '/';
X#ifdef OSK
X else if ((!isalnum (*p)) || ((unsigned char) *p >= 0x80))
X *p = '_';
X#endif /* OSK */
X
X last_resp = -1;
X this_resp = -1;
X index_group(group, group_path); /* update index file */
X read_newsrc_line(group); /* get sequencer information */
X
X if (space_mode) {
X for (i = 0; i < top_base; i++)
X if (new_responses(i))
X break;
X if (i < top_base)
X index_point = i;
X else
X index_point = top_base - 1;
X } else
X index_point = top_base - 1;
X
X show_group_page(group, TRUE);
X
X while (1) {
X if ((n = ReadCh()) == EOF)
X longjmp (jmp_buffer, 1);
X ch = (char) n;
X
X if (ch > '0' && ch <= '9') { /* 0 goes to basenote */
X prompt_subject_num(ch, group);
X } else switch (ch) {
X case 'I': /* toggle inverse video */
X inverse_okay = !inverse_okay;
X if (inverse_okay)
X info_message("Inverse video enabled");
X else
X info_message("Inverse video disabled");
X break;
X
X case 's': /* subscribe to this group */
X subscribe(group, ':', my_group[cur_groupnum],
X TRUE);
X sprintf(buf, "subscribed to %s", group);
X info_message(buf);
X break;
X
X case 'u': /* unsubscribe to this group */
X subscribe(group, '!', my_group[cur_groupnum],
X TRUE);
X sprintf(buf, "unsubscribed to %s", group);
X info_message(buf);
X break;
X
X case 'g': /* choose a new group by name */
X n = choose_new_group();
X if (n >= 0 && n != cur_groupnum) {
X fix_new_highest(cur_groupnum);
X cur_groupnum = n;
X index_point = -3;
X goto group_done;
X }
X break;
X
X case 'c': /* catchup--mark all articles as read */
X if (prompt_yn("Mark everything as read? (y/n): ")) {
X for (n = 0; n < top; n++)
X arts[n].unread = 0;
X show_group_page(group, FALSE);
X info_message("All articles marked as read");
X }
X break;
X
X#ifdef USE_ARROW
X case 27: /* common arrow keys */
X ch = ReadCh();
X if (ch == '[' || ch == 'O')
X ch = ReadCh();
X switch (ch) {
X case 'A':
X case 'D':
X case 'i':
X goto group_up;
X
X case 'B':
X case 'I':
X case 'C':
X goto group_down;
X }
X break;
X#endif /* USE_ARROW */
X
X case 'n': /* next group */
X clear_message();
X if (cur_groupnum + 1 >= local_top)
X info_message("No more groups");
X else {
X fix_new_highest(cur_groupnum);
X cur_groupnum++;
X index_point = -3;
X space_mode = FALSE;
X goto group_done;
X }
X break;
X
X case 'p': /* previous group */
X clear_message();
X if (cur_groupnum <= 0)
X info_message("No previous group");
X else {
X fix_new_highest(cur_groupnum);
X cur_groupnum--;
X index_point = -3;
X space_mode = FALSE;
X goto group_done;
X }
X break;
X
X case ' ':
X if (top_base <= 0)
X info_message("*** No Articles ***");
X else if (last_subj_on_screen == top_base)
X info_message("*** End of Articles ***");
X else
X clear_message();
X break;
X
X case '\t':
X fix_new_highest(cur_groupnum);
X space_mode = TRUE;
X
X if (index_point < 0
X || (n=next_unread((int) base[index_point]))<0) {
X for (i = cur_groupnum+1;
X i < local_top; i++)
X if (unread[i] > 0)
X break;
X if (i >= local_top)
X goto group_done;
X
X cur_groupnum = i;
X index_point = -3;
X goto group_done;
X }
X index_point = show_page(n, group, group_path);
X if (index_point < 0)
X goto group_done;
X show_group_page(group, TRUE);
X break;
X
X
X case 'N': /* go to next unread article */
X if (index_point < 0) {
X info_message("No next unread article");
X break;
X }
X
X n = next_unread( (int) base[index_point]);
X if (n == -1)
X info_message("No next unread article");
X else {
X index_point =
X show_page(n, group, group_path, TRUE);
X if (index_point < 0) {
X fix_new_highest(cur_groupnum);
X space_mode = FALSE;
X goto group_done;
X }
X show_group_page(group, TRUE);
X }
X break;
X
X case 'P': /* go to previous unread article */
X if (index_point < 0) {
X info_message("No previous unread article");
X break;
X }
X
X n = prev_response( (int) base[index_point]);
X n = prev_unread(n);
X if (n == -1)
X info_message("No previous unread article");
X else {
X index_point =
X show_page(n, group, group_path, TRUE);
X if (index_point < 0) {
X fix_new_highest(cur_groupnum);
X space_mode = FALSE;
X goto group_done;
X }
X show_group_page(group, TRUE);
X }
X break;
X
X case 'w': /* post a basenote */
X post_base(group);
X update_newsrc(group, my_group[cur_groupnum]);
X index_group(group, group_path);
X read_newsrc_line(group);
X index_point = top_base - 1;
X show_group_page(group, TRUE);
X break;
X
X case 't': /* return to group selection page */
X case ctrl('B'): /* cursor key */
X fix_new_highest(cur_groupnum);
X goto group_done;
X
X#ifndef OSK
X case '\r':
X#else /* OSK */
X case '\l':
X#endif /* OSK */
X case '\n': /* read current basenote */
X case ctrl('F'): /* cursor key */
X if (index_point < 0) {
X info_message("*** No Articles ***");
X break;
X }
X index_point = show_page((int) base[index_point],
X group, group_path, TRUE);
X if (index_point < 0) {
X fix_new_highest(cur_groupnum);
X space_mode = FALSE;
X goto group_done;
X }
X show_group_page(group, TRUE);
X break;
X
X case ctrl('D'): /* page down */
X case ctrl('V'): /* full page down */
X if (!top_base || index_point == top_base - 1)
X break;
X
X erase_subject_arrow();
X index_point += (ch == ctrl('V')) ?
X NOTESLINES :
X NOTESLINES / 2;
X if (index_point >= top_base)
X index_point = top_base - 1;
X
X if (index_point < first_subj_on_screen
X || index_point >= last_subj_on_screen)
X show_group_page(group, TRUE);
X else
X draw_subject_arrow();
X break;
X
X case '-': /* go to last viewed article */
X if (this_resp < 0) {
X info_message("No last message");
X break;
X }
X index_point = show_page(this_resp,
X group, group_path, TRUE);
X if (index_point < 0) {
X fix_new_highest(cur_groupnum);
X space_mode = FALSE;
X goto group_done;
X }
X show_group_page(group, TRUE);
X break;
X
X case ctrl('U'): /* page up */
X case ctrl('Z'): /* full page up */
X if (!top_base)
X break;
X
X erase_subject_arrow();
X index_point -= (ch == ctrl('Z')) ?
X NOTESLINES :
X NOTESLINES / 2;
X if (index_point < 0)
X index_point = 0;
X if (index_point < first_subj_on_screen
X || index_point >= last_subj_on_screen)
X show_group_page(group, TRUE);
X else
X draw_subject_arrow();
X break;
X
X case 'v':
X info_message(cvers);
X break;
X
X case '!':
X shell_escape();
X show_group_page(group, TRUE);
X break;
X
X case ctrl('N'):
X case 'j': /* line down */
X#ifdef USE_ARROW
Xgroup_down:
X#endif /* USE_ARROW */
X if (!top_base || index_point + 1 >= top_base)
X break;
X
X erase_subject_arrow();
X index_point++;
X if (index_point >= last_subj_on_screen)
X show_group_page(group, FALSE);
X else
X draw_subject_arrow();
X break;
X
X case ctrl('P'):
X case 'k': /* line up */
X#ifdef USE_ARROW
Xgroup_up:
X#endif /* USE_ARROW */
X if (!top_base || !index_point)
X break;
X
X erase_subject_arrow();
X index_point--;
X if (index_point + 1 <= first_subj_on_screen)
X show_group_page(group, FALSE);
X else
X draw_subject_arrow();
X break;
X
X case ctrl('R'):
X case ctrl('L'):
X case ctrl('W'):
X case 'i': /* return to index */
X show_group_page(group, FALSE);
X break;
X
X case '/': /* forward search */
X search_subject(TRUE, group);
X break;
X
X case '?': /* backward search */
X search_subject(FALSE, group);
X break;
X
X#ifndef OSK
X case 'q': /* quit */
X index_point = -2;
X fix_new_highest(cur_groupnum);
X space_mode = FALSE;
X goto group_done;
X#endif /* OSK */
X
X case 'h':
X tass_group_help();
X show_group_page(group, TRUE);
X break;
X
X default:
X info_message("Bad command. Type 'h' for help.");
X }
X }
X
Xgroup_done:
X update_newsrc(group, my_group[cur_groupnum]);
X
X if (index_point == -2)
X tass_done(0);
X}
X
X
X/*
X * Correct highest[] for the group selection page display since
X * new articles may have been read or marked unread
X */
X
Xfix_new_highest(groupnum)
Xint groupnum;
X{
X int i;
X int sum = 0;
X
X for (i = 0; i < top; i++)
X if (arts[i].unread)
X sum++;
X
X unread[groupnum] = sum;
X}
X
X
Xshow_mail () {
X if (mail_check()) { /* you have mail message in */
X MoveCursor(0, COLS - 14); /* upper right */
X printf("you have mail");
X fflush (stdout);
X }
X}
X
Xshow_group_page(group, clr)
Xchar *group;
Xint clr;
X{
X int i;
X int n;
X char resps[10];
X char new_resps;
X int respnum;
X
X#ifdef SIGTSTP
X signal(SIGTSTP, group_susp);
X#endif
X
X if (clr) {
X ClearScreen();
X printf("%s\r\012", nice_time()); /* time in upper left */
X center_line(1, group);
X }
X
X show_mail ();
X
X MoveCursor(INDEX_TOP, 0);
X
X first_subj_on_screen = (index_point / NOTESLINES) * NOTESLINES;
X if (first_subj_on_screen < 0)
X first_subj_on_screen = 0;
X
X last_subj_on_screen = first_subj_on_screen + NOTESLINES;
X if (last_subj_on_screen >= top_base) {
X last_subj_on_screen = top_base;
X first_subj_on_screen = top_base - NOTESLINES;
X
X if (first_subj_on_screen < 0)
X first_subj_on_screen = 0;
X }
X
X for (i = first_subj_on_screen; i < last_subj_on_screen; i++) {
X if (new_responses(i))
X new_resps = '+';
X else
X new_resps = ' ';
X
X n = nresp(i);
X if (n)
X sprintf(resps, "%4d", n);
X else
X strcpy(resps, " ");
X
X respnum = base[i];
X
X MoveCursor(INDEX_TOP + i - first_subj_on_screen, 3);
X printf("%4d %c %s", i + 1, new_resps, arts[respnum].subject);
X CleartoEOLN ();
X MoveCursor(INDEX_TOP + i - first_subj_on_screen, COLS - 35);
X printf ("%s %-.30s", resps, arts[respnum].from);
X MoveCursor(INDEX_TOP + i - first_subj_on_screen, COLS - 1);
X CleartoEOLN ();
X }
X
X if (top_base <= 0)
X info_message("*** No Articles ***");
X else if (last_subj_on_screen == top_base)
X info_message("*** End of Articles ***");
X
X if (top_base > 0)
X draw_subject_arrow();
X}
X
Xdraw_subject_arrow() {
X
X draw_arrow(INDEX_TOP + (index_point-first_subj_on_screen) );
X}
X
Xerase_subject_arrow() {
X
X erase_arrow(INDEX_TOP + (index_point-first_subj_on_screen) );
X}
X
X
Xprompt_subject_num(ch, group)
Xchar ch;
Xchar *group;
X{
Xint num;
X
X
X clear_message();
X
X if ((num = parse_num(ch, "Read article> ")) == -1) {
X clear_message();
X return FALSE;
X }
X num--; /* index from 0 (internal) vs. 1 (user) */
X
X if (num >= top_base)
X num = top_base - 1;
X
X if (num >= first_subj_on_screen
X && num < last_subj_on_screen) {
X erase_subject_arrow();
X index_point = num;
X draw_subject_arrow();
X } else {
X index_point = num;
X show_group_page(group, TRUE);
X }
X}
X
Xvoid
Xsearch_subject(forward, group)
Xint forward;
Xchar *group;
X{
X char buf[LEN+1];
X int i;
X#ifndef REGEXP
X extern char *regcmp();
X extern char *regex();
X char *re;
X#else /* REGEXP */
X regexp *re;
X#endif /* REGEXP */
X char *prompt;
X
X clear_message();
X
X if (forward)
X prompt = "/";
X else
X prompt = "?";
X
X if (!parse_string(prompt, buf))
X return;
X
X if (strlen(buf))
X strcpy(subject_search_string, buf);
X else if (!strlen(subject_search_string)) {
X info_message("No search pattern");
X return;
X }
X
X i = index_point;
X
X glob_name(subject_search_string, buf);
X
X#ifndef REGEXP
X if ((re = regcmp(buf, NULL)) == NULL) {
X#else /* REGEXP */
X if ((re = regcomp (buf)) == NULL) {
X#endif /* REGEXP */
X info_message("Bad search pattern");
X return;
X }
X
X do {
X if (forward)
X i++;
X else
X i--;
X
X if (i >= top_base)
X i = 0;
X if (i < 0)
X i = top_base - 1;
X
X#ifndef REGEXP
X if (regex(re, arts[ base[i] ].subject) != NULL) {
X#else /* REGEXP */
X if (regexec (re, arts[ base[i] ].subject) != NULL) {
X#endif /* REGEXP */
X if (i >= first_subj_on_screen
X && i < last_subj_on_screen) {
X erase_subject_arrow();
X index_point = i;
X draw_subject_arrow();
X } else {
X index_point = i;
X show_group_page(group, TRUE);
X }
X return;
X }
X } while (i != index_point);
X
X info_message("No match");
X}
X
X
X/*
X * Post an original article (not a followup)
X */
X
Xpost_base(group)
Xchar *group;
X{
X FILE *fp;
X char nam[100];
X char ch;
X char subj[LEN+1];
X char buf[200];
X int line;
X#ifdef GET_DIST
X char *ptr;
X#endif /* GET_DIST */
X
X if (!parse_string("Subject: ", subj))
X return(FALSE);
X if (subj[0] == '\0')
X return(FALSE);
X
X#ifdef USE_UID
X setuid(real_uid);
X setgid(real_gid);
X#endif /* USE_UID */
X
X sprintf(nam, "%s/.article", homedir);
X umask (omask);
X if ((fp = fopen(nam, "w")) == NULL) {
X umask (0);
X fprintf(stderr, "can't open %s: ", nam);
X perror("");
X return(FALSE);
X }
X#ifndef OSK
X chmod(nam, 0600);
X#else /* OSK */
X chmod(nam, 013);
X#endif /* OSK */
X
X fprintf(fp, "Subject: %s\n", subj);
X fprintf(fp, "Newsgroups: %s\n", group);
X#ifndef GET_DIST
X/* fprintf(fp, "Distribution: \n"); */
X line = 2;
X#else /* GET_DIST */
X strcpy (buf, group);
X if (ptr = index (buf, '.'))
X *ptr = '\0';
X fprintf(fp, "Distribution: %s\n", buf);
X line = 3;
X#endif /* GET_DIST */
X if (*my_org) {
X fprintf(fp, "Organization: %s\n", my_org);
X ++line;
X }
X fprintf(fp, "\n");
X ++line;
X
X#ifdef APPEND_SIG
X add_signature (fp);
X#endif /* APPEND_SIG */
X
X fclose(fp);
X umask (0);
X
X ch = 'e';
X while (1) {
X switch (ch) {
X case 'e':
X invoke_editor(nam, line);
X break;
X
X case 'a':
X return FALSE;
X
X case 'p':
X printf("\rPosting... ");
X CleartoEOLN ();
X#ifndef OSK
X sprintf(buf, "%s/inews -h < %s", LIBDIR, nam);
X#else /* OSK */
X sprintf(buf, "inews -h <%s", nam);
X#endif /* OSK */
X if (invoke_cmd(buf)) {
X printf("article posted ");
X fflush(stdout);
X goto post_base_done;
X } else {
X printf("article rejected ");
X fflush(stdout);
X break;
X }
X }
X
X do {
X MoveCursor(LINES, 0);
X fputs("abort, edit, post: ", stdout);
X fflush(stdout);
X ch = ReadCh();
X } while (ch != 'a' && ch != 'e' && ch != 'p');
X }
X
Xpost_base_done:
X#ifdef USE_UID
X setuid(tass_uid);
X setgid(tass_gid);
X#endif /* USE_UID */
X
X continue_prompt();
X
X return(TRUE);
X}
X
X
X/*
X * Return the number of unread articles there are within a thread
X */
X
Xnew_responses(thread)
Xint thread;
X{
X int i;
X int sum = 0;
X
X for (i = base[thread]; i >= 0; i = arts[i].thread)
X if (arts[i].unread)
X sum++;
X
X return sum;
X}
X
Xvoid
Xtass_group_help() {
X char ch;
X
Xgroup_help_start:
X
X ClearScreen();
X center_line(0, TASS_HEADER);
X center_line(1, "Index Page Commands (page 1 of 2)");
X
X MoveCursor(3, 0);
X
X printf("4 Select article 4\r\012");
X printf("^D, ^V half / full Page down\r\012");
X printf("^U, ^Z half / full Page up\r\012");
X printf("<CR> Read current article\r\012");
X printf("<TAB> View next unread article or group\r\012");
X printf("c Mark all articles as read\r\012");
X printf("g Choose a new group by name\r\012");
X printf("j Down a line\r\012");
X printf("k Up a line\r\012");
X printf("n Go to next group\r\012");
X printf("N Go to next unread article\r\012");
X printf("p Go to previous group\r\012");
X printf("P Go to previous unread article\r\012");
X printf("q Quit\r\012");
X
X center_line(LINES, "-- hit space for more commands --");
X ch = ReadCh();
X if (ch != ' ')
X return;
X
X ClearScreen();
X center_line(0, TASS_HEADER);
X center_line(1, "Index Page Commands (page 2 of 2)");
X
X MoveCursor(3, 0);
X
X printf("s Subscribe to this group\r\012");
X printf("t Return to group selection index\r\012");
X printf("u Unsubscribe to this group\r\012");
X printf("w Post an article\r\012");
X printf("/ Search forward for subject\r\012");
X printf("? Search backward for subject\r\012");
X printf("- Show last message\r\012");
X
X center_line(LINES, "-- hit any key --");
X ch = ReadCh();
X if (ch == 'b')
X goto group_help_start;
X}
__END__OF__THIS__FILE__
exit 0
: end of shell archive
--
Frank Kaefer # fkk@stasys.sta.sub.org # Starnberg, Germany
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads