Article View: de.comp.sources.os9
Article #13Tass for OS-9 Part01/03
From: 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
Technical Details
Message-ID:
<1hccj4INN76t@stasys.sta.sub.org>Path:
arc1-ca.pugleaf.net!sparky!uunet!elroy.jpl.nasa.gov!ames!sun-barr!news2me.EBay.Sun.COM!seven-up.East.Sun.COM!sungy!stasys!stasys!not-for-mailShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!sparky!uunet!elroy.jpl.nasa.gov!ames!sun-barr!news2me.EBay.Sun.COM!seven-up.East.Sun.COM!sungy!stasys!stasys!not-for-mail From: frank.kaefer@stasys.sta.sub.org (Frank Kaefer) Newsgroups: de.comp.sources.os9,comp.os.os9 Subject: Tass for OS-9 Part01/03 Message-ID: <1hccj4INN76t@stasys.sta.sub.org> Date: 24 Dec 92 13:05:40 GMT Sender: news@stasys.sta.sub.org Followup-To: de.comp.sources.d Organization: Stasys News Server, Starnberg, Germany Lines: 2372 Approved: frank.kaefer@stasys.sta.sub.org (Frank Kaefer) NNTP-Posting-Host: stasys.sta.sub.org