v23i072: TRN, version of RN that follows conversation threads, Part13/14
Rich Salz
rsalz at bbn.com
Tue Dec 4 07:28:23 AEST 1990
Submitted-by: Wayne Davison <davison at dri.com>
Posting-number: Volume 23, Issue 72
Archive-name: trn/part13
---- Cut Here and unpack ----
#!/bin/sh
# this is part 13 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file rthreads.c continued
#
CurArch=13
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
exit 1; fi
( read Scheck
if test "$Scheck" != $CurArch
then echo "Please unpack part $Scheck next!"
exit 1;
else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file rthreads.c"
sed 's/^X//' << 'SHAR_EOF' >> rthreads.c
X printf( "\n\nRead failed for thread data -- continuing unthreaded.\n" );
X fclose( fp_in );
X bzero( &total, sizeof (TOTAL) );
X return 0;
X }
X lp_bmap( &total.first, 4 );
X wp_bmap( &total.root, 5 );
X
X if( !read_item( &author_cnts, (MEM_SIZE)total.author * sizeof (WORD) )
X || !read_item( &strings, (MEM_SIZE)total.string1 )
X || !read_item( &subject_cnts, (MEM_SIZE)total.subject * sizeof (WORD) )
X || !read_item( &p_roots, (MEM_SIZE)total.root * sizeof (PACKED_ROOT) )
X || !read_item( &p_articles, (MEM_SIZE)total.article * sizeof (PACKED_ARTICLE) ) ) {
X printf( "\n\nRead failed for thread data -- continuing unthreaded.\n" );
X fclose( fp_in );
X unuse_data( 0 );
X return 0;
X }
X fclose( fp_in );
X
X if( !word_same || !long_same ) {
X wp_bmap( author_cnts, total.author );
X wp_bmap( subject_cnts, total.subject );
X for( i = 0; i < total.root; i++ ) {
X lp_bmap( &p_roots[i].root_num, 1 );
X wp_bmap( &p_roots[i].articles, 3 );
X }
X for( i = 0; i < total.article; i++ ) {
X lp_bmap( &p_articles[i].num, 2 );
X wp_bmap( &p_articles[i].subject, 8 );
X }
X }
X
X#ifndef lint
X author_ptrs = (char **)safemalloc( total.author * sizeof (char **) );
X subject_ptrs = (char **)safemalloc( total.subject * sizeof (char **) );
X root_subjects = (WORD *)safemalloc( total.root * sizeof (WORD) );
X root_article_cnts = (WORD *)safemalloc( total.root * sizeof (WORD) );
X#endif
X selected_roots = safemalloc( total.root * sizeof (char) );
X
X bzero( root_article_cnts, total.root * sizeof (WORD) );
X bzero( selected_roots, total.root * sizeof (char) );
X
X for( i = 0, ptr = strings; i < total.author; i++ ) {
X author_ptrs[i] = ptr;
X ptr += strlen( ptr ) + 1;
X }
X
X for( i = 0, j = 0; i < total.root; i++ ) {
X root_subjects[i] = j;
X k = p_roots[i].subject_cnt;
X while( k-- ) {
X root_article_cnts[i] += subject_cnts[j];
X subject_ptrs[j++] = ptr;
X ptr += strlen( ptr ) + 1;
X }
X if( saved_selections ) {
X for( k = 0; k < selected_root_cnt; k++ ) {
X if( p_roots[i].root_num == saved_selections[k] ) {
X selected_roots[i] = 1;
X }
X }
X }
X }
X count_roots( !saved_selections );
X Free( &saved_selections );
X select_page = 0;
X return 1;
X}
X
X/* A short-hand for reading a chunk of the file into a malloced array.
X*/
Xstatic int
Xread_item( dest, len )
Xchar **dest;
XMEM_SIZE len;
X{
X int ret;
X
X *dest = safemalloc( len );
X ret = fread( *dest, 1, (int)len, fp_in );
X if( ret != len ) {
X free( *dest );
X *dest = Nullch;
X return 0;
X }
X return 1;
X}
X
X/* Free some memory if it hasn't already been freed.
X*/
Xstatic void
XFree( pp )
Xchar **pp;
X{
X if( *pp ) {
X free( *pp );
X *pp = Nullch;
X }
X}
X
X/* Discard the thread data that we received through the use_data() call.
X** If "save_selections" is non-zero, we'll try to remember which roots
X** are currently selected long enough for the use_data() call to re-use
X** them. Only do this when you are going to re-open the same data file
X** immediately with use_data() (presumably because the data has been
X** updated while we were using it).
X*/
Xvoid
Xunuse_data( save_selections )
Xbool save_selections;
X{
X int i, j;
X
X if( save_selections ) {
X#ifndef lint
X saved_selections
X = (ART_NUM *)safemalloc( selected_root_cnt * sizeof (ART_NUM) );
X#endif
X for( i = 0, j = 0; i < total.root; i++ ) {
X if( selected_roots[i] && root_article_cnts[i] ) {
X saved_selections[j++] = p_roots[i].root_num;
X }
X }
X } else {
X selected_root_cnt = selected_count = 0;
X }
X Free( &p_roots );
X Free( &root_subjects );
X Free( &author_cnts );
X Free( &subject_cnts );
X Free( &author_ptrs );
X Free( &subject_ptrs );
X Free( &root_article_cnts );
X Free( &selected_roots );
X Free( &p_articles );
X Free( &strings );
X
X p_art = curr_p_art = Nullart;
X init_tree(); /* free any tree lines */
X
X bzero( &total, sizeof (TOTAL) );
X}
X
X/* Transform each WORD's byte-ordering in a buffer of the designated length.
X*/
Xstatic void
Xwp_bmap( buf, len )
XWORD *buf;
Xint len;
X{
X union {
X BYTE b[sizeof (WORD)];
X WORD w;
X } in, out;
X register int i;
X
X if( word_same ) {
X return;
X }
X while( len-- ) {
X in.w = *buf;
X for( i = 0; i < sizeof (WORD); i++ ) {
X out.b[my_bmap.w[i]] = in.b[mt_bmap.w[i]];
X }
X *buf++ = out.w;
X }
X}
X
X/* Transform each LONG's byte-ordering in a buffer of the designated length.
X*/
Xstatic void
Xlp_bmap( buf, len )
XLONG *buf;
Xint len;
X{
X union {
X BYTE b[sizeof (LONG)];
X LONG l;
X } in, out;
X register int i;
X
X if( long_same ) {
X return;
X }
X while( len-- ) {
X in.l = *buf;
X for( i = 0; i < sizeof (LONG); i++ ) {
X out.b[my_bmap.l[i]] = in.b[mt_bmap.l[i]];
X }
X *buf++ = out.l;
X }
X}
X
X#endif /* USETHREADS */
SHAR_EOF
echo "File rthreads.c is complete"
chmod 0660 rthreads.c || echo "restore of rthreads.c fails"
echo "x - extracting rthreads.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > rthreads.h &&
X/* $Header: rthreads.h,v 4.3.3.1 90/06/20 22:56:01 davison Trn $
X**
X** $Log: rthreads.h,v $
X** Revision 4.3.3.1 90/06/20 22:56:01 davison
X** Initial Trn Release
X**
X*/
X
X#include "threads.h"
X
XEXT TOTAL total;
X
XEXT PACKED_ROOT *p_roots INIT(0);
XEXT WORD *root_subjects INIT(0);
XEXT WORD *author_cnts INIT(0);
XEXT WORD *subject_cnts INIT(0);
XEXT char **author_ptrs INIT(0);
XEXT char **subject_ptrs INIT(0);
XEXT PACKED_ARTICLE *p_articles INIT(0);
XEXT WORD *root_article_cnts INIT(0);
XEXT char *selected_roots INIT(0);
XEXT ART_NUM *saved_selections INIT(0);
XEXT bool unread_selector INIT(0);
X
XEXT PACKED_ARTICLE *p_art INIT(0);
XEXT PACKED_ARTICLE *curr_p_art INIT(0);
XEXT PACKED_ARTICLE *recent_p_art INIT(0);
X
XEXT int selected_root_cnt INIT(0);
XEXT ART_NUM selected_count INIT(0);
XEXT int unthreaded INIT(0);
XEXT int select_page;
XEXT bool scan_all_roots;
X
XEXT bool word_same, long_same;
XEXT BMAP my_bmap, mt_bmap;
X
Xvoid thread_init(), mybytemap();
Xchar *thread_name(), *safemalloc();
Xint use_data();
Xvoid unuse_data();
Xvoid find_article(), init_tree(), entire_tree();
Xint tree_puts(), finish_tree();
Xvoid first_art(), follow_thread(), next_root(), prev_root();
Xchar select_thread();
Xint count_roots(), count_one_root();
XPACKED_ARTICLE *upper_limit();
X
X#define Nullart Null(PACKED_ARTICLE*)
SHAR_EOF
chmod 0660 rthreads.h || echo "restore of rthreads.h fails"
echo "x - extracting search.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > search.c &&
X/* $Header: search.c,v 4.3.2.2 90/03/22 23:05:31 sob Exp $
X *
X * $Log: search.c,v $
X * Revision 4.3.2.2 90/03/22 23:05:31 sob
X * Fixes provided by Wayne Davison <drivax!davison>
X *
X * Revision 4.3.2.1 90/03/17 17:46:29 sob
X * Added changes to insure that null search strings won't result in core dumps
X * on non-VAX computers.
X *
X * Revision 4.3 85/05/01 11:50:16 lwall
X * Baseline for release with 4.3bsd.
X *
X */
X
X/* string search routines */
X
X/* Copyright (c) 1981,1980 James Gosling */
X
X/* Modified Aug. 12, 1981 by Tom London to include regular expressions
X as in ed. RE stuff hacked over by jag to correct a few major problems,
X mainly dealing with searching within the buffer rather than copying
X each line to a separate array. Newlines can now appear in RE's */
X
X/* Ripped to shreds and glued back together to make a search package,
X * July 6, 1984, by Larry Wall. (If it doesn't work, it's probably my fault.)
X * Changes include:
X * Buffer, window, and mlisp stuff gone.
X * Translation tables reduced to 1 table.
X * Expression buffer is now dynamically allocated.
X * Character classes now implemented with a bitmap.
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "INTERN.h"
X#include "search.h"
X
X#ifndef BITSPERBYTE
X#define BITSPERBYTE 8
X#endif
X
X#define BMAPSIZ (127 / BITSPERBYTE + 1)
X
X/* meta characters in the "compiled" form of a regular expression */
X#define CBRA 2 /* \( -- begin bracket */
X#define CCHR 4 /* a vanilla character */
X#define CDOT 6 /* . -- match anything except a newline */
X#define CCL 8 /* [...] -- character class */
X#define NCCL 10 /* [^...] -- negated character class */
X#define CDOL 12 /* $ -- matches the end of a line */
X#define CEND 14 /* The end of the pattern */
X#define CKET 16 /* \) -- close bracket */
X#define CBACK 18 /* \N -- backreference to the Nth bracketed
X string */
X#define CIRC 20 /* ^ matches the beginning of a line */
X
X#define WORD 32 /* matches word character \w */
X#define NWORD 34 /* matches non-word characer \W */
X#define WBOUND 36 /* matches word boundary \b */
X#define NWBOUND 38 /* matches non-(word boundary) \B */
X
X#define STAR 01 /* * -- Kleene star, repeats the previous
X REas many times as possible; the value
X ORs with the other operator types */
X
X#define ASCSIZ 0200
Xtypedef char TRANSTABLE[ASCSIZ];
X
Xstatic TRANSTABLE trans = {
X0000,0001,0002,0003,0004,0005,0006,0007,
X0010,0011,0012,0013,0014,0015,0016,0017,
X0020,0021,0022,0023,0024,0025,0026,0027,
X0030,0031,0032,0033,0034,0035,0036,0037,
X0040,0041,0042,0043,0044,0045,0046,0047,
X0050,0051,0052,0053,0054,0055,0056,0057,
X0060,0061,0062,0063,0064,0065,0066,0067,
X0070,0071,0072,0073,0074,0075,0076,0077,
X0100,0101,0102,0103,0104,0105,0106,0107,
X0110,0111,0112,0113,0114,0115,0116,0117,
X0120,0121,0122,0123,0124,0125,0126,0127,
X0130,0131,0132,0133,0134,0135,0136,0137,
X0140,0141,0142,0143,0144,0145,0146,0147,
X0150,0151,0152,0153,0154,0155,0156,0157,
X0160,0161,0162,0163,0164,0165,0166,0167,
X0170,0171,0172,0173,0174,0175,0176,0177,
X};
Xstatic bool folding = FALSE;
X
Xstatic int err;
Xstatic char *FirstCharacter;
X
Xvoid
Xsearch_init()
X{
X#ifdef UNDEF
X register int i;
X
X for (i = 0; i < ASCSIZ; i++)
X trans[i] = i;
X#else
X ;
X#endif
X}
X
Xvoid
Xinit_compex(compex)
Xregister COMPEX *compex;
X{
X /* the following must start off zeroed */
X
X compex->eblen = 0;
X compex->brastr = Nullch;
X}
X
Xvoid
Xfree_compex(compex)
Xregister COMPEX *compex;
X{
X if (compex->eblen) {
X free(compex->expbuf);
X compex->eblen = 0;
X }
X if (compex->brastr) {
X free(compex->brastr);
X compex->brastr = Nullch;
X }
X}
X
Xstatic char *gbr_str = Nullch;
Xstatic int gbr_siz = 0;
X
Xchar *
Xgetbracket(compex,n)
Xregister COMPEX *compex;
Xint n;
X{
X int length = compex->braelist[n] - compex->braslist[n];
X
X if (!compex->nbra || n > compex->nbra || !compex->braelist[n] || length<0)
X return nullstr;
X growstr(&gbr_str, &gbr_siz, length+1);
X safecpy(gbr_str, compex->braslist[n], length+1);
X return gbr_str;
X}
X
Xvoid
Xcase_fold(which)
Xint which;
X{
X register int i;
X
X if (which != folding) {
X if (which) {
X for (i = 'A'; i <= 'Z'; i++)
X trans[i] = tolower(i);
X }
X else {
X for (i = 'A'; i <= 'Z'; i++)
X trans[i] = i;
X }
X folding = which;
X }
X}
X
X/* Compile the given regular expression into a [secret] internal format */
X
Xchar *
Xcompile (compex, strp, RE, fold)
Xregister COMPEX *compex;
Xregister char *strp;
Xint RE;
Xint fold;
X{
X register int c;
X register char *ep;
X char *lastep;
X char bracket[NBRA],
X *bracketp;
X char **alt = compex->alternatives;
X char *retmes = "Badly formed search string";
X
X case_fold(compex->do_folding = fold);
X if (!compex->eblen) {
X compex->expbuf = safemalloc(84);
X compex->eblen = 80;
X }
X ep = compex->expbuf; /* point at expression buffer */
X *alt++ = ep; /* first alternative starts here */
X bracketp = bracket; /* first bracket goes here */
X if (*strp == 0) { /* nothing to compile? */
X if (*ep == 0) /* nothing there yet? */
X return "Null search string";
X return Nullch; /* just keep old expression */
X }
X compex->nbra = 0; /* no brackets yet */
X lastep = 0;
X for (;;) {
X if (ep - compex->expbuf >= compex->eblen)
X grow_eb(compex);
X c = *strp++; /* fetch next char of pattern */
X if (c == 0) { /* end of pattern? */
X if (bracketp != bracket) { /* balanced brackets? */
X#ifdef VERBOSE
X retmes = "Unbalanced parens";
X#endif
X goto cerror;
X }
X *ep++ = CEND; /* terminate expression */
X *alt++ = 0; /* terminal alternative list */
X /*
X compex->eblen = ep - compex->expbuf + 1;
X compex->expbuf = saferealloc(compex->expbuf,compex->eblen+4); */
X return Nullch; /* return success */
X }
X if (c != '*')
X lastep = ep;
X if (!RE) { /* just a normal search string? */
X *ep++ = CCHR; /* everything is a normal char */
X *ep++ = c;
X }
X else /* it is a regular expression */
X switch (c) {
X
X case '\\': /* meta something */
X switch (c = *strp++) {
X case '(':
X if (compex->nbra >= NBRA) {
X#ifdef VERBOSE
X retmes = "Too many parens";
X#endif
X goto cerror;
X }
X *bracketp++ = ++compex->nbra;
X *ep++ = CBRA;
X *ep++ = compex->nbra;
X break;
X case '|':
X if (bracketp>bracket) {
X#ifdef VERBOSE
X retmes = "No \\| in parens"; /* Alas! */
X#endif
X goto cerror;
X }
X *ep++ = CEND;
X *alt++ = ep;
X break;
X case ')':
X if (bracketp <= bracket) {
X#ifdef VERBOSE
X retmes = "Unmatched right paren";
X#endif
X goto cerror;
X }
X *ep++ = CKET;
X *ep++ = *--bracketp;
X break;
X case 'w':
X *ep++ = WORD;
X break;
X case 'W':
X *ep++ = NWORD;
X break;
X case 'b':
X *ep++ = WBOUND;
X break;
X case 'B':
X *ep++ = NWBOUND;
X break;
X case '0': case '1': case '2': case '3': case '4':
X case '5': case '6': case '7': case '8': case '9':
X *ep++ = CBACK;
X *ep++ = c - '0';
X break;
X default:
X *ep++ = CCHR;
X if (c == '\0')
X goto cerror;
X *ep++ = c;
X break;
X }
X break;
X case '.':
X *ep++ = CDOT;
X continue;
X
X case '*':
X if (lastep == 0 || *lastep == CBRA || *lastep == CKET
X || *lastep == CIRC
X || (*lastep&STAR)|| *lastep>NWORD)
X goto defchar;
X *lastep |= STAR;
X continue;
X
X case '^':
X if (ep != compex->expbuf && ep[-1] != CEND)
X goto defchar;
X *ep++ = CIRC;
X continue;
X
X case '$':
X if (*strp != 0 && (*strp != '\\' || strp[1] != '|'))
X goto defchar;
X *ep++ = CDOL;
X continue;
X
X case '[': { /* character class */
X register int i;
X
X if (ep - compex->expbuf >= compex->eblen - BMAPSIZ)
X grow_eb(compex); /* reserve bitmap */
X for (i = BMAPSIZ; i; --i)
X ep[i] = 0;
X
X if ((c = *strp++) == '^') {
X c = *strp++;
X *ep++ = NCCL; /* negated */
X }
X else
X *ep++ = CCL; /* normal */
X
X i = 0; /* remember oldchar */
X do {
X if (c == '\0') {
X#ifdef VERBOSE
X retmes = "Missing ]";
X#endif
X goto cerror;
X }
X if (*strp == '-' && *(++strp))
X i = *strp++;
X else
X i = c;
X while (c <= i) {
X ep[c / BITSPERBYTE] |= 1 << (c % BITSPERBYTE);
X if (fold && isalpha(c))
X ep[(c ^ 32) / BITSPERBYTE] |=
X 1 << ((c ^ 32) % BITSPERBYTE);
X /* set the other bit too */
X c++;
X }
X } while ((c = *strp++) != ']');
X ep += BMAPSIZ;
X continue;
X }
X
X defchar:
X default:
X *ep++ = CCHR;
X *ep++ = c;
X }
X }
Xcerror:
X compex->expbuf[0] = 0;
X compex->nbra = 0;
X return retmes;
X}
X
Xvoid
Xgrow_eb(compex)
Xregister COMPEX *compex;
X{
X compex->eblen += 80;
X compex->expbuf = saferealloc(compex->expbuf, (MEM_SIZE)compex->eblen + 4);
X}
X
Xchar *
Xexecute (compex, addr)
Xregister COMPEX *compex;
Xchar *addr;
X{
X register char *p1 = addr;
X register char *trt = trans;
X register int c;
X
X if (addr == Nullch || compex->expbuf == Nullch)
X return Nullch;
X if (compex->nbra) { /* any brackets? */
X for (c = 0; c <= compex->nbra; c++)
X compex->braslist[c] = compex->braelist[c] = Nullch;
X if (compex->brastr)
X free(compex->brastr);
X compex->brastr = savestr(p1); /* in case p1 is not static */
X p1 = compex->brastr; /* ! */
X }
X case_fold(compex->do_folding); /* make sure table is correct */
X FirstCharacter = p1; /* for ^ tests */
X if (compex->expbuf[0] == CCHR && !compex->alternatives[1]) {
X c = trt[compex->expbuf[1]]; /* fast check for first character */
X do {
X if (trt[*p1] == c && advance (compex, p1, compex->expbuf))
X return p1;
X p1++;
X } while (*p1 && !err);
X return Nullch;
X }
X else { /* regular algorithm */
X do {
X register char **alt = compex->alternatives;
X while (*alt) {
X if (advance (compex, p1, *alt++))
X return p1;
X }
X p1++;
X } while (*p1 && !err);
X return Nullch;
X }
X}
X
X/* advance the match of the regular expression starting at ep along the
X string lp, simulates an NDFSA */
Xbool
Xadvance (compex, lp, ep)
Xregister COMPEX *compex;
Xregister char *ep;
Xregister char *lp;
X{
X register char *curlp;
X register char *trt = trans;
X register int i;
X
X while ((*ep & STAR) || *lp || *ep == CIRC || *ep == CKET)
X switch (*ep++) {
X
X case CCHR:
X if (trt[*ep++] != trt[*lp]) return FALSE;
X lp++;
X continue;
X
X case CDOT:
X if (*lp == '\n') return FALSE;
X lp++;
X continue;
X
X case CDOL:
X if (!*lp || *lp == '\n')
X continue;
X return FALSE;
X
X case CIRC:
X if (lp == FirstCharacter || lp[-1]=='\n')
X continue;
X return FALSE;
X
X case WORD:
X if (isalnum(*lp)) {
X lp++;
X continue;
X }
X return FALSE;
X
X case NWORD:
X if (!isalnum(*lp)) {
X lp++;
X continue;
X }
X return FALSE;
X
X case WBOUND:
X if ((lp == FirstCharacter || !isalnum(lp[-1])) !=
X (!*lp || !isalnum(*lp)) )
X continue;
X return FALSE;
X
X case NWBOUND:
X if ((lp == FirstCharacter || !isalnum(lp[-1])) ==
X (!*lp || !isalnum(*lp)))
X continue;
X return FALSE;
X
X case CEND:
X return TRUE;
X
X case CCL:
X if (cclass (ep, *lp, 1)) {
X ep += BMAPSIZ;
X lp++;
X continue;
X }
X return FALSE;
X
X case NCCL:
X if (cclass (ep, *lp, 0)) {
X ep += BMAPSIZ;
X lp++;
X continue;
X }
X return FALSE;
X
X case CBRA:
X compex->braslist[*ep++] = lp;
X continue;
X
X case CKET:
X i = *ep++;
X compex->braelist[i] = lp;
X compex->braelist[0] = lp;
X compex->braslist[0] = compex->braslist[i];
X continue;
X
X case CBACK:
X if (compex->braelist[i = *ep++] == 0) {
X fputs("bad braces\n",stdout) FLUSH;
X err = TRUE;
X return FALSE;
X }
X if (backref (compex, i, lp)) {
X lp += compex->braelist[i] - compex->braslist[i];
X continue;
X }
X return FALSE;
X
X case CBACK | STAR:
X if (compex->braelist[i = *ep++] == 0) {
X fputs("bad braces\n",stdout) FLUSH;
X err = TRUE;
X return FALSE;
X }
X curlp = lp;
X while (backref (compex, i, lp)) {
X lp += compex->braelist[i] - compex->braslist[i];
X }
X while (lp >= curlp) {
X if (advance (compex, lp, ep))
X return TRUE;
X lp -= compex->braelist[i] - compex->braslist[i];
X }
X continue;
X
X case CDOT | STAR:
X curlp = lp;
X while (*lp++ && lp[-1] != '\n');
X goto star;
X
X case WORD | STAR:
X curlp = lp;
X while (*lp++ && isalnum(lp[-1]));
X goto star;
X
X case NWORD | STAR:
X curlp = lp;
X while (*lp++ && !isalnum(lp[-1]));
X goto star;
X
X case CCHR | STAR:
X curlp = lp;
X while (*lp++ && trt[lp[-1]] == trt[*ep]);
X ep++;
X goto star;
X
X case CCL | STAR:
X case NCCL | STAR:
X curlp = lp;
X while (*lp++ && cclass (ep, lp[-1], ep[-1] == (CCL | STAR)));
X ep += BMAPSIZ;
X goto star;
X
X star:
X do {
X lp--;
X if (advance (compex, lp, ep))
X return TRUE;
X } while (lp > curlp);
X return FALSE;
X
X default:
X fputs("Badly compiled pattern\n",stdout) FLUSH;
X err = TRUE;
X return -1;
X }
X if (*ep == CEND || *ep == CDOL) {
X return TRUE;
X }
X return FALSE;
X}
X
Xbool
Xbackref (compex, i, lp)
Xregister COMPEX *compex;
Xregister int i;
Xregister char *lp;
X{
X register char *bp;
X
X bp = compex->braslist[i];
X while (*lp && *bp == *lp) {
X bp++;
X lp++;
X if (bp >= compex->braelist[i])
X return TRUE;
X }
X return FALSE;
X}
X
Xbool
Xcclass (set, c, af)
Xregister char *set;
Xregister int c;
X{
X c &= 0177;
X#if BITSPERBYTE == 8
X if (set[c >> 3] & 1 << (c & 7))
X#else
X if (set[c / BITSPERBYTE] & 1 << (c % BITSPERBYTE))
X#endif
X return af;
X return !af;
X}
SHAR_EOF
chmod 0660 search.c || echo "restore of search.c fails"
echo "x - extracting search.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > search.h &&
X/* $Header: search.h,v 4.3 85/05/01 11:50:46 lwall Exp $
X *
X * $Log: search.h,v $
X * Revision 4.3 85/05/01 11:50:46 lwall
X * Baseline for release with 4.3bsd.
X *
X */
X
X#ifndef NBRA
X#define NBRA 10 /* the maximum number of meta-brackets in an
X RE -- \( \) */
X#define NALTS 10 /* the maximum number of \|'s */
X
Xtypedef struct {
X char *expbuf; /* The compiled search string */
X int eblen; /* Length of above buffer */
X char *alternatives[NALTS]; /* The list of \| seperated alternatives */
X char *braslist[NBRA]; /* RE meta-bracket start list */
X char *braelist[NBRA]; /* RE meta-bracket end list */
X char *brastr; /* saved match string after execute() */
X char nbra; /* The number of meta-brackets int the most
X recenlty compiled RE */
X bool do_folding; /* fold upper and lower case? */
X} COMPEX;
X
Xvoid search_init();
Xvoid init_compex();
Xvoid free_compex();
Xchar *getbracket();
Xvoid case_fold();
Xchar *compile();
Xvoid grow_eb();
Xchar *execute();
Xbool advance();
Xbool backref();
Xbool cclass();
X#endif
SHAR_EOF
chmod 0660 search.h || echo "restore of search.h fails"
echo "x - extracting sw.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > sw.c &&
X/* $Header: sw.c,v 4.3.3.1 90/06/20 22:40:11 davison Trn $
X *
X * $Log: sw.c,v $
X * Revision 4.3.3.1 90/06/20 22:40:11 davison
X * Initial Trn Release
X *
X * Revision 4.3.2.3 90/05/08 22:06:00 sob
X * Added quick startup (-q) flag.
X *
X * Revision 4.3.2.2 90/03/22 23:05:34 sob
X * Fixes provided by Wayne Davison <drivax!davison>
X *
X * Revision 4.3.2.1 89/12/09 00:52:40 sob
X * Now handles SIGWINCH correctly.
X *
X * Revision 4.3.1.2 85/05/21 13:36:23 lwall
X * Sped up "rn -c" by not doing unnecessary initialization.
X *
X * Revision 4.3.1.1 85/05/10 11:40:38 lwall
X * Branch for patches.
X *
X * Revision 4.3 85/05/01 11:50:54 lwall
X * Baseline for release with 4.3bsd.
X *
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "head.h"
X#include "only.h"
X#include "term.h"
X#include "ng.h"
X#include "intrp.h"
X#include "INTERN.h"
X#include "sw.h"
X
Xvoid
Xsw_init(argc,argv,tcbufptr)
Xint argc;
Xchar *argv[];
Xchar **tcbufptr;
X{
X register int i;
X
X if (argc >= 2 && strEQ(argv[1],"-c"))
X checkflag=TRUE; /* so we can optimize for -c */
X interp(*tcbufptr,1024,GLOBINIT);
X sw_file(tcbufptr,FALSE);
X safecpy(*tcbufptr,getenv("RNINIT"),1024);
X if (**tcbufptr) {
X if (**tcbufptr == '/') {
X sw_file(tcbufptr,TRUE);
X }
X else
X sw_list(*tcbufptr);
X }
X
X for (i = 1; i < argc; i++)
X decode_switch(argv[i]);
X}
X
Xvoid
Xsw_file(tcbufptr,bleat)
Xchar **tcbufptr;
Xbool bleat;
X{
X int initfd = open(*tcbufptr,0);
X
X if (initfd >= 0) {
X fstat(initfd,&filestat);
X if (filestat.st_size > 1024)
X *tcbufptr = saferealloc(*tcbufptr,(MEM_SIZE)filestat.st_size);
X if (filestat.st_size) {
X read(initfd,*tcbufptr,(int)filestat.st_size);
X (*tcbufptr)[filestat.st_size-1] = '\0';
X /* wipe out last newline */
X sw_list(*tcbufptr);
X }
X else
X **tcbufptr = '\0';
X close(initfd);
X }
X else {
X if (bleat)
X printf(cantopen,*tcbufptr) FLUSH;
X **tcbufptr = '\0';
X }
X}
X
X/* decode a list of space separated switches */
X
Xvoid
Xsw_list(swlist)
Xchar *swlist;
X{
X char *tmplist = safemalloc((MEM_SIZE) strlen(swlist) + 2);
X /* semi-automatic string */
X register char *p, inquote = 0;
X
X strcpy(tmplist,swlist);
X for (p=tmplist; isspace(*p); p++) ; /* skip any initial spaces */
X while (*p) { /* "String, or nothing" */
X if (!inquote && isspace(*p)) { /* word delimiter? */
X *p++ = '\0'; /* chop here */
X while (isspace(*p)) /* these will be ignored later */
X p++;
X }
X else if (inquote == *p) {
X strcpy(p,p+1); /* delete trailing quote */
X inquote = 0; /* no longer quoting */
X }
X else if (!inquote && *p == '"' || *p == '\'') {
X /* OK, I know when I am not wanted */
X inquote = *p; /* remember single or double */
X strcpy(p,p+1); /* delete the quote */
X } /* (crude, but effective) */
X else if (*p == '\\') { /* quoted something? */
X if (p[1] == '\n') /* newline? */
X strcpy(p,p+2); /* "I didn't see anything" */
X else {
X strcpy(p,p+1); /* delete the backwhack */
X p++; /* leave the whatever alone */
X }
X }
X else
X p++; /* normal char, leave it alone */
X }
X *++p = '\0'; /* put an extra null on the end */
X if (inquote)
X printf("Unmatched %c in switch\n",inquote) FLUSH;
X for (p = tmplist; *p; /* p += strlen(p)+1 */ ) {
X decode_switch(p);
X while (*p++) ; /* point at null + 1 */
X }
X free(tmplist); /* this oughta be in Ada */
X}
X
X/* decode a single switch */
X
Xvoid
Xdecode_switch(s)
Xregister char *s;
X{
X while (isspace(*s)) /* ignore leading spaces */
X s++;
X#ifdef DEBUGGING
X if (debug)
X printf("Switch: %s\n",s) FLUSH;
X#endif
X if (*s != '-' && *s != '+') { /* newsgroup pattern */
X setngtodo(s);
X }
X else { /* normal switch */
X bool upordown = *s == '-' ? TRUE : FALSE;
X char tmpbuf[LBUFLEN];
X
X s++;
X switch (*s) {
X#ifdef TERMMOD
X case '=': {
X char *beg = s+1;
X
X while (*s && *s != '-' && *s != '+') s++;
X cpytill(tmpbuf,beg,*s);
X if (upordown ? strEQ(getenv("TERM"),tmpbuf)
X : strNE(getenv("TERM"),tmpbuf) ) {
X decode_switch(s);
X }
X break;
X }
X#endif
X#ifdef BAUDMOD
X case '0': case '1': case '2': case '3': case '4':
X case '5': case '6': case '7': case '8': case '9':
X if (upordown ? (just_a_sec*10 <= atoi(s))
X : (just_a_sec*10 >= atoi(s)) ) {
X while (isdigit(*s)) s++;
X decode_switch(s);
X }
X break;
X#endif
X case '/':
X if (checkflag)
X break;
X#ifdef SETENV
X setenv("SAVEDIR", upordown ? "%p/%c" : "%p" );
X setenv("SAVENAME", upordown ? "%a" : "%^C");
X#else
X notincl("-/");
X#endif
X break;
X case 'c':
X checkflag = upordown;
X break;
X case 'C':
X s++;
X if (*s == '=') s++;
X docheckwhen = atoi(s);
X break;
X case 'd': {
X if (checkflag)
X break;
X s++;
X if (*s == '=') s++;
X if (cwd) {
X chdir(cwd);
X free(cwd);
X }
X cwd = savestr(s);
X break;
X }
X#ifdef DEBUGGING
X case 'D':
X s++;
X if (*s == '=') s++;
X if (*s)
X if (upordown)
X debug |= atoi(s);
X else
X debug &= ~atoi(s);
X else
X if (upordown)
X debug |= 1;
X else
X debug = 0;
X break;
X#endif
X case 'e':
X erase_screen = upordown;
X break;
X case 'E':
X#ifdef SETENV
X s++;
X if (*s == '=')
X s++;
X strcpy(tmpbuf,s);
X s = index(tmpbuf,'=');
X if (s) {
X *s++ = '\0';
X setenv(tmpbuf,s);
X }
X else
X setenv(tmpbuf,nullstr);
X#else
X notincl("-E");
X#endif
X break;
X case 'F':
X s++;
X indstr = savestr(s);
X break;
X#ifdef INNERSEARCH
X case 'g':
X gline = atoi(s+1)-1;
X break;
X#endif
X case 'H':
X case 'h': {
X register int len, i;
X char *t;
X int flag = (*s == 'h' ? HT_HIDE : HT_MAGIC);
X
X if (checkflag)
X break;
X s++;
X len = strlen(s);
X for (t=s; *t; t++)
X if (isupper(*t))
X *t = tolower(*t);
X for (i=HEAD_FIRST; i<HEAD_LAST; i++)
X if (!len || strnEQ(s,htype[i].ht_name,len))
X if (upordown)
X htype[i].ht_flags |= flag;
X else
X htype[i].ht_flags &= ~flag;
X break;
X }
X case 'i':
X s++;
X if (*s == '=') s++;
X initlines = atoi(s);
X initlines_specified = TRUE;
X break;
X case 'l':
X muck_up_clear = upordown;
X break;
X case 'L':
X#ifdef CLEAREOL
X can_home_clear = upordown;
X#else
X notincl("-L");
X#endif
X break;
X case 'M':
X mbox_always = upordown;
X break;
X case 'm':
X s++;
X if (*s == '=') s++;
X if (!upordown)
X marking = NOMARKING;
X else if (*s == 'u')
X marking = UNDERLINE;
X else {
X marking = STANDOUT;
X }
X break;
X case 'N':
X norm_always = upordown;
X break;
X#ifdef VERBOSE
X case 'n':
X fputs("This isn't readnews. Don't use -n.\n\n",stdout) FLUSH;
X break;
X#endif
X case 'r':
X findlast = upordown;
X break;
X case 's':
X s++;
X if (*s == '=') s++;
X if (*s) {
X countdown = atoi(s);
X suppress_cn = FALSE;
X }
X else {
X if (!upordown)
X countdown = 5;
X suppress_cn = upordown;
X }
X break;
X case 'S':
X#ifdef ARTSEARCH
X s++;
X if (*s == '=') s++;
X if (*s)
X scanon = atoi(s);
X else
X scanon = upordown*3;
X#else
X notincl("-S");
X#endif
X break;
X case 't':
X#ifdef VERBOSE
X#ifdef TERSE
X verbose = !upordown;
X#else
X notincl("+t");
X#endif
X#else
X notincl("+t");
X#endif
X break;
X case 'T':
X typeahead = upordown;
X break;
X case 'v':
X#ifdef VERIFY
X verify = upordown;
X#else
X notincl("-v");
X#endif
X break;
X case 'x':
X#ifdef USETHREADS
X s++;
X if (*s == '=') s++;
X if (*s <= '9' && *s >= '0') {
X if ((max_tree_lines = atoi(s)) > 11)
X max_tree_lines = 11;
X do {
X s++;
X } while (*s <= '9' && *s >= '0');
X } else
X max_tree_lines = upordown*6;
X if (*s)
X strncpy(select_order, s, 3);
X if (mode == 'i')
X use_threads = upordown;
X else if (use_threads != upordown)
X printf("You must exit and restart to turn threaded operation %s.\n\n",
X upordown ? "on" : "off");
X#else
X notincl("-x");
X#endif
X break;
X case 'X':
X#ifdef USETHREADS
X s++;
X if (*s == '=') s++;
X if (*s <= '9' && *s >= '0') {
X select_on = atoi(s);
X do {
X s++;
X } while (*s <= '9' && *s >= '0');
X } else
X select_on = upordown;
X if (*s)
X end_select = *s++;
X if (*s)
X page_select = *s;
X#else
X notincl("-X");
X#endif
X break;
X /*
X * People want a way to avoid checking for new newsgroups on startup.
X */
X case 'q':
X quickstart = upordown;
X break;
X default:
X#ifdef VERBOSE
X IF(verbose)
X printf("\nIgnoring unrecognized switch: -%c\n", *s) FLUSH;
X ELSE
X#endif
X#ifdef TERSE
X printf("\nIgnoring -%c\n", *s) FLUSH;
X#endif
X break;
X }
X }
X}
X
X/* print current switch values */
X
Xvoid
Xpr_switches()
X{
X static char mp[2] = {'+','-'};
X register int i;
X
X fputs("\nCurrent switch settings:\n",stdout);
X printf("%c/ ", mp[strEQ(getval("SAVEDIR",SAVEDIR),"%p/%c")]);
X printf("%cc ", mp[checkflag]);
X printf("-C%d ", docheckwhen);
X printf("-d%s ", cwd);
X#ifdef DEBUGGING
X if (debug)
X printf("-D%d ", debug);
X#endif
X printf("%ce ", mp[erase_screen]);
X printf("-F\"%s\" ", indstr);
X#ifdef INNERSEARCH
X printf("-g%d", gline);
X#endif
X putchar('\n');
X#ifdef VERBOSE
X if (verbose) {
X for (i=HEAD_FIRST; i<HEAD_LAST; i++)
X printf("%ch%s%c",
X mp[htype[i].ht_flags & HT_HIDE], htype[i].ht_name,
X (! (i % 5) ? '\n' : ' ') );
X }
X#endif
X printf("-i%d ", initlines);
X printf("%cl ", mp[muck_up_clear]);
X#ifdef CLEAREOL
X printf("%cL ", mp[can_home_clear]);
X#endif /* CLEAREOL */
X if (marking)
X printf("-m%c ",marking==UNDERLINE?'u':'s');
X else
X printf("+m ");
X printf("%cM ", mp[mbox_always]);
X printf("%cN ", mp[norm_always]);
X printf("%cr ", mp[findlast]);
X if (countdown)
X printf("-s%d ", countdown);
X else
X printf("%cs ", mp[suppress_cn]);
X#ifdef ARTSEARCH
X if (scanon)
X printf("-S%d ",scanon);
X else
X printf("+S ");
X#endif
X#ifdef VERBOSE
X#ifdef TERSE
X printf("%ct ", mp[!verbose]);
X#endif
X#endif
X printf("%cT ", mp[typeahead]);
X#ifdef VERIFY
X printf("%cv ", mp[verify]);
X#endif
X#ifdef USETHREADS
X if (use_threads)
X printf("-x%d%s ",max_tree_lines,select_order);
X else
X printf("+x ");
X if (select_on)
X printf("-X%d%c%c ",select_on,end_select,page_select);
X else
X printf("+X ");
X#endif
X fputs("\n\n",stdout) FLUSH;
X#ifdef ONLY
X if (maxngtodo) {
X#ifdef VERBOSE
X IF(verbose)
X fputs("Current restriction:",stdout);
X ELSE
X#endif
X#ifdef TERSE
X fputs("Only:",stdout);
X#endif
X for (i=0; i<maxngtodo; i++)
X printf(" %s",ngtodo[i]);
X fputs("\n\n",stdout) FLUSH;
X }
X#ifdef VERBOSE
X else if (verbose)
X fputs("No restriction.\n\n",stdout) FLUSH;
X#endif
X#endif
X}
X
Xvoid
Xcwd_check()
X{
X char tmpbuf[LBUFLEN];
X
X if (!cwd)
X cwd = savestr(filexp("~/News"));
X strcpy(tmpbuf,cwd);
X if (chdir(cwd)) {
X safecpy(tmpbuf,filexp(cwd),sizeof tmpbuf);
X if (makedir(tmpbuf,MD_DIR) < 0 || chdir(tmpbuf) < 0) {
X interp(cmd_buf, (sizeof cmd_buf), "%~/News");
X if (makedir(cmd_buf,MD_DIR) < 0)
X strcpy(tmpbuf,homedir);
X else
X strcpy(tmpbuf,cmd_buf);
X chdir(tmpbuf);
X#ifdef VERBOSE
X IF(verbose)
X printf("\
XCannot make directory %s--\n\
X articles will be saved to %s\n\
X\n\
X",cwd,tmpbuf) FLUSH;
X ELSE
X#endif
X#ifdef TERSE
X printf("\
XCan't make %s--\n\
X using %s\n\
X\n\
X",cwd,tmpbuf) FLUSH;
X#endif
X }
X }
X free(cwd);
X getwd(tmpbuf);
X if (eaccess(tmpbuf,2)) {
X#ifdef VERBOSE
X IF(verbose)
X printf("\
XCurrent directory %s is not writeable--\n\
X articles will be saved to home directory\n\n\
X",tmpbuf) FLUSH;
X ELSE
X#endif
X#ifdef TERSE
X printf("%s not writeable--using ~\n\n",tmpbuf) FLUSH;
X#endif
X strcpy(tmpbuf,homedir);
X }
X cwd = savestr(tmpbuf);
X}
SHAR_EOF
chmod 0660 sw.c || echo "restore of sw.c fails"
echo "x - extracting sw.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > sw.h &&
X/* $Header: sw.h,v 4.3 85/05/01 11:51:07 lwall Exp $
X *
X * $Log: sw.h,v $
X * Revision 4.3 85/05/01 11:51:07 lwall
X * Baseline for release with 4.3bsd.
X *
X */
X
X#ifdef INNERSEARCH
XEXT int gline INIT(0);
X#endif
X
Xvoid sw_init();
Xvoid sw_file();
Xvoid sw_list();
Xvoid decode_switch();
Xvoid pr_switches();
Xvoid cwd_check();
SHAR_EOF
chmod 0660 sw.h || echo "restore of sw.h fails"
echo "x - extracting term.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > term.c &&
X/* $Header: term.c,v 4.3.3.1 90/07/28 18:09:09 davison Trn $
X *
X * $Log: term.c,v $
X * Revision 4.3.3.1 90/07/28 18:09:09 davison
X * Initial Trn Release
X *
X * Revision 4.3.2.7 90/04/21 16:54:29 sob
X * Installed patches provided by SCO for SCO Xenix
X *
X * Revision 4.3.2.6 90/04/13 23:48:17 sob
X * Modifications provided by Gene Hackney for 3b2.
X *
X * Revision 4.3.2.5 90/04/06 20:35:08 sob
X * Added fixes for SCO Xenix sent by ronald at robobar.co.uk.
X *
X * Revision 4.3.2.4 90/03/22 23:05:38 sob
X * Fixes provided by Wayne Davison <drivax!davison>
X *
X * Revision 4.3.2.3 89/11/28 01:51:58 sob
X * Now handles SIGWINCH correctly.
X *
X * Revision 4.3.2.2 89/11/27 01:31:34 sob
X * Altered NNTP code per ideas suggested by Bela Lubkin
X * <filbo at gorn.santa-cruz.ca.us>
X *
X * Revision 4.3.2.1 89/11/06 01:02:12 sob
X * Added RRN support from NNTP 1.5
X *
X * Revision 4.3.1.3 85/09/10 11:05:23 lwall
X * Improved %m in in_char().
X *
X * Revision 4.3.1.2 85/05/16 16:45:35 lwall
X * Forced \r to \n on input.
X * Fix for terminfo braindamage regarding bc emulation.
X *
X * Revision 4.3.1.1 85/05/10 11:41:03 lwall
X * Branch for patches.
X *
X * Revision 4.3 85/05/01 11:51:10 lwall
X * Baseline for release with 4.3bsd.
X *
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "final.h"
X#include "help.h"
X#include "cheat.h"
X#include "intrp.h"
X#include "INTERN.h"
X#include "term.h"
X
X#ifdef SYS_PTEM
X#include <sys/stream.h>
X#include <sys/ptem.h>
X#endif
X
Xchar ERASECH; /* rubout character */
Xchar KILLCH; /* line delete character */
Xchar tcarea[TCSIZE]; /* area for "compiled" termcap strings */
X
X#ifdef USETHREADS
Xint upcost;
X#endif
X
X/* guarantee capability pointer != Nullch */
X/* (I believe terminfo will ignore the &tmpaddr argument.) */
X
X#define Tgetstr(key) ((tmpstr = tgetstr(key,&tmpaddr)) ? tmpstr : nullstr)
X
X#ifdef PUSHBACK
Xstruct keymap {
X char km_type[128];
X union km_union {
X struct keymap *km_km;
X char *km_str;
X } km_ptr[128];
X};
X
X#define KM_NOTHIN 0
X#define KM_STRING 1
X#define KM_KEYMAP 2
X#define KM_BOGUS 3
X
X#define KM_TMASK 3
X#define KM_GSHIFT 4
X#define KM_GMASK 7
X
Xtypedef struct keymap KEYMAP;
X
XKEYMAP *topmap INIT(Null(KEYMAP*));
X
Xvoid mac_init();
XKEYMAP *newkeymap();
Xvoid show_keymap();
Xvoid pushstring();
X#endif
X
Xvoid line_col_calcs();
X
X/* terminal initialization */
X
Xvoid
Xterm_init()
X{
X savetty(); /* remember current tty state */
X
X#ifdef TERMIO
X ospeed = _tty.c_cflag & CBAUD; /* for tputs() */
X ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */
X KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */
X#else
X ospeed = _tty.sg_ospeed; /* for tputs() */
X ERASECH = _tty.sg_erase; /* for finish_command() */
X KILLCH = _tty.sg_kill; /* for finish_command() */
X#endif
X
X /* The following could be a table but I can't be sure that there isn't */
X /* some degree of sparsity out there in the world. */
X
X switch (ospeed) { /* 1 second of padding */
X#ifdef BEXTA
X case BEXTA: just_a_sec = 1920; break;
X#else
X#ifdef B19200
X case B19200: just_a_sec = 1920; break;
X#endif
X#endif
X case B9600: just_a_sec = 960; break;
X case B4800: just_a_sec = 480; break;
X case B2400: just_a_sec = 240; break;
X case B1800: just_a_sec = 180; break;
X case B1200: just_a_sec = 120; break;
X case B600: just_a_sec = 60; break;
X case B300: just_a_sec = 30; break;
X /* do I really have to type the rest of this??? */
X case B200: just_a_sec = 20; break;
X case B150: just_a_sec = 15; break;
X case B134: just_a_sec = 13; break;
X case B110: just_a_sec = 11; break;
X case B75: just_a_sec = 8; break;
X case B50: just_a_sec = 5; break;
X default: just_a_sec = 960; break;
X /* if we are running detached I */
X } /* don't want to know about it! */
X}
X
X/* set terminal characteristics */
X
Xvoid
Xterm_set(tcbuf)
Xchar *tcbuf; /* temp area for "uncompiled" termcap entry */
X{
X char *tmpaddr; /* must not be register */
X register char *tmpstr;
X char *tgetstr();
X char *s;
X int status;
X#ifdef TIOCGWINSZ
X#ifdef u3b2
Xstruct winsize {
X unsigned short ws_row; /* rows, in characters*/
X unsigned short ws_col; /* columns, in character */
X unsigned short ws_xpixel; /* horizontal size, pixels */
X unsigned short ws_ypixel; /* vertical size, pixels */
X};
X#endif
X struct winsize winsize;
X#endif
X
X#ifdef PENDING
X#if ! defined (FIONREAD) && ! defined (RDCHK)
X /* do no delay reads on something that always gets closed on exit */
X
X devtty = open("/dev/tty",0);
X if (devtty < 0) {
X printf(cantopen,"/dev/tty") FLUSH;
X finalize(1);
X }
X fcntl(devtty,F_SETFL,O_NDELAY);
X#endif
X#endif
X
X /* get all that good termcap stuff */
X
X#ifdef HAVETERMLIB
X status = tgetent(tcbuf,getenv("TERM")); /* get termcap entry */
X if (status < 1) {
X#ifdef VERBOSE
X printf("No termcap %s found.\n", status ? "file" : "entry") FLUSH;
X#else
X fputs("Termcap botch\n",stdout) FLUSH;
X#endif
X finalize(1);
X }
X tmpaddr = tcarea; /* set up strange tgetstr pointer */
X s = Tgetstr("pc"); /* get pad character */
X PC = *s; /* get it where tputs wants it */
X if (!tgetflag("bs")) { /* is backspace not used? */
X BC = Tgetstr("bc"); /* find out what is */
X if (BC == nullstr) /* terminfo grok's 'bs' but not 'bc' */
X BC = Tgetstr("le");
X } else
X BC = "\b"; /* make a backspace handy */
X UP = Tgetstr("up"); /* move up a line */
X if (!*UP) /* no UP string? */
X marking = 0; /* disable any marking */
X if (muck_up_clear) /* this is for weird HPs */
X CL = "\n\n\n\n";
X else
X CL = Tgetstr("cl"); /* get clear string */
X CE = Tgetstr("ce"); /* clear to end of line string */
X#if defined(CLEAREOL) || defined(USETHREADS)
X HO = Tgetstr("ho"); /* home cursor if no CM */
X CM = Tgetstr("cm"); /* cursor motion */
X if (*CM || *HO)
X can_home = TRUE;
X#endif
X#ifdef CLEAREOL
X CD = Tgetstr("cd"); /* clear to end of display */
X if (!*CE || !*CD || !can_home) /* can we CE, CD, and home? */
X can_home_clear = FALSE; /* no, so disable use of clear eol */
X#endif /* CLEAREOL */
X#ifdef USETHREADS
X upcost = strlen(UP);
X#endif
X SO = Tgetstr("so"); /* begin standout */
X SE = Tgetstr("se"); /* end standout */
X if ((SG = tgetnum("sg"))<0)
X SG = 0; /* blanks left by SG, SE */
X US = Tgetstr("us"); /* start underline */
X UE = Tgetstr("ue"); /* end underline */
X if ((UG = tgetnum("ug"))<0)
X UG = 0; /* blanks left by US, UE */
X if (*US)
X UC = nullstr; /* UC must not be NULL */
X else
X UC = Tgetstr("uc"); /* underline a character */
X if (!*US && !*UC) { /* no underline mode? */
X US = SO; /* substitute standout mode */
X UE = SE;
X UG = SG;
X }
X LINES = tgetnum("li"); /* lines per page */
X COLS = tgetnum("co"); /* columns on page */
X
X#ifdef TIOCGWINSZ
X { struct winsize ws;
X if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) {
X LINES = ws.ws_row;
X COLS = ws.ws_col;
X }
X }
X#endif
X
X AM = tgetflag("am"); /* terminal wraps automatically? */
X XN = tgetflag("xn"); /* then eats next newline? */
X VB = Tgetstr("vb");
X if (!*VB)
X VB = "\007";
X CR = Tgetstr("cr");
X if (!*CR) {
X if (tgetflag("nc") && *UP) {
X CR = safemalloc((MEM_SIZE)strlen(UP)+2);
X sprintf(CR,"%s\r",UP);
X }
X else
X CR = "\r";
X }
X#ifdef TIOCGWINSZ
X if (ioctl(1, TIOCGWINSZ, &winsize)>=0) {
X if (winsize.ws_row>0) LINES=winsize.ws_row;
X if (winsize.ws_col>0) COLS=winsize.ws_col;
X }
X#endif
X#else
X ?????? /* Roll your own... */
X#endif
X line_col_calcs();
X noecho(); /* turn off echo */
X crmode(); /* enter cbreak mode */
X
X#ifdef PUSHBACK
X mac_init(tcbuf);
X#endif
X}
X
X#ifdef PUSHBACK
Xvoid
Xmac_init(tcbuf)
Xchar *tcbuf;
X{
X char tmpbuf[1024];
X
X tmpfp = fopen(filexp(getval("RNMACRO",RNMACRO)),"r");
X if (tmpfp != Nullfp) {
X while (fgets(tcbuf,1024,tmpfp) != Nullch) {
X mac_line(tcbuf,tmpbuf,(sizeof tmpbuf));
X }
X fclose(tmpfp);
X }
X}
X
Xvoid
Xmac_line(line,tmpbuf,tbsize)
Xchar *line;
Xchar *tmpbuf;
Xint tbsize;
X{
X register char *s, *m;
X register KEYMAP *curmap;
X register int ch;
X register int garbage = 0;
X static char override[] = "\nkeymap overrides string\n";
X
X if (topmap == Null(KEYMAP*))
X topmap = newkeymap();
X if (*line == '#' || *line == '\n')
X return;
X if (line[ch = strlen(line)-1] == '\n')
X line[ch] = '\0';
X m = dointerp(tmpbuf,tbsize,line," \t");
X if (!*m)
X return;
X while (*m == ' ' || *m == '\t') m++;
X for (s=tmpbuf,curmap=topmap; *s; s++) {
X ch = *s & 0177;
X if (s[1] == '+' && isdigit(s[2])) {
X s += 2;
X garbage = (*s & KM_GMASK) << KM_GSHIFT;
X }
X else
X garbage = 0;
X if (s[1]) {
X if ((curmap->km_type[ch] & KM_TMASK) == KM_STRING) {
X fputs(override,stdout) FLUSH;
X free(curmap->km_ptr[ch].km_str);
X curmap->km_ptr[ch].km_str = Nullch;
X }
X curmap->km_type[ch] = KM_KEYMAP + garbage;
X if (curmap->km_ptr[ch].km_km == Null(KEYMAP*))
X curmap->km_ptr[ch].km_km = newkeymap();
X curmap = curmap->km_ptr[ch].km_km;
X }
X else {
X if ((curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP)
X fputs(override,stdout) FLUSH;
X else {
X curmap->km_type[ch] = KM_STRING + garbage;
X curmap->km_ptr[ch].km_str = savestr(m);
X }
X }
X }
X}
X
XKEYMAP*
Xnewkeymap()
X{
X register int i;
X register KEYMAP *map;
X
X#ifndef lint
X map = (KEYMAP*)safemalloc(sizeof(KEYMAP));
X#else
X map = Null(KEYMAP*);
X#endif /* lint */
X for (i=127; i>=0; --i) {
X map->km_ptr[i].km_km = Null(KEYMAP*);
X map->km_type[i] = KM_NOTHIN;
X }
X return map;
X}
X
Xvoid
Xshow_macros()
X{
X char prebuf[64];
X
X if (topmap != Null(KEYMAP*)) {
X print_lines("Macros:\n",STANDOUT);
X *prebuf = '\0';
X show_keymap(topmap,prebuf);
X }
X else {
X print_lines("No macros defined.\n", NOMARKING);
X }
X}
X
Xvoid
Xshow_keymap(curmap,prefix)
Xregister KEYMAP *curmap;
Xchar *prefix;
X{
X register int i;
X register char *next = prefix + strlen(prefix);
X register int kt;
X
X for (i=0; i<128; i++) {
X if (kt = curmap->km_type[i]) {
X if (i < ' ')
X sprintf(next,"^%c",i+64);
X else if (i == ' ')
X strcpy(next,"\\040");
X else if (i == 127)
X strcpy(next,"^?");
X else
X sprintf(next,"%c",i);
X if ((kt >> KM_GSHIFT) & KM_GMASK) {
X sprintf(cmd_buf,"+%d", (kt >> KM_GSHIFT) & KM_GMASK);
X strcat(next,cmd_buf);
X }
X switch (kt & KM_TMASK) {
X case KM_NOTHIN:
X sprintf(cmd_buf,"%s %c\n",prefix,i);
X print_lines(cmd_buf,NOMARKING);
X break;
X case KM_KEYMAP:
X show_keymap(curmap->km_ptr[(char)i].km_km, prefix);
X break;
X case KM_STRING:
X sprintf(cmd_buf,"%s %s\n",prefix,curmap->km_ptr[i].km_str);
X print_lines(cmd_buf,NOMARKING);
X break;
X case KM_BOGUS:
X sprintf(cmd_buf,"%s BOGUS\n",prefix);
X print_lines(cmd_buf,STANDOUT);
X break;
X }
X }
X }
X}
X
X#endif
X
X/* routine to pass to tputs */
X
Xchar
Xputchr(ch)
Xregister char ch;
X{
X putchar(ch);
X#ifdef lint
X ch = Null(char);
X ch = ch;
X#endif
X return((char) 0);
X}
X
X/* input the 2nd and succeeding characters of a multi-character command */
X/* returns TRUE if command finished, FALSE if they rubbed out first character */
X
Xbool
Xfinish_command(donewline)
Xint donewline;
X{
X register char *s;
X register bool quoteone = FALSE;
X
X s = buf;
X if (s[1] != FINISHCMD) /* someone faking up a command? */
X return TRUE;
X do {
X top:
X if ((unsigned char)*s < ' ') {
X putchar('^');
X putchar(*s | 64);
X }
X else if (*s == '\177') {
X putchar('^');
X putchar('?');
X }
X else
X putchar(*s); /* echo previous character */
X s++;
Xre_read:
X fflush(stdout);
X getcmd(s);
X if (quoteone) {
X quoteone = FALSE;
X continue;
X }
X if (errno || *s == Ctl('l')) {
X *s = Ctl('r'); /* force rewrite on CONT */
X }
X if (*s == '\033') { /* substitution desired? */
X#ifdef ESCSUBS
X char tmpbuf[4], *cpybuf;
X
X tmpbuf[0] = '%';
X read_tty(&tmpbuf[1],1);
X#ifdef RAWONLY
X tmpbuf[1] &= 0177;
X#endif
X tmpbuf[2] = '\0';
X if (tmpbuf[1] == 'h') {
X (void) help_subs();
X *s = '\0';
X reprint();
X goto re_read;
X }
X else if (tmpbuf[1] == '\033') {
X *s = '\0';
X cpybuf = savestr(buf);
X interp(buf, (sizeof buf), cpybuf);
X free(cpybuf);
X s = buf + strlen(buf);
X reprint();
X goto re_read;
X }
X else {
X interp(s,(sizeof buf) - (s-buf),tmpbuf);
X fputs(s,stdout);
X s += strlen(s);
X }
X goto re_read;
X#else
X notincl("^[");
X *s = '\0';
X reprint();
X goto re_read;
X#endif
X }
X else if (*s == ERASECH) { /* they want to rubout a char? */
X rubout();
X s--; /* discount the char rubbed out */
X if ((unsigned char)*s < ' ' || *s == '\177')
X rubout();
X if (s == buf) { /* entire string gone? */
X fflush(stdout); /* return to single char command mode */
X return FALSE;
X }
X else
X goto re_read;
X }
X else if (*s == KILLCH) { /* wipe out the whole line? */
X while (s-- != buf) { /* emulate that many ERASEs */
X rubout();
X if ((unsigned char)*s < ' ' || *s == '\177')
X rubout();
X }
X fflush(stdout);
X return FALSE; /* return to single char mode */
X }
X#ifdef WORDERASE
X else if (*s == Ctl('w')) { /* wipe out one word? */
X *s-- = ' ';
X while (!isspace(*s) || isspace(s[1])) {
X rubout();
X if (s-- == buf) {
X fflush(stdout);
X return FALSE; /* return to single char mode */
X }
X if ((unsigned char)*s < ' ' || *s == '\177')
X rubout();
X }
X s++;
X goto re_read;
X }
X#endif
X else if (*s == Ctl('r')) {
X *s = '\0';
X reprint();
X goto re_read;
X }
X else if (*s == Ctl('v')) {
X putchar('^');
X backspace();
X fflush(stdout);
X getcmd(s);
X goto top;
X }
X else if (*s == '\\') {
X quoteone = TRUE;
X }
X } while (*s != '\n'); /* till a newline (not echoed) */
X *s = '\0'; /* terminate the string nicely */
X if (donewline)
X putchar('\n') FLUSH;
X return TRUE; /* say we succeeded */
X}
X
X/* discard any characters typed ahead */
X
Xvoid
Xeat_typeahead()
X{
X#ifdef PUSHBACK
X if (!typeahead && nextin==nextout) /* cancel only keyboard stuff */
X#else
X if (!typeahead)
X#endif
X {
X#ifdef PENDING
X while (input_pending())
X read_tty(buf,sizeof(buf));
X#else /* this is probably v7 */
X ioctl(_tty_ch,TIOCSETP,&_tty);
X#endif
X }
X}
X
Xvoid
Xsettle_down()
X{
X dingaling();
X fflush(stdout);
X sleep(1);
X#ifdef PUSHBACK
X nextout = nextin; /* empty circlebuf */
X#endif
X eat_typeahead();
X}
X
X#ifdef PUSHBACK
X/* read a character from the terminal, with multi-character pushback */
X
Xint
Xread_tty(addr,size)
Xchar *addr;
Xint size;
X{
X if (nextout != nextin) {
X *addr = circlebuf[nextout++];
X nextout %= PUSHSIZE;
X return 1;
X }
X else {
X size = read(0,addr,size);
X#ifdef RAWONLY
X *addr &= 0177;
X#endif
X return size;
X }
X}
X
X#ifdef PENDING
X#if ! defined (FIONREAD) && ! defined (RDCHK)
Xint
Xcircfill()
X{
X register int Howmany = read(devtty,circlebuf+nextin,1);
X
X if (Howmany) {
X nextin += Howmany;
X nextin %= PUSHSIZE;
X }
X return Howmany;
X}
X#endif /* PENDING */
X#endif /* FIONREAD */
X
Xvoid
Xpushchar(c)
Xchar c;
X{
X nextout--;
X if (nextout < 0)
X nextout = PUSHSIZE - 1;
X if (nextout == nextin) {
X fputs("\npushback buffer overflow\n",stdout) FLUSH;
X sig_catcher(0);
X }
X circlebuf[nextout] = c;
X}
X
X#else /* PUSHBACK */
X#ifndef read_tty
X/* read a character from the terminal, with hacks for O_NDELAY reads */
X
Xint
Xread_tty(addr,size)
Xchar *addr;
Xint size;
X{
X if (is_input) {
X *addr = pending_ch;
X is_input = FALSE;
X return 1;
X }
X else {
X size = read(0,addr,size);
X#ifdef RAWONLY
X *addr &= 0177;
X#endif
X return size;
X }
X}
X#endif /* read_tty */
X#endif /* PUSHBACK */
X
X/* print an underlined string, one way or another */
X
Xvoid
Xunderprint(s)
Xregister char *s;
X{
X assert(UC);
X if (*UC) { /* char by char underline? */
X while (*s) {
X if ((unsigned char)*s < ' ') {
X putchar('^');
X backspace();/* back up over it */
X underchar();/* and do the underline */
X putchar(*s+64);
X backspace();/* back up over it */
X underchar();/* and do the underline */
X }
X else {
X putchar(*s);
X backspace();/* back up over it */
X underchar();/* and do the underline */
X }
X s++;
X }
X }
X else { /* start and stop underline */
X underline(); /* start underlining */
X while (*s) {
X if ((unsigned char)*s < ' ') {
X putchar('^');
X putchar(*s+64);
X }
X else
X putchar(*s);
X s++;
X }
X un_underline(); /* stop underlining */
X }
X}
X
X/* keep screen from flashing strangely on magic cookie terminals */
X
X#ifdef NOFIREWORKS
Xvoid
Xno_sofire()
X{
X if (*UP && *SE) { /* should we disable fireworks? */
X putchar('\n');
X un_standout();
X up_line();
X carriage_return();
X }
X}
X
Xvoid
Xno_ulfire()
X{
X if (*UP && *US) { /* should we disable fireworks? */
X putchar('\n');
X un_underline();
X up_line();
X carriage_return();
X }
X}
X#endif
X
X/* get a character into a buffer */
X
Xvoid
Xgetcmd(whatbuf)
Xregister char *whatbuf;
X{
X#ifdef PUSHBACK
X register KEYMAP *curmap;
X register int i;
X bool no_macros;
X int times = 0; /* loop detector */
X char scrchar;
X
Xtryagain:
X curmap = topmap;
X no_macros = (whatbuf != buf && nextin == nextout);
X#endif
X for (;;) {
X int_count = 0;
X errno = 0;
X if (read_tty(whatbuf,1) < 0)
X if (!errno)
X errno = EINTR;
X else {
X perror(readerr);
X sig_catcher(0);
X }
X#ifdef PUSHBACK
X if (*whatbuf & 0200 || no_macros) {
X *whatbuf &= 0177;
X goto got_canonical;
X }
X if (curmap == Null(KEYMAP*))
X goto got_canonical;
X for (i = (curmap->km_type[*whatbuf] >> KM_GSHIFT) & KM_GMASK; i; --i){
X read_tty(&scrchar,1);
X }
X switch (curmap->km_type[*whatbuf] & KM_TMASK) {
X case KM_NOTHIN: /* no entry? */
X if (curmap == topmap) /* unmapped canonical */
X goto got_canonical;
X settle_down();
X goto tryagain;
X case KM_KEYMAP: /* another keymap? */
X curmap = curmap->km_ptr[*whatbuf].km_km;
X assert(curmap != Null(KEYMAP*));
X break;
X case KM_STRING: /* a string? */
X pushstring(curmap->km_ptr[*whatbuf].km_str);
X if (++times > 20) { /* loop? */
X fputs("\nmacro loop?\n",stdout);
X settle_down();
X }
X no_macros = FALSE;
X goto tryagain;
X }
X#else
X#ifdef RAWONLY
X *whatbuf &= 0177;
X#endif
X break;
X#endif
X }
X
Xgot_canonical:
X#ifndef TERMIO
X if (*whatbuf == '\r')
X *whatbuf = '\n';
X#endif
X if (whatbuf == buf)
X whatbuf[1] = FINISHCMD; /* tell finish_command to work */
X}
X
X#ifdef PUSHBACK
Xvoid
Xpushstring(str)
Xchar *str;
X{
X register int i;
X char tmpbuf[PUSHSIZE];
X register char *s = tmpbuf;
X
X assert(str != Nullch);
X interp(s,PUSHSIZE,str);
X for (i = strlen(s)-1; i >= 0; --i) {
X s[i] ^= 0200;
X pushchar(s[i]);
X }
X}
X#endif
X
Xint
Xget_anything()
X{
X char tmpbuf[2];
X
Xreask_anything:
X unflush_output(); /* disable any ^O in effect */
X standout();
X#ifdef VERBOSE
X IF(verbose)
X fputs("[Type space to continue] ",stdout);
X ELSE
X#endif
X#ifdef TERSE
X fputs("[MORE] ",stdout);
X#endif
X un_standout();
X fflush(stdout);
X eat_typeahead();
X if (int_count) {
X return -1;
X }
X collect_subjects(); /* loads subject cache until */
X /* input is pending */
X getcmd(tmpbuf);
X if (errno || *tmpbuf == '\f') {
X putchar('\n') FLUSH; /* if return from stop signal */
X goto reask_anything; /* give them a prompt again */
X }
X if (*tmpbuf == 'h') {
X#ifdef VERBOSE
X IF(verbose)
X fputs("\nType q to quit or space to continue.\n",stdout) FLUSH;
X ELSE
X#endif
X#ifdef TERSE
X fputs("\nq to quit, space to continue.\n",stdout) FLUSH;
X#endif
X goto reask_anything;
X }
X else if (*tmpbuf != ' ' && *tmpbuf != '\n') {
X carriage_return();
X erase_eol(); /* erase the prompt */
X carriage_return();
X return *tmpbuf == 'q' ? -1 : *tmpbuf;
X }
X if (*tmpbuf == '\n') {
X page_line = LINES - 1;
X carriage_return();
X erase_eol();
X carriage_return();
X }
X else {
X page_line = 1;
X if (erase_screen) /* -e? */
X clear(); /* clear screen */
X else {
X carriage_return();
X erase_eol(); /* erase the prompt */
X carriage_return();
X }
X }
X return 0;
X}
X
X#ifdef USETHREADS
Xint
Xpause_getcmd()
X{
X unflush_output(); /* disable any ^O in effect */
X standout();
X#ifdef VERBOSE
X IF(verbose)
X fputs("[Type space or a command] ",stdout);
X ELSE
X#endif
X#ifdef TERSE
X fputs("[CMD] ",stdout);
X#endif
X un_standout();
X fflush(stdout);
X eat_typeahead();
X if (int_count) {
X return -1;
X }
X getcmd(buf);
X if (errno || *buf == '\f') {
X return 0; /* if return from stop signal */
X }
X else if (*buf != ' ') {
X carriage_return();
X erase_eol(); /* erase the prompt */
X carriage_return();
X return *buf;
X }
X return 0;
X}
X#endif
X
Xvoid
Xin_char(prompt, newmode)
Xchar *prompt;
Xchar newmode;
X{
X char oldmode = mode;
X
Xreask_in_char:
X unflush_output(); /* disable any ^O in effect */
X fputs(prompt,stdout);
X fflush(stdout);
X eat_typeahead();
X mode = newmode;
X getcmd(buf);
X if (errno || *buf == '\f') {
X putchar('\n') FLUSH; /* if return from stop signal */
X goto reask_in_char; /* give them a prompt again */
X }
X mode = oldmode;
X}
X
Xint
Xprint_lines(what_to_print,hilite)
Xchar *what_to_print;
Xint hilite;
X{
X register char *s;
X register int i;
X
X if (page_line < 0) /* they do not want to see this? */
X return -1;
X for (s=what_to_print; *s; ) {
X if (page_line >= LINES || int_count) {
X if (i = -1, int_count || (i = get_anything())) {
X page_line = -1; /* disable further print_lines */
X return i;
X }
X }
X page_line++;
X if (hilite == STANDOUT) {
X#ifdef NOFIREWORKS
X if (erase_screen)
X no_sofire();
X#endif
X standout();
X }
X else if (hilite == UNDERLINE) {
X#ifdef NOFIREWORKS
X if (erase_screen)
X no_ulfire();
X#endif
X underline();
X }
X for (i=0; i<COLS; i++) {
X if (!*s)
X break;
X if ((unsigned char)*s >= ' ')
X putchar(*s);
X else if (*s == '\t') {
X putchar(*s);
X i = ((i+8) & ~7) - 1;
X }
X else if (*s == '\n') {
X i = 32000;
X }
X else {
X i++;
X putchar('^');
X putchar(*s + 64);
X }
X s++;
X }
X if (i) {
X if (hilite == STANDOUT)
X un_standout();
X else if (hilite == UNDERLINE)
X un_underline();
X if (AM && i == COLS)
X fflush(stdout);
X else
X putchar('\n') FLUSH;
X }
X }
X return 0;
X}
X
Xvoid
Xpage_init()
X{
X page_line = 1;
X if (erase_screen)
X clear();
X else
X putchar('\n') FLUSH;
X}
X
Xvoid
Xpad(num)
Xint num;
X{
X register int i;
X
X for (i = num; i; --i)
X putchar(PC);
X fflush(stdout);
X}
X
X/* echo the command just typed */
X
X#ifdef VERIFY
Xvoid
Xprintcmd()
X{
X if (verify && buf[1] == FINISHCMD) {
X if ((unsigned char)*buf < ' ') {
X putchar('^');
X putchar(*buf | 64);
X backspace();
X backspace();
X }
X else {
X putchar(*buf);
X backspace();
X }
X fflush(stdout);
X }
X}
X#endif
X
Xvoid
Xrubout()
X{
X backspace(); /* do the old backspace, */
X putchar(' '); /* space, */
X backspace(); /* backspace trick */
X}
X
Xvoid
Xreprint()
X{
X register char *s;
X
X fputs("^R\n",stdout) FLUSH;
X for (s = buf; *s; s++) {
X if ((unsigned char)*s < ' ') {
SHAR_EOF
echo "End of part 13"
echo "File term.c is continued in part 14"
echo "14" > s2_seq_.tmp
exit 0
exit 0 # Just in case...
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list