amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (02/07/91)
Submitted-by: lordbah@amusing.bisco.kodak.COM (Jeff Van Epps) Posting-number: Volume 91, Issue 007 Archive-name: news/barn-2.01/part02 #!/bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 2 (of 2)." # Contents: arn.c barn.doc # Wrapped by tadguy@ab20 on Wed Feb 6 20:05:20 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'arn.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'arn.c'\" else echo shar: Extracting \"'arn.c'\" \(44397 characters\) sed "s/^X//" >'arn.c' <<'END_OF_FILE' X/* X * File Name: arn.c X * Project: BARN - Bah's Amiga ReadNews. X * Purpose: Mainline code for Amiga News Reader. X * Functions: main. X * Author: Jeff Van Epps X * Created: 02 Sep 89 X * Last Modified: 15 Jan 91 X * Comments: X * barn [config_file] X * X * config_file is optional, defaulting to "barn.config". X * X * History: X * 02 Sep 89/JVE Created. X * 03 Dec 89/JVE Tries to cope with "Re: $x" subject lines when finding X * next article to read and when killing articles. X * 16 Dec 89/JVE Fixed a bug in the last mark in UpdateReadList if the X * last article was still unread. X * 30 Dec 89/JVE Implemented kill-list checking. X * 05 Sep 90/JVE Removed volume references -- run from base directory X * of news articles. X * 12 Sep 90/JVE Added parm to Paginate() to account for headers X * already printed. X * 17 Oct 90/JVE Added '-' option to go back to previous article. X * Made Paginate() pass up the 'K' option. X * Added help option. Added Reply and Followup. X * 19 Oct 90/JVE UNIX (Sun) port. Curses, readdir, etc. X * 20 Oct 90/JVE Added configuration file stuff. X * 22 Oct 90/JVE Use regular expressions for subject following and X * killing. X * 24 Oct 90/JVE Added scan subjects command "=". X * 05 Nov 90/JVE New opendir() and stat() functions from SAS/C used. X * 10 Nov 90/JVE Make sure articles shown in order of posting. X * 11 Nov 90/JVE Added LINES/COLS to config file. X * 14 Nov 90/JVE Added "m" - mark as unread, and use of "n" (next) X * within pager. Added CR/NL to print one more line X * in pager, scanner. X * 16 Nov 90/JVE Scanner fix -- used to always show first article in X * group even if already read. X * 22 Nov 90/JVE End page at form feed. X * 25 Nov 90/JVE Config option to clear screen for each new page. X * 28 Nov 90/JVE Fix to MakePattern to escape backslash in regexp. X * Changed getcmd() to take array of strings for "help" X * to escape compiler string length limitation. Changed X * MakePattern() to allow characters between 'Re:' and X * 'text' being matched. X * 05 Jan 91/JVE Fixed line-wrap calculations in Paginate(). Put last X * line of previous page at top of next page when in X * NO_SCROLL mode. Put 'subject' in article_info as an X * an optimization, so header list didn't have to be X * searched constantly. Added '#' command to go to X * specific article by number. X * 06 Jan 91/JVE Added back-page capability in Paginate(). Changed X * name to "barn" to avoid conflict with another "arn" X * in the Amiga world which performs the same function. X * Changed getcmd() to accept one list of commands which X * gets displayed to the user and one which doesn't. X * Added "a - About BARN" command. X * 15 Jan 91/JVE Avoid regexec() on NULL subject. X */ X X# include <stdio.h> X# include <stdlib.h> X# include <string.h> X# include <regexp.h> X X# ifdef sun X# include <dirent.h> /* opendir & friends */ X# else /* not sun */ X# include <sys/types.h> X# include <sys/dir.h> X# endif /* sun */ X X# include <sys/stat.h> X X# include "standard.h" X# include "article.h" X# include "ng.h" X# include "kill.h" X# include "configure.h" X# include "variables.h" X# include "reply.h" X# include "screenstuff.h" X X# define BARN_VERSION "BARN version 2.01" X X# define DEFAULT_CONFIG "barn.config" /* ARN default configuration file */ X# define DEFAULT_NEWSRC ".newsrc" /* default .newsrc filename */ X# define DEFAULT_KILL "KILL" /* default name of kill files */ X# define KILL "KILL" /* KILL filenames start with this */ X# define REPLY_PATTERN "^[RrEe: ]*" /* regex for reply articles */ X# define REPLY_REMOVER "^[RrEe: ]*(.*)$" /* regex for removing Re: */ X X# ifdef sun X X/* LINES and COLS are variables set by curses. */ X X# define Lines LINES X# define Columns COLS X X# endif /* sun */ X X# ifdef sun X X# define COPY_CMD "cp" X# define raw( x ) cbreak() X# define cooked( x ) nocbreak() X# define printf printw X# define gets( x ) getstr( (x) ) X# define getchar() getch() X X# else /* not sun */ X X# define COPY_CMD "copy" X X# endif /* sun */ X Xtypedef struct { X long num; /* article number */ X ARTICLE_INFO *pointer; /* article structure */ X } SORT_INFO; X Xstatic char *currentdir; /* current directory on startup */ X X# ifndef sun X Xstatic int Lines, Columns; /* terminal characteristics */ X X# endif X X/* X * Local function which creates the string form and internal form of a X * regular expression. The string form should have all regexp-special X * characters escaped. X */ X Xstruct regexp *MakePattern( char *pattern, char *original ); X Xvoid ScanSubjects( ARTICLE_INFO *arts ); X X Xvoid main( argc, argv ) X Xint argc; Xchar **argv; X X{ XNG_INFO *alreadyread; /* list of newsgroups/articles already read */ XNG_INFO *ng; /* ptr for newsgroup list traversal */ XKILL_INFO *globalkill; /* entries from global KILL file */ Xchar *config_file; /* name of configuration file to use */ Xchar *newsrc_file; /* name of .newsrc file */ Xchar *global_kill_file; /* name of global kill file */ Xvoid Startup(), CheckConfiguration(); Xextern char *getcwd(); X Xcurrentdir = getcwd( NULLP( char ), BUFSIZ ); Xif ( argc < 2 ) X config_file = DEFAULT_CONFIG; Xelse X config_file = argv[1]; XConfigure( config_file ); XCheckConfiguration(); XStartup(); X X/* X * Find all articles we've already read from the .newsrc file. X */ X Xif ( ( newsrc_file = GetVar( VAR_NEWSRC ) ) == NULL ) X newsrc_file = DEFAULT_NEWSRC; Xalreadyread = GetNewsRC( newsrc_file ); Xif ( ( global_kill_file = GetVar( VAR_KILL ) ) == NULL ) X global_kill_file = DEFAULT_KILL; Xglobalkill = GetKillList( global_kill_file ); X X/* X * For each newsgroup in the .newsrc, handle the newsgroup. X * (Code needs to be added to notice newsgroups not in .newsrc) X */ X Xfor ( ng = alreadyread; ng != NULLP( NG_INFO ); ng = ng->next ) X { X chdir( currentdir ); X if ( ReadNewsgroup( ng, &globalkill ) ) X break; X } X Xchdir( currentdir ); XPutNewsRC( newsrc_file, alreadyread ); XPutKillList( global_kill_file, globalkill ); X XDestroyNGList( alreadyread ); XDestroyKillList( globalkill ); X Xcooked( stdin ); X X# ifdef sun Xendwin(); X# endif X} X X X/****************************************************************************/ X/* FUNCTION: AboutBARN */ X/* */ X/* PURPOSE: Print version of BARN. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* */ X/* RETURNS: none */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 06 Jan 91 Created. */ X/* */ X/****************************************************************************/ X Xvoid AboutBARN() X X{ Xprintf( "\n\n%s written by Jeff Van Epps (aka Lord Bah)\n\n", BARN_VERSION ); Xprintf( "raw.c written by Chuck McManis\n" ); Xprintf( "sendpacket.c written by Phil Lindsay, Carolyn Scheppner, and Andy Finkel\n" ); Xprintf( "Uses regexp library written by Henry Spencer, copyrighted by the\n" ); Xprintf( "University of Toronto in 1986.\n" ); Xprintf( "\nExecutable freely redistributable with the restriction that\n" ); Xprintf( "the authors are not responsible for consequences of use.\n" ); Xprintf( "\nAddress comments to amusing!jeffv@bisco.kodak.com or lordbah@cup.portal.com.\n" ); Xprintf( "\nHit any key to continue" ); X(void) getchar(); Xprintf( "\n" ); X} X X X/****************************************************************************/ X/* FUNCTION: Startup */ X/* */ X/* PURPOSE: Get ready to run ARN. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* */ X/* RETURNS: */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 05 Sep 89 Created. */ X/* */ X/****************************************************************************/ X Xvoid Startup() X X{ X# ifdef sun X(void) initscr(); X# endif Xraw( stdin ); X} X X X/****************************************************************************/ X/* FUNCTION: CheckConfiguration */ X/* */ X/* PURPOSE: Check to see that user has defined required variables in */ X/* the configuration file. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* */ X/* RETURNS: none */ X/* */ X/* COMMENTS: */ X/* Exits if any of the required variables are not set. */ X/* */ X/* HISTORY: */ X/* 1. 20 Oct 90 Created. */ X/* 2. 11 Nov 90 Added LINES & COLS. */ X/* */ X/****************************************************************************/ X Xstatic void CheckConfiguration() X X{ Xchar *tmp; X Xif ( GetVar( VAR_USER ) == NULL || GetVar( VAR_NODE ) == NULL || X GetVar( VAR_DOMAIN ) == NULL || GetVar( VAR_NAME ) == NULL ) X { X fprintf( stderr, "Must define \"%s\", \"%s\", \"%s\", and \"%s\" in configuration file.\n", X VAR_USER, VAR_NODE, VAR_DOMAIN, VAR_NAME ); X exit( 1 ); X } X X# ifndef sun X Xif ( ( ( tmp = GetVar( VAR_LINES ) ) == NULL ) || X ( Lines = atoi( tmp ) ) < 10 || X Lines > 48 ) X { X fprintf( stderr, "Missing/unreasonable '%s' value in config file.\n", X VAR_LINES ); X exit( 1 ); X } Xif ( ( ( tmp = GetVar( VAR_COLS ) ) == NULL ) || X ( Columns = atoi( tmp ) ) < 10 || X Columns > 133 ) X { X fprintf( stderr, "Missing/unreasonable '%s' value in config file.\n", X VAR_COLS ); X exit( 1 ); X } X X# endif X X} X X X/****************************************************************************/ X/* FUNCTION: ReadNewsgroup */ X/* */ X/* PURPOSE: Allow user to read articles within a newsgroup. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* ng I/O List of articles read within newsgroup. */ X/* gkill I/O Global kill list. */ X/* */ X/* RETURNS: */ X/* TRUE If user wants to quit. */ X/* FALSE Otherwise. */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 04 Sep 89 Created. */ X/* 2. 06 Jan 91 Added "a" - About BARN command. */ X/* */ X/****************************************************************************/ X X# define READ_CMDS "acnqy " Xstatic char *READ_HELP[] = { X"<space> Yes, read this newsgroup.", X" a About BARN.", X" c Catch up. Mark everything in this newsgroup as read.", X" n No, don't read this newsgroup.", X" q Quit ARN.", X" y Yes, read this newsgroup.", X(char *) NULL X}; X XReadNewsgroup( ng, gkill ) X XNG_INFO *ng; XKILL_INFO **gkill; X X{ XKILL_INFO *lkill; /* local (within newsgroup) kill list */ XDIR *dir; /* pointer to directory structure */ X# ifdef sun Xstruct dirent *info; /* pointer to each file's info */ X# else /* not sun, Amiga */ Xstruct direct *info; X# endif /* sun */ Xstruct stat stats; /* file system information about file */ XARTICLE_INFO *todo; /* articles to be read */ XARTICLE_INFO *art; /* info on eligible article */ XARTICLE_INFO **where; /* where to link in next eligible article */ XARTICLE_INFO *tmp; Xchar *fn; /* name of file currently being checked */ Xint narticles; /* number of unread articles in newsgroup */ Xchar *kill_file; /* name of local kill file */ XARTICLE_INFO *CheckArticle(); Xint rc = FALSE, done = FALSE; Xvoid ReadArticles(), SortArticles(), UpdateReadList(); X Xif ( chdir( ng->name ) != 0 ) X fprintf( stderr, "Newsgroup directory %s missing!\n", ng->name ); Xelse X { X printf( "Checking %s ...\n", ng -> name ); X if ( ( kill_file = GetVar( VAR_KILL ) ) == NULL ) X kill_file = DEFAULT_KILL; X lkill = GetKillList( kill_file ); X /* X * For each file in directory, check vs. already read (ng), check X * vs. global kill (gkill), check vs. local kill (lkill). If killed, X * add to already read (ng). If not read and not killed, put on X * unread list (todo). X */ X# ifdef sun X if ( ( ( dir = opendir( "." ) ) == NULL ) || X# else /* Amiga */ X if ( ( ( dir = opendir( "" ) ) == NULL ) || X# endif X ( info = readdir( dir ) ) == NULL ) X printf( "%s is empty.\n", ng->name ); X else X { X todo = NULLP( ARTICLE_INFO ); X where = &todo; X narticles = 0; X do { X /* X * If filename starts with "KILL", this is a KILL file, not a X * news article. Don't open anything but regular files. X */ X if ( strncmp( fn = info -> d_name, KILL, strlen(KILL) ) != 0 && X stat( fn, &stats ) == 0 && ( stats.st_mode & S_IFREG ) ) X if ( art = CheckArticle( fn, ng->markers, *gkill, lkill ) ) X { X *where = art; X where = & (*where)->next; X ++narticles; X } X } while ( ( info = readdir( dir ) ) != NULL ); X closedir( dir ); X /* talk to user */ X if ( narticles == 0 ) X printf( "%s has no unread articles.\n", ng->name ); X else while ( !done ) X { X printf( "%s has %d unread articles. Read now? ", ng->name, narticles ); X switch ( getcmd( READ_CMDS, READ_HELP, "" ) ) { X case ' ': X case 'y': X SortArticles( &todo, narticles ); X for(art = todo; art != NULLP(ARTICLE_INFO); art = art->next) X if ( art->beenread ) X --narticles; X ReadArticles( ng, todo, narticles, gkill, &lkill ); X printf( "\n" ); X UpdateReadList( ng, todo ); X done = TRUE; X break; X case 'q': X rc = TRUE; X printf( "\n" ); X done = TRUE; X break; X case 'c': /* catch up by marking all articles read */ X printf( "\n" ); X for ( tmp = todo; tmp != NULLP( ARTICLE_INFO ); tmp = tmp->next ) X tmp->beenread = TRUE; X UpdateReadList( ng, todo ); X done = TRUE; X break; X case 'a': X AboutBARN(); X break; X case 'n': /* don't read now, just goto cleanup below */ X printf( "\n" ); X done = TRUE; X break; X default: X break; X } X } X /* kill any remaining todo list */ X for ( ; todo != NULLP( ARTICLE_INFO ); todo = tmp ) X { X tmp = todo->next; X DestroyArticle( todo ); X } X } X /* X * Write the local kill file to disk in case we changed it. X */ X PutKillList( kill_file, lkill ); X DestroyKillList( lkill ); X } Xreturn rc; X} X X X/****************************************************************************/ X/* FUNCTION: CheckArticle */ X/* */ X/* PURPOSE: Check readability of an article. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* filename I Name of file containing article to be checked. */ X/* markers I Pointer to list of articles already read. */ X/* gkill I Global kill list. */ X/* lkill I Local kill list. */ X/* */ X/* RETURNS: */ X/* (ARTICLE_INFO *) Pointer to article, if readable. */ X/* (NULL) If not readable. */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 04 Sep 89 Created. */ X/* 2. 30 Dec 89 Implemented kill-list checking. */ X/* 3. 22 Oct 90 Changed from string compare to regular */ X/* expression match. */ X/* 4. 24 Oct 90 Tells what it killed and why. */ X/* */ X/****************************************************************************/ X XARTICLE_INFO *CheckArticle( filename, markers, gkill, lkill ) X Xchar *filename; XMARKER *markers; XKILL_INFO *gkill, *lkill; X X{ Xlong article_number; XARTICLE_INFO *art; XHEADER_INFO *hdr; XKILL_INFO *kp; Xint done = FALSE; X Xarticle_number = atol( filename ); Xif ( NumberCovered( markers, article_number ) ) X return NULLP( ARTICLE_INFO ); Xif ( ( art = ParseArticle( filename ) ) == NULL ) X return art; X X/* X * Check kill lists. X */ X Xfor ( hdr = art->headers; hdr != NULLP(HEADER_INFO) && !done; hdr = hdr->next ) X { X for ( kp = gkill; kp != NULLP( KILL_INFO ) && !done; kp = kp->next ) X if ( strcmp( hdr->fieldname, kp->fieldname ) == 0 && X regexec( kp -> pattern, hdr -> fieldvalue ) == 1 ) X { X done = art->beenread = TRUE; X printf( "Killed %ld, %s: %s\n", art -> number, X hdr -> fieldname, hdr -> fieldvalue ); X } X for ( kp = lkill; kp != NULLP( KILL_INFO ) && !done; kp = kp->next ) X if ( strcmp( hdr->fieldname, kp->fieldname ) == 0 && X regexec( kp -> pattern, hdr -> fieldvalue ) == 1 ) X { X done = art->beenread = TRUE; X printf( "Killed %ld, %s: %s\n", art -> number, X hdr -> fieldname, hdr -> fieldvalue ); X } X } Xreturn art; X} X X X/****************************************************************************/ X/* FUNCTION: FindNextArticle */ X/* */ X/* PURPOSE: Find next article on same subject thread. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* current I Last article displayed. */ X/* sub I Subject line of last article. */ X/* found O Indicate whether or not we found another art. */ X/* top I Head of list of articles. */ X/* */ X/* RETURNS: */ X/* (ARTICLE_INFO *) next article on subject thread. */ X/* */ X/* COMMENTS: */ X/* Returns "current" if no match is found. */ X/* */ X/* HISTORY: */ X/* 1. 14 Nov 90 Created. */ X/* */ X/****************************************************************************/ X XARTICLE_INFO *FindNextArticle( current, sub, found, top ) X XARTICLE_INFO *current; Xchar *sub; Xint *found; XARTICLE_INFO *top; X X{ XARTICLE_INFO *p; Xchar temp[MAXLINE]; Xstruct regexp *subre; /* regular expression pattern for subj match */ X X*found = FALSE; Xif ( ( subre = MakePattern( temp, sub ) ) == NULL ) X fprintf( stderr, "Can't compile regex '%s'\n", temp ); Xfor ( p = current->next; p != NULLP( ARTICLE_INFO ) && p != current; ) X { X if ( ! p->beenread ) X if ( p -> subject ) X if ( regexec( subre, p -> subject ) == 1 ) X { X current = p; X *found = TRUE; X } X if ( *found ) X break; X if ( ( p = p->next ) == NULLP( ARTICLE_INFO ) ) X p = top; X } Xif ( subre != NULL ) X free( (char *) subre ); Xreturn p; X} X X/****************************************************************************/ X/* FUNCTION: ReadArticles */ X/* */ X/* PURPOSE: Allow user to read articles within a newsgroup. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* ng I/O List of articles read. */ X/* todo I/O List of articles to be read. */ X/* narticles I Number of unread articles. */ X/* gkill I/O Global kill list. */ X/* lkill I/O Local kill list. */ X/* */ X/* RETURNS: */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 04 Sep 89 Created. */ X/* 2. 17 Oct 90 Added '-' command to go to previous article. */ X/* 3. 10 Nov 90 Find next art from beginning if not following */ X/* Subject thread. Makes sure articles get shown */ X/* in order of posting. */ X/* 4. 28 Nov 90 Added "/" to let user specify Subject to match. */ X/* 5. 05 Jan 91 Added "#" to go to specific article number. */ X/* 6. 06 Jan 91 Added "a" command for About BARN information. */ X/* */ X/****************************************************************************/ X X# define CMDS "acFfjKkmnqRrwy-=/# " Xstatic char *CMDS_HELP[] = { X" a About BARN.", X" c Catch up. Mark all articles in this newsgroup as read.", X" Ff Followup, 'F' includes current article.", X" Kk Kill articles with this Subject, 'K' adds to KILL file.", X" m Mark this article as unread.", X" n,j Mark this article as read.", X" q Quit this newsgroup.", X" Rr Reply via mail, 'R' includes current article.", X" w Write this article. Prompts for filename.", X" y,<sp> Read this article.", X" - Go to previous article, marking it as unread.", X" = Scan subjects.", X" / Find next article with subject entered.", X" # Go to numbered article.", X(char *) NULL X}; X Xvoid ReadArticles( ng, todo, narticles, gkill, lkill ) X XNG_INFO *ng; XARTICLE_INFO *todo; Xint narticles; XKILL_INFO **gkill, **lkill; X X{ Xint cmd; /* user's command */ XARTICLE_INFO *current; /* article currently being examined */ XARTICLE_INFO *previous = NULLP( ARTICLE_INFO ); /* last art. examined */ XARTICLE_INFO *last; /* used to track previous */ XARTICLE_INFO *p; XKILL_INFO *tmp; Xchar temp[BUFSIZ], buf[BUFSIZ], *sub, followsub[BUFSIZ]; Xint readit; /* did article get read? */ Xint found; /* found another article with same subject? */ Xint follow = FALSE; /* following subject thread? */ Xint lines; /* lines used on screen for header */ Xstruct regexp *subre; /* regular expression pattern for subj match */ Xvoid DisplayHeaders(); XARTICLE_INFO *FindNumberedArticle(); X Xcurrent = todo; Xwhile ( narticles > 0 ) X { X while ( current->beenread ) X if ( ( current = current->next ) == NULLP( ARTICLE_INFO ) ) X current = todo; X Clear_Screen; X printf( "Article %ld (%d more) in %s\n", current->number, narticles - 1, ng->name ); X lines = 1; X DisplayHeaders( current, &lines, &sub ); X printf( "\nRead? " ); X ++lines; X readit = FALSE; X last = current; X if ( ( cmd = getcmd( CMDS, CMDS_HELP, "" ) ) == ' ' || cmd == 'y' ) X { X cmd = Paginate( current, lines ); X readit = TRUE; X } X found = FALSE; X switch ( cmd ) { X case ' ': X case 'y': X current->beenread = TRUE; X if ( --narticles == 0 ) X break; X /* X * Find next article with same subject. X */ X if ( !follow ) X strcpy( followsub, sub ); X current = FindNextArticle( current, followsub, &found, todo ); X follow = found; X break; X case 'a': X AboutBARN(); X found = TRUE; /* don't let us go to top of newsgroup */ X break; X case '/': X cooked( stdin ); X printf( "/" ); X gets( buf ); X current = FindNextArticle( current, buf, &found, todo ); X if ( follow = found ) X strcpy( followsub, buf ); X else X printf( "Not found.\n" ); X raw( stdin ); X break; X case '#': X cooked( stdin ); X printf( "#" ); X gets( buf ); X current = FindNumberedArticle( todo, (long) atol( buf ) ); X if ( current == NULLP( ARTICLE_INFO ) ) X { X printf( "Not found.\n" ); X current = last; X } X else if ( current -> beenread ) X { X current -> beenread = FALSE; X ++narticles; X } X raw( stdin ); X found = TRUE; /* so we don't start at top of newsgroup */ X break; X case 'k': X case 'K': X /* kill */ X current->beenread = TRUE; X --narticles; X printf( "\nKilling %s ...\n", sub ); X if ( ( subre = MakePattern( temp, sub ) ) == NULL ) X fprintf( stderr, "Can't compile regex '%s'\n", temp ); X for ( p = current->next; p != NULLP( ARTICLE_INFO ) && p != current; ) X { X if ( ! p->beenread ) X if ( p -> subject && regexec( subre, p -> subject ) == 1 ) X { X p->beenread = TRUE; X --narticles; X } X if ( ( p = p->next ) == NULLP( ARTICLE_INFO ) ) X p = todo; X } X if ( cmd == 'K' ) X { X printf( "Adding to newsgroup KILL file..." ); X tmp = (KILL_INFO *) malloc( sizeof( KILL_INFO ) ); X tmp->fieldname = strdup( HDR_SUBJECT ); X tmp->fieldvalue = strdup( temp ); X tmp->pattern = subre; X tmp->next = *lkill; X *lkill = tmp; X printf( "Done\n" ); X } X else if ( subre != NULL ) X free( (char *) subre ); X follow = FALSE; X break; X case 'n': X case 'j': X /* X * Mark this article as read and move on to the next one with X * the same subject. X */ X current->beenread = TRUE; X --narticles; X /* FALL THROUGH */ X case 'm': X /* X * "Mark as unread". This simply goes on to the next unread X * article without marking this one as read. Better marking X * routines are possible. X */ X if ( !follow ) X strcpy( followsub, sub ); X current = FindNextArticle( current, followsub, &found, todo ); X follow = found; X if ( !found && cmd == 'm' ) X { X if ( ( current = current -> next ) == NULLP( ARTICLE_INFO ) ) X current = todo; X } X else if ( !found ) X current = todo; X break; X case 'q': X if ( readit ) X current->beenread = TRUE; X narticles = 0; /* break out of while loop */ X break; X case 'c': /* catch up by marking all articles read */ X for ( current = todo; narticles > 0; current = current->next ) X { X if ( ! current->beenread ) X --narticles; X current->beenread = TRUE; X } X break; X case 'w': /* write article to top directory */ X if ( readit ) X { X current->beenread = TRUE; X --narticles; X } X cooked( stdin ); X printf( "\nFilename: " ); X gets( buf ); X raw( stdin ); X sprintf( temp, "%s %ld %s/%s", COPY_CMD, current->number, X currentdir, buf ); X system( temp ); X break; X case 'r': X Reply( current, FALSE ); X break; X case 'R': X Reply( current, TRUE ); X break; X case 'f': X Followup( ng -> name, current, FALSE ); X break; X case 'F': X Followup( ng -> name, current, TRUE ); X break; X case '-': X if ( previous != NULL ) X { X current = previous; X if ( current -> beenread ) X { X current -> beenread = FALSE; X ++narticles; X } X } X break; X case '=': /* subject scan */ X ScanSubjects( todo ); X break; X default: X break; X } X previous = last; X if ( !found ) X current = todo; /* if not following thread, start at top */ X } X} X X X/****************************************************************************/ X/* FUNCTION: DisplayHeaders */ X/* */ X/* PURPOSE: Print interesting article headers. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* current I Pointer to current article. */ X/* lines O Pointer to count of lines printed on screen. */ X/* sub O Pointer to current subject pointer. */ X/* */ X/* RETURNS: none */ X/* */ X/* COMMENTS: */ X/* Prints all headers stored in the articles header list, keeping */ X/* track of the number of lines printed on the screen and returning */ X/* that in 'lines'. When the 'Subject' header is encountered, a */ X/* pointer to its value is stored in 'sub'. */ X/* */ X/* HISTORY: */ X/* 1. 05 Jan 91 Factored out of ReadArticles(). */ X/* */ X/****************************************************************************/ X Xvoid DisplayHeaders( current, lines, sub ) X XARTICLE_INFO *current; Xint *lines; Xchar **sub; X X{ XHEADER_INFO *ptr; X Xfor ( ptr = current->headers; ptr != NULLP( HEADER_INFO ); ptr = ptr->next ) X { X ++*lines; X if ( strcmp( ptr->fieldname, HDR_SUBJECT ) == 0 ) X { X *sub = ptr->fieldvalue; X printf( "%s: ", ptr -> fieldname ); X UL_ON; X printf( "%s", *sub ); X UL_OFF; X printf( "\n" ); X if ( strlen( ptr->fieldname ) + strlen( *sub ) + 2 > Columns ) X ++*lines; X } X else X { X printf( "%s: %s\n", ptr->fieldname, ptr->fieldvalue ); X if ( strlen( ptr->fieldname ) + strlen( ptr->fieldvalue ) + 2 > Columns ) X ++*lines; X } X } X} X X X/****************************************************************************/ X/* FUNCTION: FindNumberedArticle */ X/* */ X/* PURPOSE: Return pointer to article identified by given number. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* head I Pointer to head of article list. */ X/* number I Number of article to find. */ X/* */ X/* RETURNS: */ X/* NULL No such numbered article. */ X/* ARTICLE_INFO *ptr Pointer to numbered article. */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 05 Jan 91 Created. */ X/* */ X/****************************************************************************/ X XARTICLE_INFO *FindNumberedArticle( head, number ) X XARTICLE_INFO *head; Xlong number; X X{ XARTICLE_INFO *p; X Xfor ( p = head; p != NULLP( ARTICLE_INFO ); p = p -> next ) X if ( p -> number == number ) X return p; X else if ( p -> number > number ) X break; Xreturn NULLP( ARTICLE_INFO ); X} X X/****************************************************************************/ X/* FUNCTION: SortArticles */ X/* */ X/* PURPOSE: Sort article list by article number. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* arts I/O Pointer to list of articles. */ X/* narticles I Number of articles in list. */ X/* */ X/* RETURNS: none */ X/* */ X/* COMMENTS: */ X/* *arts is modified. */ X/* */ X/* HISTORY: */ X/* 1. 04 Sep 89 Created. */ X/* */ X/****************************************************************************/ X Xvoid SortArticles( arts, narticles ) X XARTICLE_INFO **arts; Xint narticles; X X{ XARTICLE_INFO *ptr; XARTICLE_INFO **where; XSORT_INFO *articles; Xint i; Xint Compare(); X Xarticles = (SORT_INFO *) malloc( sizeof( SORT_INFO ) * narticles ); Xfor ( ptr = *arts, i = 0; i < narticles; i++, ptr = ptr->next ) X { X articles[i].pointer = ptr; X articles[i].num = ptr->number; X } Xqsort( (char *) articles, narticles, sizeof( SORT_INFO ), Compare ); Xwhere = arts; Xfor ( i = 0; i < narticles; i++ ) X { X *where = articles[i].pointer; X where = &(*where)->next; X } X*where = NULLP( ARTICLE_INFO ); Xfree( articles ); X} X X X/****************************************************************************/ X/* FUNCTION: Compare */ X/* */ X/* PURPOSE: Help function to sort articles. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* as defined by qsort() */ X/* */ X/* RETURNS: */ X/* as defined by qsort() */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 04 Sep 89 Created. */ X/* */ X/****************************************************************************/ X XCompare( first, second, size ) X XSORT_INFO *first, *second; Xint size; X X{ Xif ( first->num < second->num ) X return -1; Xelse if ( first->num > second->num ) X return 1; Xelse X return 0; X} X X X/****************************************************************************/ X/* FUNCTION: getcmd */ X/* */ X/* PURPOSE: Get a command from the user. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* cmdlist I List of valid commands. */ X/* help I Array of help strings to display for user. */ X/* otherlist I List of other valid commands that aren't shown */ X/* to the user. */ X/* */ X/* RETURNS: */ X/* */ X/* COMMENTS: */ X/* The commands string must not allow 'h', 'H', or '?' as these are */ X/* used to view the help string. */ X/* */ X/* HISTORY: */ X/* 1. 04 Sep 89 Created. */ X/* 2. 17 Oct 90 Added help options. */ X/* 3. 14 Nov 90 Added CR/NL. */ X/* 4. 28 Nov 90 Made "help" array of strings to escape compiler */ X/* string length limitations. */ X/* 5. 06 Jan 91 Generalized special cases of CR/NL to otherlist.*/ X/* */ X/****************************************************************************/ X Xgetcmd( cmdlist, help, otherlist ) X Xchar *cmdlist; Xchar **help; Xchar *otherlist; X X{ Xint ch; Xchar **p; X Xch = '"'; Xprintf( "[%s%s] ", cmdlist, ( strchr( otherlist, (char) '\r' ) ? "<CR>" : "" ) ); Xwhile ( strchr( cmdlist, (char) ch ) == NULLP( char ) ) X { X ch = getchar(); X if ( ch == 'h' || ch == 'H' || ch == '?' ) /* user wants help */ X { X INVERSE_OFF; X printf( "\n" ); X for ( p = help; *p != (char *) NULL; p++ ) X printf( "%s\n", *p ); X printf( "\n" ); X printf( "[%s%s] ", cmdlist, ( strchr( otherlist, (char) '\r' ) ? "<CR>" : "" ) ); X } X else if ( strchr( otherlist, (char) ch ) != NULLP( char ) ) X break; X } Xreturn ch; X} X X X/****************************************************************************/ X/* FUNCTION: Paginate */ X/* */ X/* PURPOSE: Display article one page at a time. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* article I Article to be displayed. */ X/* lines I Lines already used on display. */ X/* */ X/* RETURNS: */ X/* Last command character hit by user if it needs to be processed */ X/* by caller. */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 28 Sep 89 Created. */ X/* 2. 30 Dec 89 Return command letter hit at end of article. */ X/* 3. 12 Sep 90 Added parm for lines already printed on display.*/ X/* 4. 24 Oct 90 Added percentage read display. */ X/* 5. 14 Nov 90 Added CR/NL to print one more line. */ X/* 6. 22 Nov 90 End page at form feed. */ X/* 7. 25 Nov 90 Added noscroll. If set in config file, screen */ X/* will be cleared for each new page rather than */ X/* scrolling. */ X/* 8. 05 Jan 91 Fixed line-wrap calculation. */ X/* 9. 05 Jan 91 Put last line of previous page at top of next */ X/* page in NO_SCROLL mode (for continuity). */ X/* 10. 06 Jan 91 Added "b" - go back one page. */ X/* 11. 06 Jan 91 Added "a" - About BARN. */ X/* */ X/****************************************************************************/ X X# define PAGE_CMDS " abFfjKkmnqRrw" X# define KICK_CMDS "kKwRrFfmn" /* get kicked up to calling function */ Xstatic char *PAGE_HELP[] = { X"<space> View next page of article.", X"<return> Next line.", X" a About BARN.", X" b,<bs> Go back one page.", X" F,f Followup, 'F' includes current article.", X" j,n,q Quit the pager, marking the article as read.", X" K Same as 'k', and adds Subject to KILL file.", X" k Kill (mark as read) all articles w/this Subject.", X" m Mark as unread.", X" R,r Reply via mail, 'R' includes current article.", X" w Write the article to disk. Prompts for a filename.", X(char *) NULL X}; X X XPaginate( article, lines ) X XARTICLE_INFO *article; Xint lines; X X{ Xchar filename[MAXLINE], buf[BUFSIZ], lastbuf[BUFSIZ], *readstatus; XFILE *fp; Xint line_number; /* what line of display the cursor is on */ Xint cmd, rc = ' '; Xint length; Xchar *p; X Xprintf( "\r%*s\r", Columns, " " ); Xsprintf( filename, "%ld", article->number ); Xif ( ( fp = fopen( filename, "r" ) ) == NULLP( FILE ) ) X printf( "ARTICLE UNREADABLE!\n" ); Xelse X { X fseek( fp, 0L, 2 ); X length = ftell( fp ); X fseek( fp, article->textpos, 0 ); X line_number = lines; X while ( ( readstatus = fgets( buf, BUFSIZ, fp ) ) != NULLP( char ) ) X { X if ( buf[strlen(buf)-1] == '\n' ) /* remove trailing newline */ X buf[strlen(buf)-1] = NULL; X line_number += 1 + ( strlen( buf ) - 1 ) / Columns; X if ( line_number > ( Lines - 1 ) || strchr( buf, '\f' ) != NULL ) X { X /* inverse video please */ X INVERSE_ON; X printf( "-- more (%d%%) --", (int) ((ftell( fp ) * 100) / length) ); X cmd = getcmd( PAGE_CMDS, PAGE_HELP, "\r\n\b" ); X INVERSE_OFF; X printf( "\r%40s\r", " " ); X if ( ( p = strchr( buf, '\f' ) ) != NULL ) X *p = ' '; X if ( cmd == ' ' ) X { X line_number = 0; X if ( GetVar( VAR_NOSCROLL ) != NULL ) X { X Clear_Screen; X printf( "%s\n", lastbuf ); X line_number += 1 + ( strlen( lastbuf ) - 1 ) / Columns; X } X line_number += 1 + ( strlen( buf ) - 1 ) / Columns; X printf( "%s\n", buf ); X } X else if ( cmd == '\n' || cmd == '\r' ) X /* X * Print one line. Don't adjust line_number because we X * want to prompt again for next line. X */ X printf( "%s\n", buf ); X else if ( cmd == 'b' || cmd == '\b' ) X { X long currentpos, newpos; X char *scratch, *ptr; X int size, lines; X X /* go back one page */ X line_number = 0; X currentpos = ftell( fp ) - strlen( buf ) - 1; X newpos = currentpos - ( Lines * 2 ) * ( Columns + 1 ); X if ( newpos < article -> textpos ) X newpos = article -> textpos; X fseek( fp, newpos, 0 ); X size = ( currentpos - ftell( fp ) ) * sizeof( char ); X scratch = malloc( size ); X (void) fread( scratch, sizeof( char ), size, fp ); X ptr = scratch + size - 1; X lines = 0; X while ( ptr > scratch && lines < ( Lines - 1 ) * 2 ) X { X if ( *ptr == '\n' ) X ++lines; X --ptr; X } X if ( ptr > scratch ) X newpos += ( ptr + 2 - scratch ); X fseek( fp, newpos, 0 ); X free( scratch ); X if ( GetVar( VAR_NOSCROLL ) != NULL ) X { X Clear_Screen; X } X } X else if ( cmd == 'a' ) X { X AboutBARN(); X /* X * Fool this fn into re-reading the same line and printing X * the prompt again. X */ X fseek( fp, 0 - ( strlen( buf ) + 1 ), 1 ); X strcpy( buf, lastbuf ); X } X else X { X if ( strchr( KICK_CMDS, (char) cmd ) != NULL ) X rc = cmd; /* these commands get kicked up to caller */ X break; X } X } X else X printf( "%s\n", buf ); X strcpy( lastbuf, buf ); X } X fclose( fp ); X if ( readstatus == NULLP( char ) ) X { X INVERSE_ON; X printf( "End of article %ld. ", article->number ); X cmd = getcmd( PAGE_CMDS, PAGE_HELP, "" ); X INVERSE_OFF; X if ( strchr( KICK_CMDS, (char) cmd ) != NULL ) X rc = cmd; /* these commands get kicked up to caller */ X } X } Xreturn rc; X} X X X/****************************************************************************/ X/* FUNCTION: MakePattern */ X/* */ X/* PURPOSE: Attempt to create a valid regular expression pattern. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* pattern O Put string form of pattern here. */ X/* text I Text to be matched. */ X/* */ X/* RETURNS: */ X/* (struct regexp *) Regular expression internal form. */ X/* (NULL) Couldn't grok pattern. */ X/* */ X/* COMMENTS: */ X/* Finds characters with special meaning to regexp and escapes them by */ X/* preceding them with a backslash. */ X/* */ X/* Pattern had better be large enough to hold the resulting string. */ X/* */ X/* HISTORY: */ X/* 1. 22 Oct 90 Created. */ X/* 2. 28 Nov 90 Added backslash as char that needs to be */ X/* backslashed. */ X/* Match random text between optional 'Re:' stuff */ X/* and 'text' pattern. */ X/* */ X/****************************************************************************/ X Xstruct regexp *MakePattern( pattern, text ) X Xchar *pattern; Xchar *text; X X{ Xchar temp[BUFSIZ]; /* translated string pattern */ Xchar base[BUFSIZ]; /* string without Re: stuff */ Xchar *p, *pbase; Xstruct regexp *remover_re; Xstruct regexp *re; /* resulting regular expression */ Xint len; X# ifdef DEBUG_REGSUB Xint i; X# endif X Xstrcpy( base, "\\1" ); Xif ( ( remover_re = regcomp( REPLY_REMOVER ) ) == NULL ) X fprintf( stderr, "Can't compile REPLY_REMOVER\n" ); X# ifdef DEBUG_REGSUB Xif ( regexec( remover_re, text ) == 1 ) X for ( i = 0; i < 10; i++ ) X printf( "%d '%*s'\n", i, remover_re -> endp[1] - remover_re -> startp[1], X remover_re -> startp[1] ); Xregsub( remover_re, text, base ); Xprintf( "but base is '%s'\n", base ); Xgetchar(); X# else X(void) regexec( remover_re, text ); Xlen = remover_re -> endp[1] - remover_re -> startp[1]; Xstrncpy( base, remover_re -> startp[1], len ); Xbase[len] = NULL; X# endif Xp = temp; pbase = base; Xwhile ( *pbase != NULL ) X { X switch ( *pbase ) X { X case '.': X case '*': X case '+': X case '(': X case ')': X case '[': X case ']': X case '^': X case '$': X case '-': X case '?': X case '|': X case '\\': X *p++ = '\\'; X default: X *p++ = *pbase; X break; X } X pbase++; X } X*p = NULL; X/* X * Try to match the Re: stuff that followups generate. X */ Xstrcpy( pattern, REPLY_PATTERN ); Xstrcat( pattern, ".*" ); Xstrcat( pattern, temp ); Xre = regcomp( pattern ); Xif ( remover_re != NULL ) X free( (char *) remover_re ); Xreturn re; X} X X X/****************************************************************************/ X/* FUNCTION: ScanSubjects */ X/* */ X/* PURPOSE: List articles in newsgroup by subject. */ X/* */ X/* INPUT PARAMETERS: */ X/* NAME I/O DESCRIPTION */ X/* ---- --- ----------- */ X/* arts I Header of list of articles in newsgroup. */ X/* */ X/* RETURNS: */ X/* */ X/* COMMENTS: */ X/* */ X/* HISTORY: */ X/* 1. 24 Oct 90 Created. */ X/* 2. 14 Nov 90 Added CR to print one more line. */ X/* 3. 16 Nov 90 Bug fix: always showed first article even if */ X/* it had already been read. */ X/* */ X/****************************************************************************/ X X# define SCAN_CMDS "q " Xstatic char *SCAN_HELP[] = { X"<space> View next page of subjects.", X"<return> View one more line.", X" q Quit scanning subjects.", X(char *) NULL X}; X Xvoid ScanSubjects( arts ) X XARTICLE_INFO *arts; X X{ XARTICLE_INFO *pbase; /* ptr to base article of subject thread */ XARTICLE_INFO *preply; /* tmep ptr to reply articles */ Xint n_replies; /* # of replies to a base article */ Xchar *sub; /* subject */ Xchar dummy[MAXLINE]; /* not used */ Xstruct regexp *pat; /* pattern to match subjects */ Xint printed = 0; /* # subjects printed this screenful */ Xint cmd; X Xprintf( "\n" ); Xfor ( pbase = arts; pbase != NULL; pbase = pbase -> next ) X if ( ! pbase -> beenread ) X pbase -> done = FALSE; X else X pbase -> done = TRUE; X/* X * Find first unread base article. X */ Xfor ( pbase = arts; pbase != NULL && pbase -> done; pbase = pbase -> next ) ; Xwhile ( pbase != NULL ) X { X if ( pbase -> subject ) X sub = strdup( pbase -> subject ); X else X sub = strdup( "<None>" ); X pbase -> done = TRUE; X /* X * Count replies. X */ X n_replies = 0; X pat = MakePattern( dummy, sub ); X for ( preply = pbase -> next; preply != NULL; preply = preply -> next ) X if ( ! preply -> done && preply -> subject && X regexec( pat, preply -> subject ) == 1 ) X { X n_replies++; X preply -> done = TRUE; X } X printf( "%5ld: (%2d replies) %-55.55s\n", pbase -> number, n_replies, sub ); X if ( pat != NULL ) X free( (char *) pat ); X if ( sub != NULL ) X free( sub ); X /* X * Find next base article we haven't processed. X */ X for ( pbase = pbase -> next; pbase != NULL && pbase -> done; X pbase = pbase -> next ) X ; X if ( ++printed >= Lines - 1 ) X { X /* inverse video please */ X INVERSE_ON; X printf( "--more--" ); X cmd = getcmd( SCAN_CMDS, SCAN_HELP, "\r\n" ); X INVERSE_OFF; X printf( "\r%40s\r", " " ); X if ( cmd != '\n' && cmd != '\r' ) X printed = 0; X if ( cmd == 'q' ) X break; X } X } Xif ( printed > 0 ) X { X /* inverse video please */ X INVERSE_ON; X printf( "--more--" ); X (void) getcmd( SCAN_CMDS, SCAN_HELP, "" ); X INVERSE_OFF; X printf( "\r%40s\r", " " ); X } Xfor ( pbase = arts; pbase != NULL; pbase = pbase -> next ) X if ( ! pbase -> beenread ) X pbase -> done = FALSE; X else X pbase -> done = TRUE; X} END_OF_FILE if test 44397 -ne `wc -c <'arn.c'`; then echo shar: \"'arn.c'\" unpacked with wrong size! fi chmod +x 'arn.c' # end of 'arn.c' fi if test -f 'barn.doc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'barn.doc'\" else echo shar: Extracting \"'barn.doc'\" \(15480 characters\) sed "s/^X//" >'barn.doc' <<'END_OF_FILE' X BARN - Bah's Amiga ReadNews X X Version 2.0 - January 6, 1991 X X Written by Jeff Van Epps (aka Lord Bah) X X X1 OVERVIEW X X1.1 Purpose X X BARN is a Usenet news-reader designed to replace "Anews", which comes Xwith AmigaUUCP 1.03D. It is intended to behave quite a bit like "rn" from Xthe UNIX world. X X1.2 Features X X Follows subject threads while reading articles. This means that after Xyou read one article, you are next presented with the next article in the Xnewsgroup with the same subject. X X Kill files allow you to weed out messages regarding subject which you Xaren't interested in, or from people whose messages waste your time. X X Article pager behaves like "more", with forward and backward paging and Xdisplay of percentage of article read. X X Email reply function and post followup article function use editor Xspecified by the user in a configuration file. Both functions have an X"include article" option. X X Configuration files allow multiple users to read news on the same Xmachine, from the same directory, at the same time. X X X2 REQUIREMENTS X X2.1 Hardware X X There are no specific hardware requirements. X X2.2 Software X X So far, BARN has only been used on AmigaUUCP 1.03D systems. But it Xshould understand any future versions with no problems. X X It does require the "rmail" program to send email. X X X3 INSTALLATION X X3.1 Executable X X Place the "barn" executable wherever you want, most conveniently Xsomewhere in your path. X X3.2 Configuration File X X Edit the supplied "barn.config" file for your site. Blank lines are Xignored, and any line beginning with a "#" in column 1 is a comment. Each Xline is of the form "variable=value", with no spaces allowed around the Xequal sign. X X user=username X X This is the equivalent of UserName in UULIB:Config. It is the user Xname by which the mail system addresses mail to you. It is used in the XFrom: line of outgoing mail and news postings. X X node=machinename X X This is the equivalent of NodeName in UULIB:Config. It is the name by Xwhich the outside world knows your machine. It is used in the From: line Xof outgoing mail and news postings. X X name=Joe User X X This is the equivalent of RealName in UULIB:Config. It is your full Xname. It is used in the From: line of outgoing mail and news postings. X X domain=.server.company.com X X This is the equivalent of DomainName in UULIB:Config. It is used in Xconstructing the From: line of outgoing mail and news postings. The full Xconstructed line is: From: $node!$user@$domain ($name). I don't consider Xthis general enough, but I haven't had to come up with anything better yet. X X editor=stevie X X The editor which will be invoked to compose email and followups. X X newsrc=.newsrc X X The name of the file which keeps track of which articles you have read Xin each newsgroup. X X kill=KILL X X The name of the kill files. There will be one global kill file in the Xroot of the news directory structure whose kill rules will apply to all Xarticles in all newsgroups. There is also a local kill file within each Xnewsgroup which applies to only that newsgroup. This filename must start Xwith the letters "KILL". X X signature=UULIB:.signature X X The contents of the signature file will be appended to the outgoing Xemail and news articles which you compose before you are placed in the Xeditor. The contents will NOT be appended after you exit the editor, so Xyou may delete the signature if you want. X X lines=23 X columns=80 X X These define the dimensions of the window in which you will run the Xprogram. I'm new to Amiga programming and haven't yet bothered to figure Xout how to obtain the current window size or adjust to dynamic resizing Xduring execution. X X noscroll=true X X Setting this variable to any value (even false!) will cause the pager Xto start each new page at the top of the window rather than scrolling. X X3.3 Multiple Users X X If you will have multiple users certain rules should be followed Xregarding configuration files. X X There will be one configuration file for each user. I recommend naming Xthe files <username>.config or barn.<usename>. X X Within each config file, the variables "user", "name", "newsrc", X"kill", and "signature" will change. I recommend setting "newsrc" to X<username>.newsrc and "kill" to KILL.<username>. The others are up to you. X X The variables "lines", "columns", "noscroll", and "editor" may be Xdifferent in each config file depending on individual user preference. X X When the config file to be used is not named "barn.config", you must Xspecify it on the command line, i.e. X X barn <username>.config X X3.4 Initial .newsrc file X X Create a text file called ".newsrc" in the base directory where news is Xstored (usually UUNEWS:). Each line must have the name of a newsgroup X(e.g. comp.sys.amiga), a space and a "0". This file will be used by BARN Xto keep track of which articles have already been read in each newsgroup. X X If you have been using a different newsreader, you may indicate to BARN Xwhich articles you have already read in each newsgroup. Each line in the Xnewsrc file contains the newsgroup name, one space, and a sequence of Xmarkers separated by commas. Each marker is either one number or an Xinclusive range of two numbers separated by a dash. As an example: X X comp.sys.amiga 1-500,512,520-534 X X indicates that in newsgroup comp.sys.amiga, articles numbered 1 thru X500, 512, and 520 thru 534 have been read. Articles numbered 501 thru 511, X513 thru 519, and 535 or higher have not been read. X X X4 OPERATION X X4.1 Newsgroup Selection Level X X BARN will scan the newsgroups listed in the newsrc file in the order in Xwhich they are listed. Upon encountering each newsgroup, it will descend Xinto that directory. For each file in the directory whose name does not Xbegin with "KILL", it will interpret the filename as an article number and Xdetermine whether or not that article has already been read, based on the Xmarkers for that newsgroup in the newsrc file. If the article has not been Xread, then the headers of the article are read and compared against the Xglobal and local kill files. If the article is not killed, it is added to Xthe list of articles to be read. X X When finished scanning the directory, BARN presents the newsgroup name Xand the number of unread articles to the user and asks if the user wishes Xto read the newsgroup now. If there are no unread articles, BARN proceeds Xto the next newsgroup in the newsrc file without user intervention. X X User commands at the newsgroup selection level: X X a About BARN. Prints credits and version. X c Catch up. Mark all articles in the newsgroup as read. X n No, don't read this newsgroup now. Markers are not updated. X q Quit BARN. Writes the newsrc file and exits. X y Yes, read this newsgroup now. Goes to article selection level. X <space> Same as Yes. X h,H,or ? Prints help for this level. X X4.2 Article Selection Level X X The interesting headers of the lowest numbered article are presented to Xthe user along with the article selection level prompt. X X User commands at the article selection level: X X a About BARN. Prints credits and version. X c Catch up. Mark all articles in the newsgroup as read. X f Post followup news article. Uses user's $editor from config. X F Followup, including contents of current article in new article. X j Junk this article (mark it as read). X k Kill articles matching the current subject pattern. X K Kill, placing this kill pattern in the local kill file. X m Mark this article as unread. X n No, don't read this article. Mark it as read. X q Quit this newsgroup. X r Reply to article originator via email. Uses $editor from config. X R Reply, including contents of current article. X w Write this article. Prompts for filename. X y Yes, read this article now. X <space> Yes, read this article now. X <minus> Go to previously displayed article, marking it as unread. X <equal> Scan subject threads. X / Find next article with subject which BARN will prompt for. X # Go to article with number BARN will prompt for (if unread). X h,H,or ? Print help for this level. X X Unless directed otherwise, BARN will present the next article matching Xthe current subject thread. If there is no such article, it will present Xthe lowest numbered article still unread. X X There are some commands which are less than completely clear, so I'll Xgo over them. X X4.2.1 Followups X X The Subject header is copied from the current article, and other Xheaders are generated from sources such as the config file, the system date Xand time, etc. These headers are placed into a temporary file named XT:arnreply. The user's signature is appended to the file, and the user's Xeditor specified in the config file is started, given the temporary Xfilename as an argument on the command line. If the user specified an Xincluding followup (capital F command), then the text of the current Xarticle is placed in the temporary file between the headers and the Xsignature, with each line prefixed by "> ". X X When the editor returns, the last modification time of the temporary Xfile is checked. If it was not modified by the user while in the editor, Xthe followup operation is aborted. If it was modified, then the contents Xof the temporary file are sent as an email message to "inews@bisco". This Xis a cheap way to post news for me, but I realize that no one else will be Xable to use it. For now, you will have to obtain the source for BARN and Xmodify this address to one which you can reach which will let you post Xnews. Eventually, either I will fix this or someone else will fix this and XI will include the fix in a subsequent release of BARN. X X4.2.2 Replies X X Replies are almost identical to followups. The only difference is that Xa To: field is inserted into the temporary file addressing the mail to the Xoriginator of the article before invoking the editor, and the To: field is Xextracted again after exiting the editor (allowing the user to change the Xdestination of the message in the editor, in case the From: field from the Xoriginal article was bad). The "rmail" program is then fed the contents of Xthe temporary file and given the address as an argument. X X This should probably use the Reply-To: field if one exists, but I Xhaven't gotten around to it yet. X X4.2.3 Kill Files X X Each line of a kill file contains a header name and a regular Xexpression, such as: X X1 Subject: MS-DOS X2 Sender: fool@school X3 Subject: ^Re: X4 Subject: word.*process X X(1) kills any article whose subject line contains the string "MS-DOS". (2) Xkills any article whose sender header line contains the string X"fool@school". (3) kills any article whose subject line begins with "Re:". X(4) kills any article whose subject line contains the string "word", Xfollowed by any number of characters, followed by the string "process". X X Any "interesting" header may be used in a kill file. Currently, X"interesting" is defined as one of: "Subject", "From", "To", "Date", X"Sender". A future version of BARN will allow these to be specified in a Xfile referenced from the config file. X X For a full description of regular expressions, see the regexp man page. X X Any kill file can be edited with a normal editor, but don't leave any Xblank lines. Do not edit the global kill file while BARN is running, and Xdo not edit a local kill file while BARN is in that newsgroup, or your Xchanges to the file will be lost when BARN writes out its idea of what the Xkill file is. X X When you use the 'K' command to add a subject to the local kill file, XBARN will escape with a backslash any characters in the subject which would Xhave special meaning to regexp and add a pattern on the front of the Xsubject which matches any number of "Re:"-type prependages. X X4.2.4 Mark as Unread X X The 'm' command is not well-implemented at this time. It should keep Xthe article out of the user's sight until all other articles in the Xnewsgroup have been dealt with. Instead the user will see the article Xagain right after the next subject thread is exhausted. X X It will be improved. X X4.2.5 Write Article X X This prompts for a filename and then writes the current article to that Xfilename in the directory from which BARN was executed using the AmigaDOS X"copy" command. X X4.2.6 Previous Article X X Returns to the previously displayed article and unmarks ONLY that Xarticle, even if the user has just executed a 'k' - kill command on that Xsubject. X X4.2.7 Scan Subjects X X For each unique subject still unread, displays the first article number Xusing that subject, the number of reply articles under the same subject, Xand the subject text. X X4.2.8 Find Subject X X The '/' command sets the current subject thread to whatever the user Xenters. The system behaves just as if an article with that subject had Xbeen read. This includes using the user-entered subject as a kill pattern. X X4.3 Pager Level X X BARN's built-in pager displays the article one page at a time, using Xthe 'lines' and 'columns' variables from the config file. Most of the Xcommands valid at the article selection level are also valid within the Xpager. X X User commands at the pager level: X X a About BARN. Prints credits and version. X b Go backward one page. X <space> Go forward one page. X <return> Go forward one line. X <bs> Go backward one page. X h,H,or ? Print help for the pager level. X X and [fFjkKmnqrRw] from the article selection level. X X X5 LIMITATIONS X X5.1 Followup X X As noted in section 4.2.1, you will probably not be able to post news Xfrom your system unless you can modify the source code. There is no method Xfor posting "base" articles, only followups. X X5.2 From Name X X The "From:" line constructed may not be in a form suitable for your Xsite. X X5.3 Article Expiration X X I consider article expiration to be a separate function. I've supplied Xa short shell script that I use to do this; modify to suit. X X X6 FUTURE ENHANCEMENTS X X Junking a range of article numbers. X X Verbose command to display all article headers. X X Customizable list of "interesting" headers, not just "From", "Date", and X"Subject". X X Use Message-ID to avoid multiple display of cross-posted articles. I Xcurrently do this with an external program. X X More interesting commands to handle articles which have already been read. X X X7 SOURCE CODE MODIFICATION X X I use SAS C 5.10. There are probably some library functions that I use Xwhich Manx does not provide. I also use Miles Bader's "cc" front-end to X"lc" to sort out all of the compiler options. You will also need to find XHenry Spencer's "regexp" library to recompile. X X If you do make modifications, please send them to me rather than Xreleasing them yourself so that I can try to keep track of different Xversions. Thank you for your cooperation. X X X8 CREDITS X X Uses sendpacket.c and raw.c from the AmigaUUCP distribution, which is Xapparently now in the hands of an entity called Dynamyx. The mentioned source Xfiles were written by CBM and Chuck McManis, respectively. I'll put my source Xin the public domain eventually, but I want to make sure there are no bugs Xfirst. X X Uses the regexp library written by Henry Spencer and copyrighted by Xthe University of Toronto in 1986. X X All else written by Jeff Van Epps. X X Compiled with SAS C 5.10. END_OF_FILE if test 15480 -ne `wc -c <'barn.doc'`; then echo shar: \"'barn.doc'\" unpacked with wrong size! fi chmod +x 'barn.doc' # end of 'barn.doc' fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Mail submissions (sources or binaries) to <amiga@uunet.uu.net>. Mail comments to the moderator at <amiga-request@uunet.uu.net>. Post requests for sources, and general discussion to comp.sys.amiga.misc.