home *** CD-ROM | disk | FTP | other *** search
- From: skrenta@blekko.commodore.com (Rich Skrenta)
- Newsgroups: alt.sources
- Subject: Tass 3.2 newsreader part 1/3
- Message-ID: <159@blekko.commodore.com>
- Date: 17 Apr 91 17:00:36 GMT
-
-
- # This is a shell archive. Remove anything before this line,
- # then unpack it by saving it in a file and typing "sh file".
- #
- # This archive contains:
- # COPYRIGHT Makefile README Tass.man
- # art.c curses.c group.c hashstr.c
- #
-
- echo x - COPYRIGHT
- cat >COPYRIGHT <<'@EOF'
- /*
- * Tass, a visual Usenet news reader
- * (c) Copyright 1990 by Rich Skrenta
- *
- * Distribution agreement:
- *
- * You may freely copy or redistribute this software, so long
- * as there is no profit made from its use, sale, trade or
- * reproduction. You may not change this copyright notice,
- * and it must be included prominently in any copy made.
- */
- @EOF
-
- chmod 600 COPYRIGHT
-
- echo x - Makefile
- cat >Makefile <<'@EOF'
-
- # Make sure LIBDIR, SPOOLDIR and MAILER are correct in tass.h
- #
- # Make sure spool_open.c knows whether readdir returns struct direct or
- # struct dirent. The defines below should take care of this.
-
- # For Berkeley systems:
- #
- # CFLAGS= -DBSD
- # LIBS= -lcurses -ltermcap
-
- # For System V
- #
- CFLAGS=-g
- LIBS= -lcurses
-
- # For 286 Xenix
- #
- # CFLAGS=-g -M2l -F 8000
- # LIBS= -lcurses -ltermcap -lx
-
- # For SCO Unix System V
- #
- # CFLAGS=-g -UM_XENIX -DSCO_UNIX
- # LIBS= -lcurses -lgen
-
-
- # You only need to worry about the following two defines if you want
- # to build rtass (remote Tass via nntp)
- #
- # point NNTPLIB at the nntp clientlib.o support library
- #
- NNTPLIB=../nntp/common/clientlib.o
- #
- # NETLIBS should be the networking libraries you need to link with
- # the nntp clientlib.o
- #
- NETLIBS=-lnsl -lsocket
-
-
- OBJECTS = curses.o art.o group.o hashstr.o mail.o main.o misc.o \
- page.o prompt.o screen.o select.o time.o
-
- tass: $(OBJECTS) spool_open.o
- cc $(CFLAGS) -o tass $(OBJECTS) spool_open.o $(LIBS)
-
- rtass: $(OBJECTS) nntp_open.o
- cc $(CFLAGS) -o rtass $(OBJECTS) nntp_open.o $(LIBS) $(NNTPLIB) $(NETLIBS)
-
- shar:
- -mv -f ../tass.shar ../tass.shar-
- shar -v [A-Z]* *.[ch] > ../tass.shar
-
- clean:
- rm -f *.o
-
- clobber: clean
- rm -f tass rtass
-
-
- art.o: art.c tass.h
- curses.o: curses.c
- group.o: group.c tass.h
- hashstr.o: hashstr.c
- mail.o: mail.c
- main.o: main.c tass.h
- misc.o: misc.c tass.h
- nntp_open.o: nntp_open.c tass.h nntp.h
- page.o: page.c tass.h
- prompt.o: prompt.c tass.h
- screen.o: screen.c tass.h
- select.o: select.c tass.h
- spool_open.o: spool_open.c tass.h
- time.o: time.c
- @EOF
-
- chmod 644 Makefile
-
- echo x - README
- cat >README <<'@EOF'
- Tass is a full screen threaded newsreader.
-
- o Organizes articles by threads. Displays a really nice
- article selection page.
-
- o Group selection page makes it easy to scan newsgroups, subscribe,
- unsubscribe, reorder your .newsrc
-
- o If you've ever used Notes, this is the program for you.
- Tass looks a lot like Notes, but has a few improvements:
- visual group selection page, Notes didn't have one
- rn style unread article detection as opposed to single timeline
- uses standard /usr/spool/news article layout
-
- I wrote Tass because I "learned" the Usenet on Notes and couldn't stand rn.
- No rn flames here, but if you've wished you could view the news a different
- way, try Tass.
-
- Newsreading style under Tass tends to be different than with rn. Instead of
- plowing through each group reading everything unread, you may find yourself
- reading fewer articles in more groups. It's easier to skip about and only
- read interesting threads with Tass.
-
- Tass keeps an index file for each group. The first time you enter a group,
- it will be a bit slow creating this file. After that Tass will incrementally
- update the index file and there should be little delay.
-
- You can also run Tass in "update mode" out of cron to update the indexes.
-
-
- Building Tass
- -------- ----
-
- 1) Edit the Makefile. Select CFLAGS and LIBS for your system.
- 2) Edit tass.h. Make sure that LIBDIR, SPOOLDIR, MAILER and
- DEF_EDITOR are correct for your system.
- 3) 'make'
-
- To build the remote NNTP Tass (rtass) you will need the nntp sources;
- specifically, clientlib.o. Point the Makefile variable NNTPLIB at your
- clientlib.o and say 'make rtass'.
-
-
- Installing Tass
- ---------- ----
-
- Copy the tass executable to some useful place. If you make tass setuid news,
- it will keep the indexes in the news spool directory. If not, tass will hide
- indexes in the user's home directory.
-
- Don't make rtass setuid news since it will get articles from the NNTP host
- and not from /usr/spool/news.
-
- There is a brief man page (Tass.man) which may be copied to the appropriate
- man directory.
-
-
- NOTE:
- -----
- Tass 3.2 uses a different name for the index files. If you've been
- using Tass 3.0 or 3.1, you should remove these old indexes before
- running 3.2. Do this with
-
- rm -rf $HOME/.tindex
- or
- find /usr/spool/news -name '.tindex' -exec rm {} \;
-
-
- Changes from 3.0
- ------- ---- ---
-
- o Index files are now 1/2 to 1/3 their previous size
- o Tass is much more conservative in its memory usage
- o Tass recognizes .signature and .Sig files
- o Screen updating is more efficient in many places
- o Job control fixed for BSD systems
- o The various mailing commands should work much better
- o Author search
- o Support for NNTP (rtass)
- o Many other enhancements
-
-
- Rich Skrenta
- --
- skrenta@blekko.commodore.com
-
- @EOF
-
- chmod 644 README
-
- echo x - Tass.man
- cat >Tass.man <<'@EOF'
- .TH TASS 1A
- .SH NAME
- tass, rtass \- Visual threaded Usenet news reader
- .SH SYNOPSIS
- .nf
- tass [options] [newsgroups]
- rtass [options] [newsgroups]
- .fi
- .SH DESCRIPTION
- Tass is a full screen threaded Usenet newsreader.
- Tass has three newsreading levels:
- the newsgroup selection page, the group index page and the article viewer.
- Use the 'h' (help) command to view a list of the commands available at a
- particular level.
- .PP
- On startup Tass will show a list of the newsgroups found in $HOME/.newsrc.
- An arrow will point to the first newsgroup. Move the arrow by either using
- the terminal arrow keys or 'j' and 'k'. Control-D will page down, control-U
- will page up. Enter a newsgroup by pressing RETURN.
- .PP
- The TAB key may be used to advance to the next newsgroup with unread articles
- and enter it.
- .PP
- rtass will attempt to connect to the NNTP port on the machine named in the
- environment variable NNTPSERVER or contained in the file /etc/nntpserver.
- rtass will index somewhat slower because the articles must be retrieved
- via the NNTP protocol.
- .PP
- Refer to the Tass help screens for further commands.
- .SH TASS INDEX FILES
- In order to keep track of threads, Tass maintains an index for each group.
- If Tass is made setuid to news, the indexes will be stored in the news spool
- directory (typically /usr/spool/news). If
- Tass is not setuid, it will store
- index files in the user's home directory, in a subdirectory called .tindx.
- .PP
- Entering a group the first time tends to be slow because the index file must
- be built from scratch. Subsequent readings of a group will cause
- Tass to incrementally update the index file, adding or removing entries as new
- articles come in or as news expires.
- .PP
- Tass may be run in update mode (the -u option) to update a series of groups
- at one time. tass -u is usually run from cron.
- .PP
- Do not make rtass setuid news since news will be obtained via NNTP and not
- from /usr/spool/news.
- .SH SIGNATURES
- Tass will recognize a signature in either $HOME/.signature or $HOME/.Sig.
- If .signature exists, then the signature will be pulled into the editor
- for Tass mail commands. A signature in .signature will not be pulled
- into the editor for posting commands since the inews program
- will append the signature itself.
- .PP
- A signature in .Sig will be pulled into the editor for both posting
- and mailing commands.
- .SH OPTIONS
- .I Tass
- recognizes the following options:
- .TP
- -f file
- Use the indicated file in place of $HOME/.newsrc.
- .TP
- -u
- Run Tass in update mode. Tass will make indexes current for every group
- in its newsrc.
-
- A good way to keep Tass index files current is to run tass -u from cron:
-
- .nf
- 20 6 * * * /usr/local/bin/tass -u -f /usr/lib/news/tass_groups
- .fi
-
- This would update the index files for those groups appearing in
- /usr/lib/news/tass_groups. To index all of the groups on the system,
- run tass -u with -f indicating the active file:
-
- .nf
- 20 6 * * * /usr/local/bin/tass -u -f /usr/lib/news/active
- .fi
-
- .SH AUTHOR
- .nf
- Rich Skrenta
- skrenta@blekko.commodore.com or skrenta@blekko.uucp.
- .fi
- @EOF
-
- chmod 644 Tass.man
-
- echo x - art.c
- cat >art.c <<'@EOF'
-
-
- #include <stdio.h>
- #include <ctype.h>
- #include <signal.h>
- #include "tass.h"
-
-
- char index_file[LEN+1];
- char *glob_art_group;
- extern char *hash_str();
-
-
- #ifdef SIGTSTP
- void
- art_susp(i)
- int i;
- {
-
- Raw(FALSE);
- putchar('\n');
- signal(SIGTSTP, SIG_DFL);
- #ifdef BSD
- sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
- #endif
- kill(0, SIGTSTP);
-
- signal(SIGTSTP, art_susp);
- Raw(TRUE);
-
- mail_setup();
- ClearScreen();
- MoveCursor(LINES, 0);
- printf("Group %s... ", glob_art_group);
- fflush(stdout);
- }
- #endif
-
-
- /*
- * Convert a string to a long, only look at first n characters
- */
-
- my_atol(s, n)
- char *s;
- int n;
- {
- long ret = 0;
-
- while (*s && n--) {
- if (*s >= '0' && *s <= '9')
- ret = ret * 10 + (*s - '0');
- else
- return -1;
- s++;
- }
-
- return ret;
- }
-
-
- /*
- * Construct the pointers to the basenotes of each thread
- * arts[] contains every article in the group. inthread is
- * set on each article that is after the first article in the
- * thread. Articles which have been expired have their thread
- * set to -2.
- */
-
- find_base() {
- int i;
-
- top_base = 0;
-
- for (i = 0; i < top; i++)
- if (!arts[i].inthread && arts[i].thread != -2) {
- if (top_base >= max_art)
- expand_art();
- base[top_base++] = i;
- }
- }
-
-
- /*
- * Count the number of non-expired articles in arts[]
- */
-
- num_arts() {
- int sum = 0;
-
- int i;
-
- for (i = 0; i < top; i++)
- if (arts[i].thread != -2)
- sum++;
-
- return sum;
- }
-
-
- /*
- * Do we have an entry for article art?
- */
-
- valid_artnum(art)
- long art;
- {
- int i;
-
- for (i = 0; i < top; i++)
- if (arts[i].artnum == art)
- return i;
-
- return -1;
- }
-
-
- /*
- * Return TRUE if arts[] contains any expired articles
- * (articles we have an entry for which don't have a corresponding
- * article file in the spool directory)
- */
-
- purge_needed() {
- int i;
-
- for (i = 0; i < top; i++)
- if (arts[i].thread == -2)
- return TRUE;
-
- return FALSE;
- }
-
-
- /*
- * Main group indexing routine. Group should be the name of the
- * newsgroup, i.e. "comp.unix.amiga". group_path should be the
- * same but with the .'s turned into /'s: "comp/unix/amiga"
- *
- * Will read any existing index, create or incrementally update
- * the index by looking at the articles in the spool directory,
- * and attempt to write a new index if necessary.
- */
-
- index_group(group, group_path)
- char *group;
- char *group_path;
- {
- int modified;
-
- glob_art_group = group;
-
- #ifdef SIGTSTP
- signal(SIGTSTP, art_susp);
- #endif
-
- if (!update) {
- clear_message();
- MoveCursor(LINES, 0);
- printf("Group %s... ", group);
- fflush(stdout);
- }
-
- hash_reclaim();
- if (local_index)
- find_local_index(group);
- else
- sprintf(index_file, "%s/%s/.tindx", SPOOLDIR, group_path);
-
- load_index();
- modified = read_group(group, group_path);
- make_threads();
- if (modified || purge_needed()) {
- if (local_index) { /* writing index in home directory */
- setuid(real_uid); /* so become them */
- setgid(real_gid);
- }
- dump_index(group);
- if (local_index) {
- setuid(tass_uid);
- setgid(tass_gid);
- }
- }
- find_base();
-
- if (modified && !update)
- clear_message();
- }
-
-
- /*
- * Index a group. Assumes any existing index has already been
- * loaded.
- */
-
- read_group(group, group_path)
- char *group;
- char *group_path;
- {
- int fd;
- long art;
- int count;
- int modified = FALSE;
- int respnum;
- int i;
-
- setup_base(group, group_path); /* load article numbers into base[] */
- count = 0;
-
- for (i = 0; i < top_base; i++) { /* for each article # */
- art = base[i];
-
- /*
- * Do we already have this article in our index? Change thread from
- * -2 to -1 if so and skip the header eating.
- */
-
- if ((respnum = valid_artnum(art)) >= 0) {
- arts[respnum].thread = -1;
- arts[respnum].unread = 1;
- continue;
- }
-
- if (!modified)
- modified = TRUE; /* we've modified the index */
- /* it will need to be re-written */
-
- fd = open_header_fd(group_path, art);
- if (fd < 0)
- continue;
-
- /*
- * Add article to arts[]
- */
-
- if (top >= max_art)
- expand_art();
-
- arts[top].artnum = art;
- arts[top].thread = -1;
- arts[top].inthread = FALSE;
- arts[top].unread = 1;
-
- if (!parse_headers(fd, &arts[top]))
- continue;
- top++;
- close(fd);
-
- if (++count % 10 == 0 && !update) {
- printf("\b\b\b\b%4d", count);
- fflush(stdout);
- }
- }
-
- return modified;
- }
-
-
- /*
- * Go through the articles in arts[] and use .thread to snake threads
- * through them. Use the subject line to construct threads. The
- * first article in a thread should have .inthread set to FALSE, the
- * rest TRUE. Only do unexprired articles we haven't visited yet
- * (arts[].thread == -1).
- */
-
- make_threads() {
- int i;
- int j;
-
- for (i = 0; i < top; i++) {
- if (arts[i].thread == -1)
- for (j = i+1; j < top; j++)
- if (arts[j].thread != -2
- && arts[i].subject == arts[j].subject) {
- arts[i].thread = j;
- arts[j].inthread = TRUE;
- break;
- }
- }
- }
-
-
- /*
- * Return a pointer into s eliminating any leading Re:'s. Example:
- *
- * Re: Reorganization of misc.jobs
- * ^ ^
- */
-
- char *
- eat_re(s)
- char *s;
- {
-
- while (*s == 'r' || *s == 'R') {
- if ((*(s+1) == 'e' || *(s+1) == 'E')) {
- if (*(s+2) == ':')
- s += 3;
- else if (*(s+2) == '^' && isdigit(*(s+3)) && *(s+4) == ':')
- s += 5; /* hurray nn */
- else
- break;
- } else
- break;
- while (*s == ' ')
- s++;
- }
-
- return s;
- }
-
-
- parse_headers(fd, h)
- int fd;
- struct header *h;
- {
- char buf[1024];
- char *p, *q;
- char flag;
- int n;
- char buf2[1024];
- char *s;
-
- n = read(fd, buf, 1024);
- if (n <= 0)
- return FALSE;
-
- buf[n - 1] = '\0';
-
- h->subject = "<no subject>";
- h->from = "<no from>";
-
- p = buf;
- while (1) {
- for (q = p; *p && *p != '\n'; p++)
- if (((*p) & 0x7F) < 32)
- *p = ' ';
- flag = *p;
- *p++ = '\0';
-
- if (strncmp(q, "From: ", 6) == 0) {
- strncpy(buf2, &q[6], MAX_FROM-1);
- buf2[MAX_FROM-1] = '\0';
- h->from = hash_str(buf2);
- } else if (strncmp(q, "Subject: ", 9) == 0) {
- strcpy(buf2, &q[9]);
- s = eat_re(buf2);
- s[MAX_SUBJ-1] = '\0';
- h->subject = hash_str(eat_re(s));
- }
-
- if (!flag || *p == '\n')
- break;
- }
-
- return TRUE;
- }
-
-
- /*
- * Write out a .tindx file. Write the group name first so if
- * local indexing is done we can disambiguate between group name
- * hash collisions by looking at the index file.
- */
-
- dump_index(group)
- char *group;
- {
- int i;
- char buf[200];
- char nam[200];
- FILE *fp;
- int *iptr;
- int realnum;
-
- sprintf(nam, "%s.%d", index_file, getpid());
- fp = fopen(nam, "w");
-
- if (fp == NULL)
- return;
-
- fprintf(fp, "%s\n", group);
- fprintf(fp, "%d\n", num_arts());
-
- realnum = 0;
- for (i = 0; i < top; i++)
- if (arts[i].thread != -2) {
- fprintf(fp, "%ld\n", arts[i].artnum);
-
- iptr = (int *) arts[i].subject;
- iptr--;
-
- if (*iptr < 0) {
- fprintf(fp, " %s\n", arts[i].subject);
- *iptr = realnum;
- } else
- fprintf(fp, "%%%d\n", *iptr);
-
- iptr = (int *) arts[i].from;
- iptr--;
-
- if (*iptr < 0) {
- fprintf(fp, " %s\n", arts[i].from);
- *iptr = realnum;
- } else
- fprintf(fp, "%%%d\n", *iptr);
-
- realnum++;
- }
-
- fclose(fp);
- chmod(nam, 0644);
- unlink(index_file);
- link(nam, index_file);
- unlink(nam);
- }
-
-
- /*
- * strncpy that stops at a newline and null terminates
- */
-
- my_strncpy(p, q, n)
- char *p;
- char *q;
- int n;
- {
-
- while (n--) {
- if (!*q || *q == '\n')
- break;
- *p++ = *q++;
- }
- *p = '\0';
- }
-
-
- /*
- * Read in a .tindx file.
- */
-
- load_index()
- {
- int i;
- long j;
- char buf[200];
- FILE *fp;
- int first = TRUE;
- char *p;
- int n;
- char *err;
-
- top = 0;
-
- fp = fopen(index_file, "r");
- if (fp == NULL)
- return;
-
- if (fgets(buf, 200, fp) == NULL
- || fgets(buf, 200, fp) == NULL) {
- err = "one";
- goto corrupt_index;
- }
-
- i = atol(buf);
- while (top < i) {
- if (top >= max_art)
- expand_art();
-
- arts[top].thread = -2;
- arts[top].inthread = FALSE;
-
- if (fgets(buf, 200, fp) == NULL) {
- err = "two";
- goto corrupt_index;
- }
- arts[top].artnum = atol(buf);
-
- if (fgets(buf, 200, fp) == NULL) {
- err = "three";
- goto corrupt_index;
- }
-
- if (buf[0] == '%') {
- n = atoi(&buf[1]);
- if (n >= top || n < 0) {
- err = "eight";
- goto corrupt_index;
- }
- arts[top].subject = arts[n].subject;
- } else if (buf[0] == ' ') {
- for (p = &buf[1]; *p && *p != '\n'; p++) ;
- *p = '\0';
- buf[MAX_SUBJ] = '\0';
- arts[top].subject = hash_str(&buf[1]);
- } else {
- err = "six";
- goto corrupt_index;
- }
-
- if (fgets(buf, 200, fp) == NULL) {
- err = "four";
- goto corrupt_index;
- }
-
- if (buf[0] == '%') {
- n = atoi(&buf[1]);
- if (n >= top || n < 0) {
- err = "nine";
- goto corrupt_index;
- }
- arts[top].from = arts[n].from;
- } else if (buf[0] == ' ') {
- for (p = &buf[1]; *p && *p != '\n'; p++) ;
- *p = '\0';
- buf[MAX_FROM] = '\0';
- arts[top].from = hash_str(&buf[1]);
- } else {
- err = "seven";
- goto corrupt_index;
- }
-
- top++;
- }
-
- fclose(fp);
- return;
-
- corrupt_index:
- fprintf(stderr, "\r\n%s: index file %s corrupt, top=%d\r\n",
- err, index_file, top);
- unlink(index_file);
- top = 0;
- }
-
-
- /*
- * Look in the local $HOME/.tindx (or wherever) directory for the
- * index file for the given group. Hashing the group name gets
- * a number. See if that #.1 file exists; if so, read first line.
- * Group we want? If no, try #.2. Repeat until no such file or
- * we find an existing file that matches our group.
- */
-
- find_local_index(group)
- char *group;
- {
- unsigned long h;
- static char buf[200];
- int i;
- char *p;
- FILE *fp;
-
- h = hash_groupname(group);
-
- i = 1;
- while (1) {
- sprintf(index_file, "%s/%lu.%d", indexdir, h, i);
- fp = fopen(index_file, "r");
- if (fp == NULL)
- return;
-
- if (fgets(buf, 200, fp) == NULL) {
- fclose(fp);
- return;
- }
- fclose(fp);
-
- for (p = buf; *p && *p != '\n'; p++) ;
- *p = '\0';
-
- if (strcmp(buf, group) == 0)
- return;
-
- i++;
- }
- }
-
-
- /*
- * Run the index file updater only for the groups we've loaded.
- */
-
- do_update() {
- int i;
- char group_path[200];
- char *p;
-
- for (i = 0; i < local_top; i++) {
- strcpy(group_path, active[my_group[i]].name);
- for (p = group_path; *p; p++)
- if (*p == '.')
- *p = '/';
-
- index_group(active[my_group[i]].name, group_path);
- }
- }
-
- @EOF
-
- chmod 644 art.c
-
- echo x - curses.c
- cat >curses.c <<'@EOF'
-
- /*
- * This is a screen management library borrowed with permission from the
- * Elm mail system (a great mailer--I highly recommend it!).
- *
- * I've hacked this library to only provide what Tass needs.
- *
- * Original copyright follows:
- */
-
- /*******************************************************************************
- * The Elm Mail System - $Revision: 2.1 $ $State: Exp $
- *
- * Copyright (c) 1986 Dave Taylor
- ******************************************************************************/
-
- #include <stdio.h>
- #include <curses.h>
-
- #define TRUE 1
- #define FALSE 0
-
- #define BACKSPACE '\b'
- #define VERY_LONG_STRING 2500
-
- int LINES=23;
- int COLS=80;
-
- int inverse_okay = TRUE;
-
- /*
- #ifdef BSD
- # ifndef BSD4_1
- # include <sgtty.h>
- # else
- # include <termio.h>
- # endif
- # else
- # include <termio.h>
- #endif
- */
-
- #include <ctype.h>
-
- /*
- #ifdef BSD
- #undef tolower
- #endif
- */
-
- #define TTYIN 0
-
- #ifdef SHORTNAMES
- # define _clearinverse _clrinv
- # define _cleartoeoln _clrtoeoln
- # define _cleartoeos _clr2eos
- #endif
-
- #ifndef BSD
- struct termio _raw_tty,
- _original_tty;
- #else
- #define TCGETA TIOCGETP
- #define TCSETAW TIOCSETP
-
- struct sgttyb _raw_tty,
- _original_tty;
- #endif
-
- static int _inraw = 0; /* are we IN rawmode? */
-
- #define DEFAULT_LINES_ON_TERMINAL 24
- #define DEFAULT_COLUMNS_ON_TERMINAL 80
-
- static int _memory_locked = 0; /* are we IN memlock?? */
-
- static int _intransmit; /* are we transmitting keys? */
-
- static
- char *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos,
- *_setinverse, *_clearinverse;
-
- static
- int _lines,_columns;
-
- static char _terminal[1024]; /* Storage for terminal entry */
- static char _capabilities[1024]; /* String for cursor motion */
-
- static char *ptr = _capabilities; /* for buffering */
-
- int outchar(); /* char output for tputs */
- char *tgetstr(), /* Get termcap capability */
- *tgoto(); /* and the goto stuff */
-
- InitScreen()
- {
- int tgetent(), /* get termcap entry */
- err;
- char termname[40];
- char *strcpy(), *getenv();
-
- if (getenv("TERM") == NULL) {
- fprintf(stderr,
- "TERM variable not set; Tass requires screen capabilities\n");
- return(FALSE);
- }
- if (strcpy(termname, getenv("TERM")) == NULL) {
- fprintf(stderr,"Can't get TERM variable\n");
- return(FALSE);
- }
- if ((err = tgetent(_terminal, termname)) != 1) {
- fprintf(stderr,"Can't get entry for TERM\n");
- return(FALSE);
- }
-
- /* load in all those pesky values */
- _clearscreen = tgetstr("cl", &ptr);
- _moveto = tgetstr("cm", &ptr);
- _cleartoeoln = tgetstr("ce", &ptr);
- _cleartoeos = tgetstr("cd", &ptr);
- _lines = tgetnum("li");
- _columns = tgetnum("co");
- _setinverse = tgetstr("so", &ptr);
- _clearinverse = tgetstr("se", &ptr);
-
- if (!_clearscreen) {
- fprintf(stderr,
- "Terminal must have clearscreen (cl) capability\n");
- return(FALSE);
- }
- if (!_moveto) {
- fprintf(stderr,
- "Terminal must have cursor motion (cm)\n");
- return(FALSE);
- }
- if (!_cleartoeoln) {
- fprintf(stderr,
- "Terminal must have clear to end-of-line (ce)\n");
- return(FALSE);
- }
- if (!_cleartoeos) {
- fprintf(stderr,
- "Terminal must have clear to end-of-screen (cd)\n");
- return(FALSE);
- }
- if (_lines == -1)
- _lines = DEFAULT_LINES_ON_TERMINAL;
- if (_columns == -1)
- _columns = DEFAULT_COLUMNS_ON_TERMINAL;
- return(TRUE);
- }
-
- ScreenSize(lines, columns)
- int *lines, *columns;
- {
- /** returns the number of lines and columns on the display. **/
-
- if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL;
- if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL;
-
- *lines = _lines - 1; /* assume index from zero*/
- *columns = _columns; /* assume index from one */
- }
-
- ClearScreen()
- {
- /* clear the screen: returns -1 if not capable */
-
- tputs(_clearscreen, 1, outchar);
- fflush(stdout); /* clear the output buffer */
- }
-
- MoveCursor(row, col)
- int row, col;
- {
- /** move cursor to the specified row column on the screen.
- 0,0 is the top left! **/
-
- char *stuff, *tgoto();
-
- stuff = tgoto(_moveto, col, row);
- tputs(stuff, 1, outchar);
- fflush(stdout);
- }
-
- CleartoEOLN()
- {
- /** clear to end of line **/
-
- tputs(_cleartoeoln, 1, outchar);
- fflush(stdout); /* clear the output buffer */
- }
-
- CleartoEOS()
- {
- /** clear to end of screen **/
-
- tputs(_cleartoeos, 1, outchar);
- fflush(stdout); /* clear the output buffer */
- }
-
- StartInverse()
- {
- /** set inverse video mode **/
-
- if (_setinverse && inverse_okay)
- tputs(_setinverse, 1, outchar);
- /* fflush(stdout); */
- }
-
-
- EndInverse()
- {
- /** compliment of startinverse **/
-
- if (_clearinverse && inverse_okay)
- tputs(_clearinverse, 1, outchar);
- /* fflush(stdout); */
- }
-
- RawState()
- {
- /** returns either 1 or 0, for ON or OFF **/
-
- return( _inraw );
- }
-
- Raw(state)
- int state;
- {
- /** state is either TRUE or FALSE, as indicated by call **/
-
- if (state == FALSE && _inraw) {
- (void) ioctl(TTYIN, TCSETAW, &_original_tty);
- _inraw = 0;
- }
- else if (state == TRUE && ! _inraw) {
-
- (void) ioctl(TTYIN, TCGETA, &_original_tty); /** current setting **/
-
- (void) ioctl(TTYIN, TCGETA, &_raw_tty); /** again! **/
- #ifdef BSD
- _raw_tty.sg_flags &= ~(ECHO | CRMOD); /* echo off */
- _raw_tty.sg_flags |= CBREAK; /* raw on */
- #else
- _raw_tty.c_lflag &= ~(ICANON | ECHO); /* noecho raw mode */
-
- _raw_tty.c_cc[VMIN] = '\01'; /* minimum # of chars to queue */
- _raw_tty.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
-
- #endif
- (void) ioctl(TTYIN, TCSETAW, &_raw_tty);
-
- _inraw = 1;
- }
- }
-
- int
- ReadCh()
- {
- /** read a character with Raw mode set! **/
-
- register int result;
- char ch;
- result = read(0, &ch, 1);
- return((result <= 0 ) ? EOF : ch & 0x7F);
- }
-
-
- outchar(c)
- char c;
- {
- /** output the given character. From tputs... **/
- /** Note: this CANNOT be a macro! **/
-
- putc(c, stdout);
- }
-
- @EOF
-
- chmod 644 curses.c
-
- echo x - group.c
- cat >group.c <<'@EOF'
-
-
- #include <stdio.h>
- #include <signal.h>
- #include "tass.h"
-
-
- int index_point;
- int first_subj_on_screen;
- int last_subj_on_screen;
- char subject_search_string[LEN+1];
- char author_search_string[LEN+1];
- extern int cur_groupnum;
- extern int last_resp; /* page.c */
- extern int this_resp; /* page.c */
- extern int space_mode; /* select.c */
- extern char *cvers;
-
- char *glob_group;
-
-
- #ifdef SIGTSTP
- void
- group_susp(i)
- int i;
- {
-
- Raw(FALSE);
- putchar('\n');
- signal(SIGTSTP, SIG_DFL);
- #ifdef BSD
- sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
- #endif
- kill(0, SIGTSTP);
-
- signal(SIGTSTP, group_susp);
- Raw(TRUE);
- mail_setup();
- show_group_page(glob_group);
- }
- #endif
-
-
- group_page(group)
- char *group;
- {
- char ch;
- int i, n;
- char group_path[200];
- char *p;
- char buf[200];
- int flag;
- int sav_groupnum;
-
- glob_group = group;
- sav_groupnum = cur_groupnum;
-
- strcpy(group_path, group); /* turn comp.unix.amiga into */
- for (p = group_path; *p; p++) /* comp/unix/amiga */
- if (*p == '.')
- *p = '/';
-
- last_resp = -1;
- this_resp = -1;
- index_group(group, group_path); /* update index file */
- read_newsrc_line(group); /* get sequencer information */
-
- if (space_mode) {
- for (i = 0; i < top_base; i++)
- if (new_responses(i))
- break;
- if (i < top_base)
- index_point = i;
- else
- index_point = top_base - 1;
- } else
- index_point = top_base - 1;
-
- show_group_page(group);
-
- while (1) {
- ch = ReadCh();
-
- if (ch > '0' && ch <= '9') { /* 0 goes to basenote */
- prompt_subject_num(ch, group);
- } else switch (ch) {
- case 'a': /* author search forward */
- case 'A': /* author search backward */
- if (index_point < 0) {
- info_message("No articles");
- break;
- }
-
- i = (ch == 'a');
-
- n = search_author((int) base[index_point],
- i, group);
- if (n < 0)
- break;
-
- index_point = show_page(n, group, group_path);
- if (index_point < 0) {
- space_mode = FALSE;
- goto group_done;
- }
- show_group_page(group);
- break;
-
- case 'I': /* toggle inverse video */
- inverse_okay = !inverse_okay;
- if (inverse_okay)
- info_message("Inverse video enabled");
- else
- info_message("Inverse video disabled");
- break;
-
- case 's': /* subscribe to this group */
- subscribe(group, ':', my_group[cur_groupnum],
- TRUE);
- sprintf(buf, "subscribed to %s", group);
- info_message(buf);
- break;
-
- case 'u': /* unsubscribe to this group */
- subscribe(group, '!', my_group[cur_groupnum],
- TRUE);
- sprintf(buf, "unsubscribed to %s", group);
- info_message(buf);
- break;
-
- case 'g': /* choose a new group by name */
- n = choose_new_group();
- if (n >= 0 && n != cur_groupnum) {
- cur_groupnum = n;
- index_point = -3;
- goto group_done;
- }
- break;
-
- case 'c': /* catchup--mark all articles as read */
- if (prompt_yn("Mark everything as read? (y/n): ")) {
- for (n = 0; n < top; n++)
- arts[n].unread = 0;
- for (n = INDEX_TOP ;
- n < NOTESLINES + INDEX_TOP; n++ ) {
- MoveCursor(n, COLS - 2);
- putchar(' ');
- }
- fflush(stdout);
- /* show_group_page(group); */
- info_message("All articles marked as read");
- }
- break;
-
- case 27: /* common arrow keys */
- ch = ReadCh();
- if (ch == '[' || ch == 'O')
- ch = ReadCh();
- switch (ch) {
- case 'A':
- case 'D':
- case 'i':
- goto group_up;
-
- case 'B':
- case 'I':
- case 'C':
- goto group_down;
- }
- break;
-
- case 'n': /* next group */
- clear_message();
- if (cur_groupnum + 1 >= local_top)
- info_message("No more groups");
- else {
- cur_groupnum++;
- index_point = -3;
- space_mode = FALSE;
- goto group_done;
- }
- break;
-
- case 'p': /* previous group */
- clear_message();
- if (cur_groupnum <= 0)
- info_message("No previous group");
- else {
- cur_groupnum--;
- index_point = -3;
- space_mode = FALSE;
- goto group_done;
- }
- break;
-
- case '\t':
- space_mode = TRUE;
-
- if (index_point < 0
- || (n=next_unread((int) base[index_point]))<0) {
- for (i = cur_groupnum+1;
- i < local_top; i++)
- if (unread[i] > 0)
- break;
- if (i >= local_top)
- goto group_done;
-
- cur_groupnum = i;
- index_point = -3;
- goto group_done;
- }
- index_point = show_page(n, group, group_path);
- if (index_point < 0)
- goto group_done;
- show_group_page(group);
- break;
-
- case 'K': /* mark rest of thread as read */
- if (new_responses(index_point)) {
- for (i = base[index_point]; i >= 0;
- i = arts[i].thread)
- arts[i].unread = 0;
- MoveCursor(INDEX_TOP +
- (index_point - first_subj_on_screen), 78);
- putchar(' ');
- fflush(stdout);
- flag = FALSE;
- } else
- flag = TRUE;
-
- n = next_unread(
- next_response(base[index_point]));
- if (n < 0) {
- if (flag)
- info_message("No next unread article");
- else
- MoveCursor(LINES, 0);
- break;
- }
-
- n = which_base(n);
- if (n < 0) {
- info_message(
- "Internal error: K which_base < 0");
- break;
- }
-
- if (n >= last_subj_on_screen) {
- index_point = n;
- show_group_page(group);
- } else {
- erase_subject_arrow();
- index_point = n;
- draw_subject_arrow();
- }
- break;
-
- case 'N': /* go to next unread article */
- if (index_point < 0) {
- info_message("No next unread article");
- break;
- }
-
- n = next_unread( (int) base[index_point]);
- if (n == -1)
- info_message("No next unread article");
- else {
- index_point =
- show_page(n, group, group_path);
- if (index_point < 0) {
- space_mode = FALSE;
- goto group_done;
- }
- show_group_page(group);
- }
- break;
-
- case 'P': /* go to previous unread article */
- if (index_point < 0) {
- info_message("No previous unread article");
- break;
- }
-
- n = prev_response( (int) base[index_point]);
- n = prev_unread(n);
- if (n == -1)
- info_message("No previous unread article");
- else {
- index_point =
- show_page(n, group, group_path);
- if (index_point < 0) {
- space_mode = FALSE;
- goto group_done;
- }
- show_group_page(group);
- }
- break;
-
- case 'w': /* post a basenote */
- post_base(group);
- update_newsrc(group, my_group[cur_groupnum]);
- index_group(group, group_path);
- read_newsrc_line(group);
- index_point = top_base - 1;
- show_group_page(group);
- break;
-
- case 't': /* return to group selection page */
- goto group_done;
-
- case ' ':
- case '\r':
- case '\n': /* read current basenote */
- if (index_point < 0) {
- info_message("*** No Articles ***");
- break;
- }
- index_point = show_page((int) base[index_point],
- group, group_path);
- if (index_point < 0) {
- space_mode = FALSE;
- goto group_done;
- }
- show_group_page(group);
- break;
-
- case ctrl('D'): /* page down */
- if (!top_base || index_point == top_base - 1)
- break;
-
- erase_subject_arrow();
- index_point += NOTESLINES / 2;
- if (index_point >= top_base)
- index_point = top_base - 1;
-
- if (index_point < first_subj_on_screen
- || index_point >= last_subj_on_screen)
- show_group_page(group);
- else
- draw_subject_arrow();
- break;
-
- case '-': /* go to last viewed article */
- if (this_resp < 0) {
- info_message("No last message");
- break;
- }
- index_point = show_page(this_resp,
- group, group_path);
- if (index_point < 0) {
- space_mode = FALSE;
- goto group_done;
- }
- show_group_page(group);
- break;
-
- case ctrl('U'): /* page up */
- if (!top_base)
- break;
-
- erase_subject_arrow();
- index_point -= NOTESLINES / 2;
- if (index_point < 0)
- index_point = 0;
- if (index_point < first_subj_on_screen
- || index_point >= last_subj_on_screen)
- show_group_page(group);
- else
- draw_subject_arrow();
- break;
-
- case 'v':
- info_message(cvers);
- break;
-
- case '!':
- shell_escape();
- show_group_page(group);
- break;
-
- case ctrl('N'):
- case 'j': /* line down */
- group_down:
- if (!top_base || index_point + 1 >= top_base)
- break;
-
- if (index_point + 1 >= last_subj_on_screen) {
- index_point++;
- show_group_page(group);
- } else {
- erase_subject_arrow();
- index_point++;
- draw_subject_arrow();
- }
- break;
-
- case ctrl('P'):
- case 'k': /* line up */
- group_up:
- if (!top_base || !index_point)
- break;
-
- if (index_point <= first_subj_on_screen) {
- index_point--;
- show_group_page(group);
- } else {
- erase_subject_arrow();
- index_point--;
- draw_subject_arrow();
- }
- break;
-
- case ctrl('R'):
- case ctrl('L'):
- case ctrl('W'):
- case 'i': /* return to index */
- show_group_page(group);
- break;
-
- case '/': /* forward search */
- search_subject(TRUE, group);
- break;
-
- case '?': /* backward search */
- search_subject(FALSE, group);
- break;
-
- case 'q': /* quit */
- index_point = -2;
- space_mode = FALSE;
- goto group_done;
-
- case 'h':
- tass_group_help();
- show_group_page(group);
- break;
-
- default:
- info_message("Bad command. Type 'h' for help.");
- }
- }
-
- group_done:
- fix_new_highest(sav_groupnum);
- update_newsrc(group, my_group[sav_groupnum]);
-
- if (index_point == -2)
- tass_done(0);
- }
-
-
- /*
- * Correct highest[] for the group selection page display since
- * new articles may have been read or marked unread
- */
-
- fix_new_highest(groupnum)
- int groupnum;
- {
- int i;
- int sum = 0;
-
- for (i = 0; i < top; i++)
- if (arts[i].unread)
- sum++;
-
- unread[groupnum] = sum;
- }
-
-
- show_group_page(group)
- char *group;
- {
- int i;
- int n;
- char resps[10];
- char new_resps;
- int respnum;
-
- #ifdef SIGTSTP
- signal(SIGTSTP, group_susp);
- #endif
-
- ClearScreen();
- printf("%s\r\n", nice_time()); /* time in upper left */
- center_line(1, group);
-
- if (mail_check()) { /* you have mail message in */
- MoveCursor(0, 66); /* upper right */
- printf("you have mail\n");
- }
-
- MoveCursor(INDEX_TOP, 0);
-
- first_subj_on_screen = (index_point / NOTESLINES) * NOTESLINES;
- if (first_subj_on_screen < 0)
- first_subj_on_screen = 0;
-
- last_subj_on_screen = first_subj_on_screen + NOTESLINES;
- if (last_subj_on_screen >= top_base) {
- last_subj_on_screen = top_base;
- first_subj_on_screen = top_base - NOTESLINES;
-
- if (first_subj_on_screen < 0)
- first_subj_on_screen = 0;
- }
-
- for (i = first_subj_on_screen; i < last_subj_on_screen; i++) {
- if (new_responses(i))
- new_resps = '+';
- else
- new_resps = ' ';
-
- n = nresp(i);
- if (n)
- sprintf(resps, "%4d", n);
- else
- strcpy(resps, " ");
-
- respnum = base[i];
-
- printf(" %4d %-*s %s %-*s %c\r\n",
- i + 1,
- MAX_SUBJ,
- arts[respnum].subject,
- resps,
- MAX_FROM,
- arts[respnum].from,
- new_resps);
- }
-
- if (top_base <= 0)
- info_message("*** No Articles ***");
- else if (last_subj_on_screen == top_base)
- info_message("*** End of Articles ***");
-
- if (top_base > 0)
- draw_subject_arrow();
- }
-
- draw_subject_arrow() {
-
- draw_arrow(INDEX_TOP + (index_point-first_subj_on_screen) );
- }
-
- erase_subject_arrow() {
-
- erase_arrow(INDEX_TOP + (index_point-first_subj_on_screen) );
- }
-
-
- prompt_subject_num(ch, group)
- char ch;
- char *group;
- {
- int num;
-
-
- clear_message();
-
- if ((num = parse_num(ch, "Read article> ")) == -1) {
- clear_message();
- return FALSE;
- }
- num--; /* index from 0 (internal) vs. 1 (user) */
-
- if (num >= top_base)
- num = top_base - 1;
-
- if (num >= first_subj_on_screen
- && num < last_subj_on_screen) {
- erase_subject_arrow();
- index_point = num;
- draw_subject_arrow();
- } else {
- index_point = num;
- show_group_page(group);
- }
- }
-
-
- search_author(current_art, forward, group)
- int current_art;
- int forward;
- char *group;
- {
- char buf[LEN+1];
- char buf2[LEN+1];
- int i;
- int len;
- char *prompt;
-
- clear_message();
-
- if (forward)
- prompt = "Author search forward: ";
- else
- prompt = "Author search backward: ";
-
- if (!parse_string(prompt, buf))
- return -1;
-
- if (strlen(buf))
- strcpy(author_search_string, buf);
- else if (!strlen(author_search_string)) {
- info_message("No search string");
- return -1;
- }
-
- make_lower(author_search_string, buf);
- len = strlen(buf);
-
- i = current_art;
-
- do {
- if (forward) {
- i = next_response(i);
- if (i < 0)
- i = 0;
- } else {
- i = prev_response(i);
- if (i < 0)
- i = top - 1;
- }
-
- make_lower(arts[i].from, buf2);
- if (match(buf, buf2, len))
- return i;
- } while (i != current_art);
-
- info_message("No match");
- return -1;
- }
-
-
- search_subject(forward, group)
- int forward;
- char *group;
- {
- char buf[LEN+1];
- char buf2[LEN+1];
- int i;
- int len;
- char *prompt;
-
- clear_message();
-
- if (forward)
- prompt = "/";
- else
- prompt = "?";
-
- if (!parse_string(prompt, buf))
- return;
-
- if (strlen(buf))
- strcpy(subject_search_string, buf);
- else if (!strlen(subject_search_string)) {
- info_message("No search string");
- return;
- }
-
- i = index_point;
-
- make_lower(subject_search_string, buf);
- len = strlen(buf);
-
- do {
- if (forward)
- i++;
- else
- i--;
-
- if (i >= top_base)
- i = 0;
- if (i < 0)
- i = top_base - 1;
-
- make_lower(arts[base[i]].subject, buf2);
- if (match(buf, buf2, len)) {
- if (i >= first_subj_on_screen
- && i < last_subj_on_screen) {
- erase_subject_arrow();
- index_point = i;
- draw_subject_arrow();
- } else {
- index_point = i;
- show_group_page(group);
- }
- return;
- }
- } while (i != index_point);
-
- info_message("No match");
- }
-
-
- /*
- * Post an original article (not a followup)
- */
-
- post_base(group)
- char *group;
- {
- FILE *fp;
- char nam[100];
- char ch;
- char subj[LEN+1];
- char buf[200];
-
- if (!parse_string("Subject: ", subj))
- return;
- if (subj[0] == '\0')
- return;
-
- setuid(real_uid);
- setgid(real_gid);
-
- sprintf(nam, "%s/.article", homedir);
- if ((fp = fopen(nam, "w")) == NULL) {
- fprintf(stderr, "can't open %s: ", nam);
- perror("");
- setuid(tass_uid);
- setgid(tass_gid);
- return(FALSE);
- }
- chmod(nam, 0600);
-
- fprintf(fp, "Subject: %s\n", subj);
- fprintf(fp, "Newsgroups: %s\n", group);
- fprintf(fp, "Distribution: \n");
- if (*my_org)
- fprintf(fp, "Organization: %s\n", my_org);
- fprintf(fp, "\n");
-
- add_signature(fp, FALSE);
- fclose(fp);
-
- ch = 'e';
- while (1) {
- switch (ch) {
- case 'e':
- invoke_editor(nam);
- break;
-
- case 'a':
- setuid(tass_uid);
- setgid(tass_gid);
- return FALSE;
-
- case 'p':
- printf("\nPosting... ");
- fflush(stdout);
- sprintf(buf, "%s/inews -h < %s", LIBDIR, nam);
- if (invoke_cmd(buf)) {
- printf("article posted\n");
- fflush(stdout);
- goto post_base_done;
- } else {
- printf("article rejected\n");
- fflush(stdout);
- break;
- }
- }
-
- do {
- MoveCursor(LINES, 0);
- fputs("abort, edit, post: ", stdout);
- fflush(stdout);
- ch = ReadCh();
- } while (ch != 'a' && ch != 'e' && ch != 'p');
- }
-
- post_base_done:
- setuid(tass_uid);
- setgid(tass_gid);
-
- continue_prompt();
-
- return(TRUE);
- }
-
-
- /*
- * Return the number of unread articles there are within a thread
- */
-
- new_responses(thread)
- int thread;
- {
- int i;
- int sum = 0;
-
- for (i = base[thread]; i >= 0; i = arts[i].thread)
- if (arts[i].unread)
- sum++;
-
- return sum;
- }
-
-
- tass_group_help() {
- char title[100];
-
- sprintf(title, "%s, Index Page Commands", TASS_HEADER);
- ClearScreen();
- center_line(0, title);
-
- MoveCursor(2, 0);
-
- printf("\t4\tSelect article 4\r\n");
- printf("\t<CR>\tRead current article\r\n");
- printf("\t<TAB>\tView next unread article or group\r\n");
- printf("\t^D^U\tPage down (^U=page up)\r\n");
- printf("\taA\tAuthor search forward (A=backward)\r\n");
- printf("\tc\tMark all articles as read\r\n");
- printf("\tg\tChoose a new group by name\r\n");
- printf("\tjk\tDown (k=up) a line\r\n");
- printf("\tK\tMark thread as read & advance\r\n");
- printf("\tnp\tGo to next (p=previous) group\r\n");
- printf("\tNP\tGo to next (P=previous) unread article\r\n");
- printf("\tq\tQuit\r\n");
- printf("\tsu\tSubscribe (u=unsubscribe) to this group\r\n");
- printf("\tt\tReturn to group selection index\r\n");
- printf("\tw\tPost an article\r\n");
- printf("\t/?\tSearch forward (?=backward) for subject\r\n");
- printf("\t-\tShow last article\r\n");
-
- center_line(LINES, "-- hit any key --");
- ReadCh();
- }
-
- @EOF
-
- chmod 644 group.c
-
- echo x - hashstr.c
- cat >hashstr.c <<'@EOF'
-
- #include <stdio.h>
-
-
- /*
- * Maintain a table of all strings we have seen.
- * If a new string comes in, add it to the table and return a pointer
- * to it. If we've seen it before, just return the pointer to it.
- *
- * Usage: hash_str("some string") returns char *
- *
- * Spillovers are chained on the end
- */
-
-
- /*
- * Arbitrary table size, but make sure it's prime!
- */
-
- /* #define TABLE_SIZE 1409 */
-
- #define TABLE_SIZE 2411
-
-
-
- struct hashnode {
- char *s; /* the string we're saving */
- struct hashnode *next; /* chain for spillover */
- };
-
- struct hashnode *table[ TABLE_SIZE ];
-
- extern char *my_malloc();
- struct hashnode *add_string();
-
-
- char *
- hash_str(s)
- char *s;
- {
- struct hashnode *p; /* used to descend the spillover structs */
- long h; /* result of hash: index into hash table */
-
- if (s == NULL)
- return(NULL);
-
- {
- char *t = s;
-
- h = *t++;
- while (*t)
- h = ((h << 1) ^ *t++) % TABLE_SIZE;
- /* h = (h * 128 + *t++) % TABLE_SIZE; */
- }
-
- p = table[h];
-
- if (p == NULL) {
- table[h] = add_string(s);
- return table[h]->s;
- }
-
- while (1) {
- if (strcmp(s, p->s) == 0)
- return(p->s);
-
- if (p->next == NULL) {
- p->next = add_string(s);
- return p->next->s;
- } else
- p = p->next;
- }
- }
-
-
- struct hashnode *
- add_string(s)
- char *s;
- {
- struct hashnode *p;
- extern char *strcpy();
- int *iptr;
-
- p = (struct hashnode *) my_malloc(sizeof(*p));
-
- p->next = NULL;
- iptr = (int *) my_malloc(strlen(s) + sizeof(int) + 1);
- *iptr++ = -1;
- p->s = (char *) iptr;
- strcpy(p->s, s);
- return(p);
- }
-
-
- hash_init() {
- int i;
-
- for (i = 0; i < TABLE_SIZE; i++)
- table[i] = NULL;
- }
-
-
- hash_reclaim() {
- int i;
- struct hashnode *p, *next;
- int *iptr;
-
- for (i = 0; i < TABLE_SIZE; i++)
- if (table[i] != NULL) {
- p = table[i];
- while (p != NULL) {
- next = p->next;
- iptr = (int *) p->s;
- free(--iptr);
- free(p);
- p = next;
- }
- table[i] = NULL;
- }
- }
-
- @EOF
-
- chmod 600 hashstr.c
-
- exit 0
-