rsalz@bbn.com (Rich Salz) (01/04/91)
Submitted-by: Wayne Davison <0004475895@mcimail.com> Posting-number: Volume 23, Issue 62 Archive-name: trn/part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: makekit ng.c rcstuff.c # Wrapped by rsalz@litchi.bbn.com on Thu Dec 27 11:34:00 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 3 (of 14)."' if test -f 'makekit' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makekit'\" else echo shar: Extracting \"'makekit'\" \(745 characters\) sed "s/^X//" >'makekit' <<'END_OF_FILE' X#!/bin/sh X# $Header: makekit,v 4.3 85/05/01 11:42:38 lwall Exp $ X# X# $Log: makekit,v $ X# Revision 4.3 85/05/01 11:42:38 lwall X# Baseline for release with 4.3bsd. X# X Xnumkits=$# Xfor kitlist in $*; do X kit=`basename $kitlist .list` X kitnum=`expr "$kit" : 'kit\([0-9][0-9]*\)'` X echo "*** Making $kit ***" X kitleader "$kit" "$kitnum" "$numkits" X for file in `/bin/cat $kitlist`; do X echo $file X echo "echo Extracting $file" >> $kit X if egrep '^\.$' $file; then X echo "sed >$file <<'!STUFFY!FUNK!' -e 's/X//'" >> $kit X sed <$file >>$kit -e 's/^/X/' X else X echo "cat >$file <<'!STUFFY!FUNK!'" >> $kit X /bin/cat $file >> $kit X fi X echo "!STUFFY!FUNK!" >> $kit X done X kittrailer "$kit" "$kitnum" "$numkits" Xdone END_OF_FILE if test 745 -ne `wc -c <'makekit'`; then echo shar: \"'makekit'\" unpacked with wrong size! fi chmod +x 'makekit' # end of 'makekit' fi if test -f 'ng.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ng.c'\" else echo shar: Extracting \"'ng.c'\" \(34937 characters\) sed "s/^X//" >'ng.c' <<'END_OF_FILE' X/* $Header: ng.c,v 4.3.3.2 90/08/20 16:03:45 davison Trn $ X * X * $Log: ng.c,v $ X * Revision 4.3.3.2 90/08/20 16:03:45 davison X * Fixed bug in backpage code. X * X * Revision 4.3.3.1 90/07/21 20:27:17 davison X * Initial Trn Release X * X * Revision 4.3.2.7 90/04/21 14:44:23 sob X * Revised previous patch insure that it does not decrement below zero. X * X * Revision 4.3.2.6 90/03/22 23:04:49 sob X * Fixes provided by Wayne Davison <drivax!davison> X * X * Revision 4.3.2.5 89/12/09 01:18:42 sob X * Fixed a bad call to nntpopen(). X * X * Revision 4.3.2.4 89/11/28 01:51:20 sob X * Removed redundant #include directive. X * X * Revision 4.3.2.3 89/11/27 01:31:03 sob X * Altered NNTP code per ideas suggested by Bela Lubkin X * <filbo@gorn.santa-cruz.ca.us> X * X * Revision 4.3.2.2 89/11/26 22:53:35 sob X * Add new patches to make RRN be faster. X * X * Revision 4.3.2.1 89/11/06 00:54:27 sob X * Added RRN support from NNTP 1.5 X * X * Revision 4.3.1.6 85/09/10 11:03:42 lwall X * Improved %m in in_char(). X * X * Revision 4.3.1.5 85/09/05 12:34:37 lwall X * Catchup command could make unread article count too big. X * X * Revision 4.3.1.4 85/07/23 18:19:46 lwall X * Added MAILCALL environment variable. X * X * Revision 4.3.1.3 85/05/16 16:48:09 lwall X * Fixed unsubsubscribe. X * X * Revision 4.3.1.2 85/05/13 09:29:28 lwall X * Added CUSTOMLINES option. X * X * Revision 4.3.1.1 85/05/10 11:36:00 lwall X * Branch for patches. X * X * Revision 4.3 85/05/01 11:43:43 lwall X * Baseline for release with 4.3bsd. X * X */ X X#include "EXTERN.h" X#include "common.h" X#include "rn.h" X#include "term.h" X#include "final.h" X#include "util.h" X#include "artsrch.h" X#include "cheat.h" X#include "help.h" X#include "kfile.h" X#include "rcstuff.h" X#include "head.h" X#include "bits.h" X#include "art.h" X#include "artio.h" X#include "ngstuff.h" X#include "intrp.h" X#include "respond.h" X#include "ngdata.h" X#include "backpage.h" X#include "rcln.h" X#include "last.h" X#include "search.h" X#ifdef SERVER X#include "server.h" X#endif X#ifdef USETHREADS X#include "rthreads.h" X#endif X#include "uudecode.h" X#include "INTERN.h" X#include "ng.h" X#include "artstate.h" /* somebody has to do it */ X X/* art_switch() return values */ X X#define AS_NORM 0 X#define AS_INP 1 X#define AS_ASK 2 X#define AS_CLEAN 3 X XART_NUM recent_art = -1; /* previous article # for '-' command */ XART_NUM curr_art = -1; /* current article # */ Xint exit_code = NG_NORM; X Xvoid Xng_init() X{ X X#ifdef KILLFILES X open_kfile(KF_GLOBAL); X#endif X#ifdef CUSTOMLINES X init_compex(&hide_compex); X init_compex(&page_compex); X#endif X} X X/* do newsgroup on line ng with name ngname */ X X/* assumes that we are chdir'ed to SPOOL, and assures that that is X * still true upon return, but chdirs to SPOOL/ngname in between X * X * If you can understand this routine, you understand most of the program. X * The basic structure is: X * for each desired article X * for each desired page X * for each line on page X * if we need another line from file X * get it X * if it's a header line X * do special things X * for each column on page X * put out a character X * end loop X * end loop X * end loop X * end loop X * X * (Actually, the pager is in another routine.) X * X * The chief problem is deciding what is meant by "desired". Most of X * the messiness of this routine is due to the fact that people want X * to do unstructured things all the time. I have used a few judicious X * goto's where I thought it improved readability. The rest of the messiness X * arises from trying to be both space and time efficient. Have fun. X */ X Xint Xdo_newsgroup(start_command) Xchar *start_command; /* command to fake up first */ X{ X#ifdef SERVER X char ser_line[256]; X char artname[32]; X static long our_pid; X#endif /* SERVER */ X char oldmode = mode; X register long i; /* scratch */ X int skipstate; /* how many unavailable articles */ X /* have we skipped already? */ X X char *whatnext = "%sWhat next? [%s]"; X X#ifdef SERVER X if (our_pid == 0) /* Agreed, this is gross */ X our_pid = getpid(); X#endif /* SERVER */ X X#ifdef ARTSEARCH X srchahead = (scanon && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0); X /* did they say -S? */ X#endif X X mode = 'a'; X#ifdef USETHREADS X recent_p_art = curr_p_art = Nullart; X#endif X recent_art = curr_art = -1; X exit_code = NG_NORM; X X#ifdef SERVER X sprintf(ser_line, "GROUP %s", ngname); X put_server(ser_line); X if (get_server(ser_line, sizeof(ser_line)) < 0) { X fprintf(stderr, "rrn: Unexpected close of server socket.\n"); X finalize(1); X } X if (*ser_line != CHAR_OK) { X if (atoi(ser_line) != ERR_NOGROUP) X fprintf(stderr, "rrn: server response to GROUP %s:\n%s\n", X ngname, ser_line); X return (-1); X } X#else /* not SERVER */ X if (eaccess(ngdir,5)) { /* directory read protected? */ X if (eaccess(ngdir,0)) { X#ifdef VERBOSE X IF(verbose) X printf("\nNewsgroup %s does not have a spool directory!\n", X ngname) FLUSH; X ELSE X#endif X#ifdef TERSE X printf("\nNo spool for %s!\n",ngname) FLUSH; X#endif X#ifdef CATCHUP X catch_up(ng); X#endif X toread[ng] = TR_NONE; X } X else { X#ifdef VERBOSE X IF(verbose) X printf("\nNewsgroup %s is not currently accessible.\n", X ngname) FLUSH; X ELSE X#endif X#ifdef TERSE X printf("\n%s not readable.\n",ngname) FLUSH; X#endif X toread[ng] = TR_NONE; /* make this newsgroup invisible */ X /* (temporarily) */ X } X mode = oldmode; X return -1; X } X X /* chdir to newsgroup subdirectory */ X X if (chdir(ngdir)) { X printf(nocd,ngdir) FLUSH; X mode = oldmode; X return -1; X } X#endif /* SERVER */ X X#ifdef CACHESUBJ X subj_list = Null(char **); /* no subject list till needed */ X#endif X X /* initialize control bitmap */ X X if (initctl()) { X mode = oldmode; X return -1; X } X X /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */ X X /* grab threaded data */ X X#ifdef USETHREADS X if (ThreadedGroup && (ThreadedGroup = use_data(thread_name(ngname)))) { X /* check if thread file is newer than the active2 entry (this is X ** possible when mthreads is still running.) */ X if (total.last > lastart) { X grow_ctl(total.last); /* sets lastart */ X } X else if (total.last < lastart) { X /* If the active2 entry is newer than the data file, something X ** bad is going on. Forget using the thread data. */ X unuse_data(0); X ThreadedGroup = FALSE; X } X } X#endif X X in_ng = TRUE; /* tell the world we are here */ X forcelast = TRUE; /* if 0 unread, do not bomb out */ X X /* remember what newsgroup we were in for sake of posterity */ X X writelast(); X X /* see if there are any special searches to do */ X X#ifdef KILLFILES X open_kfile(KF_LOCAL); X#ifdef VERBOSE X IF(verbose) X kill_unwanted(firstart,"Looking for articles to kill...\n\n",TRUE); X ELSE X#endif X#ifdef TERSE X kill_unwanted(firstart,"Killing...\n\n",TRUE); X#endif X#endif X#ifdef USETHREADS X first_art(); X#else X art=firstart; X#endif X X /* do they want a special top line? */ X X firstline = getval("FIRSTLINE",Nullch); X X /* custom line suppression, custom page ending */ X X#ifdef CUSTOMLINES X if (hideline = getval("HIDELINE",Nullch)) X compile(&hide_compex,hideline,TRUE,TRUE); X if (pagestop = getval("PAGESTOP",Nullch)) X compile(&page_compex,pagestop,TRUE,TRUE); X#endif X X /* now read each unread article */ X X rc_changed = doing_ng = TRUE; /* enter the twilight zone */ X skipstate = 0; /* we have not skipped anything (yet) */ X checkcount = 0; /* do not checkpoint for a while */ X do_fseek = FALSE; /* start 1st article at top */ X if (art > lastart) X#ifdef USETHREADS X first_art(); X#else X art=firstart; /* init the for loop below */ X#endif X for (; art<=lastart+1; ) { /* for each article */ X X /* do we need to "grow" the newsgroup? */ X X#ifdef USETHREADS X if (ThreadedGroup) { X if ((art > lastart || forcegrow) && getngsize(ng) > total.last) { X unuse_data(1); /* free data with selections saved */ X if ((ThreadedGroup = use_data(thread_name(ngname))) != 0) { X grow_ctl(total.last); /* sets lastart */ X find_article(art); X curr_p_art = p_art; X } X forcegrow = FALSE; X } X } X else X#endif X if (art > lastart || forcegrow) X grow_ctl(getngsize(ng)); X check_first(art); /* make sure firstart is still 1st */ X if (start_command) { /* fake up an initial command? */ X prompt = whatnext; X strcpy(buf,start_command); X free(start_command); X start_command = Nullch; X#ifdef USETHREADS X p_art = Nullart; X#endif X art = lastart+1; X goto article_level; X } X if (art>lastart) { /* are we off the end still? */ X ART_NUM ucount = 0; /* count of unread articles left */ X X for (i=firstart; i<=lastart; i++) X if (!(ctl_read(i))) X ucount++; /* count the unread articles */ X#ifdef DEBUGGING X /*NOSTRICT*/ X if (debug && ((ART_NUM)toread[ng]) != ucount) X printf("(toread=%ld sb %ld)",(long)toread[ng],(long)ucount) X FLUSH; X#endif X /*NOSTRICT*/ X toread[ng] = (ART_UNREAD)ucount; /* this is perhaps pointless */ X art = lastart + 1; /* keep bitmap references sane */ X if (art != curr_art) { X#ifdef USETHREADS X recent_p_art = curr_p_art; X find_article(art); X curr_p_art = p_art; X#endif X recent_art = curr_art; X /* remember last article # (for '-') */ X curr_art = art; /* remember this article # */ X } X if (erase_screen) X clear(); /* clear the screen */ X else X fputs("\n\n",stdout) FLUSH; X#ifdef VERBOSE X IF(verbose) X printf("End of newsgroup %s.",ngname); X /* print pseudo-article */ X ELSE X#endif X#ifdef TERSE X printf("End of %s",ngname); X#endif X if (ucount) { X#ifdef USETHREADS X if (selected_root_cnt) X printf(" (%ld + %ld articles still unread)", X (long)selected_count,(long)ucount-selected_count); X else X#endif X printf(" (%ld article%s still unread)", X (long)ucount,ucount==1?nullstr:"s"); X } X else { X if (!forcelast) X goto cleanup; /* actually exit newsgroup */ X } X prompt = whatnext; X#ifdef ARTSEARCH X srchahead = 0; /* no more subject search mode */ X#endif X fputs("\n\n",stdout) FLUSH; X skipstate = 0; /* back to none skipped */ X } X else if (!reread && was_read(art)) { X /* has this article been read? */ X#ifdef USETHREADS X follow_thread('n'); X#else X art++; /* then skip it */ X#endif X continue; X } X else if X (!reread && !was_read(art) X#ifdef SERVER X && nntpopen(art,GET_HEADER) == Nullfp) { X#else X && artopen(art) == Nullfp) { /* never read it, & cannot find it? */ X if (errno != ENOENT) { /* has it not been deleted? */ X#ifdef VERBOSE X IF(verbose) X printf("\n(Article %ld exists but is unreadable.)\n", X (long)art) FLUSH; X ELSE X#endif X#ifdef TERSE X printf("\n(%ld unreadable.)\n",(long)art) FLUSH; X#endif X skipstate = 0; X sleep(2); X } X#endif X switch(skipstate++) { X case 0: X clear(); X#ifdef VERBOSE X IF(verbose) X fputs("Skipping unavailable article",stdout); X ELSE X#endif X#ifdef TERSE X fputs("Skipping",stdout); X#endif X for (i = just_a_sec/3; i; --i) X putchar(PC); X fflush(stdout); X sleep(1); X break; X case 1: X fputs("..",stdout); X fflush(stdout); X break; X default: X putchar('.'); X fflush(stdout); X#ifndef SERVER X#define READDIR X#ifdef READDIR X { /* fast skip patch */ X ART_NUM newart; X X if (! (newart=getngmin(".",art))) X newart = lastart+1; X for (i=art; i<newart; i++) X oneless(i); X art = newart - 1; X } X#endif X#else X { X char ser_line[256]; X ART_NUM newart; X X put_server("NEXT"); X if (get_server(ser_line, sizeof (ser_line)) < 0) { X fprintf(stderr, X "rrn: unexpected close of server socket.\n"); X finalize(1); X } X if (ser_line[0] != CHAR_OK) X newart = lastart + 1; X else X newart = atoi(ser_line+4); X for (i=art; i<newart; i++) X oneless(i); X art = newart - 1; X } X#endif /* SERVER */ X break; X } X oneless(art); /* mark deleted as read */ X#ifdef USETHREADS X count_roots(FALSE); /* Keep selected_count accurate */ X find_article(art); X follow_thread('n'); X#else X art++; /* try next article */ X#endif X continue; X } X else { /* we have a real live article */ X skipstate = 0; /* back to none skipped */ X if (art != curr_art) { X#ifdef USETHREADS X recent_p_art = curr_p_art; X find_article(art); X curr_p_art = p_art; X#endif X recent_art = curr_art; X /* remember last article # (for '-') */ X curr_art = art; /* remember this article # */ X } X if (!do_fseek) { /* starting at top of article? */ X artline = 0; /* start at the beginning */ X topline = -1; /* and remember top line of screen */ X /* (line # within article file) */ X } X clear(); /* clear screen */ X if (!artopen(art)) { /* make sure article is found & open */ X#ifdef USETHREADS X char tmpbuf[256]; X /* see if we have tree data for this article anyway */ X init_tree(); X sprintf(tmpbuf,"%s #%ld is not available.",ngname,(long)art); X tree_puts(tmpbuf,0,0); X vwtary((ART_LINE)0,(ART_POS)0); X finish_tree(1); X prompt = whatnext; X#else X printf("Article %ld of %s is not available.\n\n", X (long)art,ngname) FLUSH; X prompt = whatnext; X#endif X#ifdef ARTSEARCH X srchahead = 0; X#endif X } X else { /* found it, so print it */ X switch (do_article()) { X case DA_CLEAN: /* quit newsgroup */ X goto cleanup; X case DA_TOEND: /* do not mark as read */ X goto reask_article; X case DA_RAISE: /* reparse command at end of art */ X goto article_level; X case DA_NORM: /* normal end of article */ X break; X } X } X if (art >= absfirst) /* don't mark non-existant articles */ X mark_as_read(); /* mark current article as read */ X reread = FALSE; X do_hiding = TRUE; X#ifdef ROTATION X rotate = FALSE; X#endif X } X X/* if these gotos bother you, think of this as a little state machine */ X Xreask_article: X#ifdef MAILCALL X setmail(); X#endif X setdfltcmd(); X#ifdef CLEAREOL X if (erase_screen && can_home_clear) X clear_rest(); X#endif /* CLEAREOL */ X unflush_output(); /* disable any ^O in effect */ X standout(); /* enter standout mode */ X printf(prompt,mailcall,dfltcmd);/* print prompt, whatever it is */ X un_standout(); /* leave standout mode */ X putchar(' '); X fflush(stdout); Xreinp_article: X eat_typeahead(); X#ifdef PENDING X look_ahead(); /* see what we can do in advance */ X if (!input_pending()) X collect_subjects(); /* loads subject cache until */ X /* input is pending */ X#endif X getcmd(buf); X if (errno || *buf == '\f') { X if (LINES < 100 && !int_count) X *buf = '\f'; /* on CONT fake up refresh */ X else { X putchar('\n') FLUSH; /* but only on a crt */ X goto reask_article; X } X } Xarticle_level: X X /* parse and process article level command */ X X switch (art_switch()) { X case AS_INP: /* multichar command rubbed out */ X goto reinp_article; X case AS_ASK: /* reprompt "End of article..." */ X goto reask_article; X case AS_CLEAN: /* exit newsgroup */ X goto cleanup; X case AS_NORM: /* display article art */ X break; X } X } /* end of article selection loop */ X X/* shut down newsgroup */ X Xcleanup: X uud_end(); X#ifdef KILLFILES X kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE); X /* do cleanup from KILL file, if any */ X#endif X#ifdef USETHREADS X if (ThreadedGroup) X unuse_data(0); /* free article thread data */ X#endif X in_ng = FALSE; /* leave newsgroup state */ X if (artfp != Nullfp) { /* article still open? */ X fclose(artfp); /* close it */ X artfp = Nullfp; /* and tell the world */ X#ifdef SERVER X sprintf(artname, "/tmp/rrn%ld.%ld", (long) openart, our_pid); X UNLINK(artname); X#endif /* SERVER */ X openart = 0; X } X putchar('\n') FLUSH; X yankback(); /* do a Y command */ X restore_ng(); /* reconstitute .newsrc line */ X doing_ng = FALSE; /* tell sig_catcher to cool it */ X free(ctlarea); /* return the control area */ X#ifdef CACHESUBJ X if (subj_list) { X for (i=OFFSET(lastart); i>=0; --i) X if (subj_list[i]) X free(subj_list[i]); X#ifndef lint X free((char*)subj_list); X#endif /* lint */ X } X#endif X write_rc(); /* and update .newsrc */ X rc_changed = FALSE; /* tell sig_catcher it is ok */ X if (chdir(spool)) { X printf(nocd,spool) FLUSH; X sig_catcher(0); X } X#ifdef KILLFILES X if (localkfp) { X fclose(localkfp); X localkfp = Nullfp; X } X#endif X mode = oldmode; X return exit_code; X} /* Whew! */ X X/* decide what to do at the end of an article */ X Xint Xart_switch() X{ X register ART_NUM i; X X setdef(buf,dfltcmd); X#ifdef VERIFY X printcmd(); X#endif X X switch (*buf) { X#ifdef USETHREADS X case '<': /* goto previous thread */ X if (!ThreadedGroup) { X goto group_unthreaded; X } X prev_root(); X return AS_NORM; X case '>': /* goto next thread */ X if (!ThreadedGroup) { X goto group_unthreaded; X } X next_root(); X return AS_NORM; X case 'U': { /* unread some articles */ X char *u_prompt, *u_help_thread; X X if (!ThreadedGroup) { X dfltcmd = "a"; X u_help_thread = nullstr; X#ifdef VERBOSE X IF(verbose) X u_prompt = "\nSet unread: all articles? [an] "; X ELSE X#endif X#ifdef TERSE X u_prompt = "\nUnread? [an] "; X#endif X } X else if (!p_art || art > lastart) { X dfltcmd = "+"; X u_help_thread = nullstr; X#ifdef VERBOSE X IF(verbose) X u_prompt = "\nSet unread: +select or all? [+an] "; X ELSE X#endif X#ifdef TERSE X u_prompt = "\nUnread? [+an] "; X#endif X } X else { X dfltcmd = "t"; X#ifdef VERBOSE X IF(verbose) { X u_prompt = "\n\ XSet unread: thread, subthread, +select, or all? [ts+an] "; X u_help_thread = "\ XType t or SP to mark this thread's articles as unread.\n\ XType s to mark the current article and its descendants as unread.\n"; X } X ELSE X#endif X#ifdef TERSE X { X u_prompt = "\nUnread? [ts+an] "; X u_help_thread = "\ Xt or SP to mark thread unread.\n\ Xs to mark subthread unread.\n"; X } X#endif X } X reask_unread: X in_char(u_prompt,'u'); X putchar('\n') FLUSH; X setdef(buf,dfltcmd); X#ifdef VERIFY X printcmd(); X#endif X if (*buf == 'h') { X fputs(u_help_thread,stdout); X#ifdef VERBOSE X IF(verbose) X { X if (ThreadedGroup) X fputs("\ XType + to enter select thread mode using all the unread articles.\n\ X(The selected threads will be marked as unread and displayed as usual.)\n\ X",stdout) FLUSH; X fputs("\ XType a to mark all articles in this group as unread.\n\ XType n to change nothing.\n\ X",stdout) FLUSH; X } X ELSE X#endif X#ifdef TERSE X { X if (ThreadedGroup) X fputs("\ X+ to select threads from the unread.\n\ X",stdout) FLUSH; X fputs("\ Xa to mark all articles unread.\n\ Xn to change nothing.\n\ X",stdout) FLUSH; X } X#endif X goto reask_unread; X } X else if (*buf == 'n' || *buf == 'q') { X return AS_ASK; X } X else if (*buf == 't' && *dfltcmd == 't') X follow_thread('u'); X else if (*buf == 's' && *dfltcmd == 't') { X follow_thread('U'); X } X else if (*buf == 'a') { X check_first(absfirst); X for (i = absfirst; i <= lastart; i++) { X onemore(i); /* mark as unread */ X } X scan_all_roots = FALSE; X count_roots(FALSE); X if (art > lastart) { X first_art(); X } X } X else if (ThreadedGroup && *buf == '+') { X *buf = 'U'; X goto select_threads; X } X else { X fputs(hforhelp,stdout) FLUSH; X settle_down(); X goto reask_unread; X } X return AS_NORM; X } X case '[': /* goto parent article */ X case '{': /* goto thread's root article */ X if (p_art) { X if (!p_art->parent) { X if (p_art == p_articles + p_roots[p_art->root].articles) { X register char *cp = (*buf=='['?"parent":"root"); X#ifdef VERBOSE X IF(verbose) X fprintf(stdout,"\n\ XThere is no %s article prior to this one.\n",cp) FLUSH; X ELSE X#endif X#ifdef TERSE X fprintf(stdout,"\nNo prior %s.\n",cp) FLUSH; X#endif X return AS_ASK; X } X *buf = '{'; X p_art--; X } X else X p_art += p_art->parent; X X if (*buf == '{') X while (p_art->parent) X p_art += p_art->parent; X X art = p_art->num; X reread = TRUE; X return AS_NORM; X } Xnot_threaded: X if (ThreadedGroup) { X#ifdef VERBOSE X IF(verbose) X fputs("\nThis article is not threaded.\n",stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("\nUnthreaded article.\n",stdout) FLUSH; X#endif X return AS_ASK; X } Xgroup_unthreaded: X#ifdef VERBOSE X IF(verbose) X fputs("\nThis group is not threaded.\n",stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("\nUnthreaded group.\n",stdout) FLUSH; X#endif X return AS_ASK; X case ']': /* goto child article */ X case '}': /* goto thread's leaf article */ X if (p_art) { X if (!(p_art++)->child_cnt) { X PACKED_ARTICLE *root_limit = upper_limit(p_art-1,FALSE); X X if (p_art == root_limit) { X#ifdef VERBOSE X IF(verbose) X fputs("\n\ XThis is the last leaf in this tree.\n",stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("\nLast leaf.\n",stdout) FLUSH; X#endif X p_art--; X return AS_ASK; X } X if (*buf == ']') X *buf = '}'; X else { X while (++p_art != root_limit && p_art->parent) X ; X p_art--; X *buf = ' '; X } X } X if( *buf == '}' ) X while (p_art->child_cnt) X p_art++; X X art = p_art->num; X reread = TRUE; X return AS_NORM; X } X goto not_threaded; X case 'T': X if (p_art) { X sprintf(buf,"T%ld\t# %s",(long)p_roots[p_art->root].root_num, X subject_ptrs[p_art->subject]); X fputs(buf,stdout); X kf_append(buf); X *buf = 'J'; X goto follow_threads; X } X goto not_threaded; X case 'K': X if (p_art) { X /* first, write kill-subject command */ X (void)art_search(buf, (sizeof buf), TRUE); X art = curr_art; X p_art = curr_p_art; X *buf = 'k'; /* then take care of any prior subjs */ X goto follow_threads; X } X goto normal_search; X case ',': /* kill this node and all descendants */ X mark_as_read(); X *buf = 'K'; X case 'k': /* kill current subject # (e.g. [1]) */ X case 'J': /* Junk all nodes in this thread */ X if (!ThreadedGroup) { X *buf = 'k'; X goto normal_search; X } Xfollow_threads: X follow_thread(*buf); X if (!reread && !toread[ng]) X return AS_CLEAN; X if (!reread && selected_root_cnt && !selected_count) X goto select_threads; X return AS_NORM; X case 't': X carriage_return(); X#ifndef CLEAREOL X erase_eol(); /* erase the prompt */ X#else X if (erase_screen && can_home_clear) X clear_rest(); X else X erase_eol(); /* erase the prompt */ X#endif /* CLEAREOL */ X fflush(stdout); X page_line = 1; X p_art = curr_p_art; X entire_tree(); X return AS_ASK; X case ':': /* execute command on selected articles */ X if (!ThreadedGroup) { X goto group_unthreaded; X } X page_line = 1; X if (!use_selected()) X return AS_INP; X putchar('\n'); X art = curr_art; X p_art = curr_p_art; X return AS_ASK; X#endif /* USETHREADS */ X case 'p': /* find previous unread article */ X#ifdef USETHREADS X if (ThreadedGroup) { X goto backtrack_threads; X } X#endif X do { X if (art <= firstart) X break; X art--; X#ifdef SERVER X } while (was_read(art) || nntpopen(art,GET_HEADER) == Nullfp); X#else X } while (was_read(art) || artopen(art) == Nullfp); X#endif X#ifdef ARTSEARCH X srchahead = 0; X#endif X return AS_NORM; X case 'P': /* goto previous article */ X#ifdef USETHREADS X if (ThreadedGroup) { Xbacktrack_threads: X backtrack_thread( *buf ); X art++; /* prepare for art-- below */ X } X#endif X if (art > absfirst) X art--; X else { X#ifdef VERBOSE X IF(verbose) X fprintf(stdout,"\n\ XThere are no%s articles prior to this one.\n\ X",*buf=='P'?nullstr:" unread") FLUSH; X ELSE X#endif X#ifdef TERSE X fprintf(stdout,"\n\ XNo previous%s articles\n\ X",*buf=='P'?nullstr:" unread") FLUSH; X#endif X art = curr_art; X#ifdef USETHREADS X p_art = curr_p_art; X#endif X return AS_ASK; X } X reread = TRUE; X#ifdef ARTSEARCH X srchahead = 0; X#endif X return AS_NORM; X case '-': X if (recent_art >= 0) { X#ifdef USETHREADS X p_art = recent_p_art; X#endif X art = recent_art; X reread = TRUE; X#ifdef ARTSEARCH X srchahead = -(srchahead != 0); X#endif X return AS_NORM; X } X else { X exit_code = NG_MINUS; X return AS_CLEAN; X } X case 'n': /* find next unread article? */ X#ifdef USETHREADS X if (ThreadedGroup) { X goto follow_threads; X } X#endif X if (art > lastart) { X if (!toread[ng]) X return AS_CLEAN; X art = firstart; X } X#ifdef ARTSEARCH X else if (scanon && srchahead) { X *buf = Ctl('n'); X goto normal_search; X } X#endif X else X art++; X X#ifdef ARTSEARCH X srchahead = 0; X#endif X return AS_NORM; X case 'N': /* goto next article */ X#ifdef USETHREADS X if (ThreadedGroup) { X goto follow_threads; X } X#endif X if (art > lastart) X art = absfirst; X else X art++; X if (art <= lastart) X reread = TRUE; X#ifdef ARTSEARCH X srchahead = 0; X#endif X return AS_NORM; X case '$': X art = lastart+1; X forcelast = TRUE; X#ifdef USETHREADS X p_art = Nullart; X#endif X#ifdef ARTSEARCH X srchahead = 0; X#endif X return AS_NORM; X case '1': case '2': case '3': /* goto specified article */ X case '4': case '5': case '6': /* or do something with a range */ X case '7': case '8': case '9': case '.': X forcelast = TRUE; X switch (numnum()) { X case NN_INP: X return AS_INP; X case NN_ASK: X return AS_ASK; X case NN_REREAD: X reread = TRUE; X#ifdef ARTSEARCH X if (srchahead) X srchahead = -1; X#endif X break; X case NN_NORM: X if (was_read(art)) { X#ifdef USETHREADS X first_art(); X#else X art = firstart; X#endif X pad(just_a_sec/3); X } X else { X putchar('\n'); X return AS_ASK; X } X break; X } X return AS_NORM; X case Ctl('k'): X edit_kfile(); X return AS_ASK; X#ifndef USETHREADS X case 'K': X case 'k': X#endif X case Ctl('n'): /* search for next article with same subject */ X#ifdef USETHREADS X if (ThreadedGroup) { X goto follow_threads; X } X#endif X case Ctl('p'): /* search for previous article with same subject */ X#ifdef USETHREADS X if (ThreadedGroup) { X goto backtrack_threads; X } X#endif X case '/': case '?': Xnormal_search: X#ifdef ARTSEARCH X { /* search for article by pattern */ X char cmd = *buf; X X reread = TRUE; /* assume this */ X page_line = 1; X switch (art_search(buf, (sizeof buf), TRUE)) { X case SRCH_ERROR: X art = curr_art; X return AS_ASK; X case SRCH_ABORT: X art = curr_art; X return AS_INP; X case SRCH_INTR: X#ifdef VERBOSE X IF(verbose) X printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; X ELSE X#endif X#ifdef TERSE X printf("\n(Intr at %ld)\n",(long)art) FLUSH; X#endif X art = curr_art; X /* restore to current article */ X return AS_ASK; X case SRCH_DONE: X fputs("done\n",stdout) FLUSH; X pad(just_a_sec/3); /* 1/3 second */ X if (!srchahead) { X art = curr_art; X return AS_ASK; X } X#ifdef USETHREADS X first_art(); X#else X art = firstart; X#endif X reread = FALSE; X return AS_NORM; X case SRCH_SUBJDONE: X#ifdef UNDEF X fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH; X pad(just_a_sec/3); /* 1/3 second */ X#endif X#ifdef USETHREADS X first_art(); X#else X art = firstart; X#endif X reread = FALSE; X return AS_NORM; X case SRCH_NOTFOUND: X fputs("\n\n\n\nNot found.\n",stdout) FLUSH; X art = curr_art; /* restore to current article */ X return AS_ASK; X case SRCH_FOUND: X if (cmd == Ctl('n') || cmd == Ctl('p')) X oldsubject = TRUE; X break; X } X return AS_NORM; X } X#else X buf[1] = '\0'; X notincl(buf); X return AS_ASK; X#endif X case 'u': /* unsubscribe from this newsgroup? */ X rcchar[ng] = NEGCHAR; X return AS_CLEAN; X case 'M': X#ifdef DELAYMARK X if (art <= lastart) { X delay_unmark(art); X printf("\nArticle %ld will return.\n",(long)art) FLUSH; X } X#else X notincl("M"); X#endif X return AS_ASK; X case 'm': X if (art <= lastart) { X unmark_as_read(); X printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH; X } X return AS_ASK; X case 'c': /* catch up */ X reask_catchup: X#ifdef VERBOSE X IF(verbose) X in_char("\nDo you really want to mark everything as read? [yn] ", X 'C'); X ELSE X#endif X#ifdef TERSE X in_char("\nReally? [ynh] ", 'C'); X#endif X putchar('\n') FLUSH; X setdef(buf,"y"); X#ifdef VERIFY X printcmd(); X#endif X if (*buf == 'h') { X#ifdef VERBOSE X IF(verbose) X fputs("\ XType y or SP to mark all articles as read.\n\ XType n to leave articles marked as they are.\n\ XType u to mark everything read and unsubscribe.\n\ X",stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("\ Xy or SP to mark all read.\n\ Xn to forget it.\n\ Xu to mark all and unsubscribe.\n\ X",stdout) FLUSH; X#endif X goto reask_catchup; X } X else if (*buf == 'n' || *buf == 'q') { X return AS_ASK; X } X else if (*buf != 'y' && *buf != 'u') { X fputs(hforhelp,stdout) FLUSH; X settle_down(); X goto reask_catchup; X } X for (i = firstart; i <= lastart; i++) { X oneless(i); /* mark as read */ X } X#ifdef USETHREADS X selected_root_cnt = selected_count = 0; X#endif X#ifdef DELAYMARK X if (dmfp) X yankback(); X#endif X if (*buf == 'u') { X rcchar[ng] = NEGCHAR; X return AS_CLEAN; X } X#ifdef USETHREADS X p_art = Nullart; X selected_count = 0; X#endif X art = lastart+1; X forcelast = FALSE; X return AS_NORM; X case 'Q': X exit_code = NG_ASK; X /* FALL THROUGH */ X case 'q': /* go back up to newsgroup level? */ X return AS_CLEAN; X case 'j': X putchar('\n') FLUSH; X if (art <= lastart) X mark_as_read(); X return AS_ASK; X case 'h': { /* help? */ X int cmd; X X if ((cmd = help_art()) > 0) X pushchar(cmd); X return AS_ASK; X } X case '&': X if (switcheroo()) /* get rest of command */ X return AS_INP; /* if rubbed out, try something else */ X return AS_ASK; X case '#': X#ifdef VERBOSE X IF(verbose) X printf("\nThe last article is %ld.\n",(long)lastart) FLUSH; X ELSE X#endif X#ifdef TERSE X printf("\n%ld\n",(long)lastart) FLUSH; X#endif X return AS_ASK; X#ifdef USETHREADS X case '+': /* enter thread selection mode */ X if (ThreadedGroup) { Xselect_threads: X *buf = select_thread(*buf); X if (*buf == 'q') { X putchar( '\n' ) FLUSH; X return AS_ASK; X } X if (*buf == 'Q') { X exit_code = NG_ASK; X return AS_CLEAN; X } X if (*buf == 'N' || !toread[ng]) X return AS_CLEAN; X return AS_NORM; X } X /* FALLTHROUGH */ X#endif X case '=': { /* list subjects */ X char tmpbuf[256]; X ART_NUM oldart = art; X int cmd; X char *subjline = getval("SUBJLINE",Nullch); X#ifndef CACHESUBJ X char *s; X#endif X X page_init(); X#ifdef CACHESUBJ X if (!subj_list) X fetchsubj(art,TRUE,FALSE); X#endif X for (i=firstart; i<=lastart && !int_count; i++) { X#ifdef CACHESUBJ X if (!was_read(i) && X (subj_list[OFFSET(i)] != Nullch || fetchsubj(i,FALSE,FALSE)) && X *subj_list[OFFSET(i)] ) { X sprintf(tmpbuf,"%5ld ", i); X if (subjline) { X art = i; X interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline); X } X else X safecpy(tmpbuf + 6, subj_list[OFFSET(i)], X (sizeof tmpbuf) - 6); X if (cmd = print_lines(tmpbuf,NOMARKING)) { X if (cmd > 0) X pushchar(cmd); X break; X } X } X#else X if (!was_read(i) && (s = fetchsubj(i,FALSE,FALSE)) && *s) { X sprintf(tmpbuf,"%5ld ", i); X if (subjline) { /* probably fetches it again! */ X art = i; X interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline); X } X else X safecpy(tmpbuf + 6, s, (sizeof tmpbuf) - 6); X if (cmd = print_lines(tmpbuf,NOMARKING)) { X if (cmd > 0) X pushchar(cmd); X break; X } X } X#endif X } X int_count = 0; X art = oldart; X return AS_ASK; X } X case '^': X#ifdef USETHREADS X first_art(); X#else X art = firstart; X#endif X#ifdef ARTSEARCH X srchahead = 0; X#endif X return AS_NORM; X#if defined(CACHESUBJ) && defined(DEBUGGING) X case 'D': X printf("\nFirst article: %ld\n",(long)firstart) FLUSH; X if (!subj_list) X fetchsubj(art,TRUE,FALSE); X if (subj_list != Null(char **)) { X for (i=1; i<=lastart && !int_count; i++) { X if (subj_list[OFFSET(i)]) X printf("%5ld %c %s\n", X i, (was_read(i)?'y':'n'), subj_list[OFFSET(i)]) FLUSH; X } X } X int_count = 0; X return AS_ASK; X#endif X case 'v': X if (art <= lastart) { X reread = TRUE; X do_hiding = FALSE; X } X return AS_NORM; X#ifdef ROTATION X case Ctl('x'): X#endif X case Ctl('r'): X#ifdef ROTATION X rotate = (*buf==Ctl('x')); X#endif X if (art <= lastart) X reread = TRUE; X return AS_NORM; X#ifdef ROTATION X case 'X': X rotate = !rotate; X /* FALL THROUGH */ X#else X case Ctl('x'): X case 'x': X case 'X': X notincl("x"); X return AS_ASK; X#endif X case 'l': case Ctl('l'): /* refresh screen */ X if (art <= lastart) { X reread = TRUE; X clear(); X do_fseek = TRUE; X artline = topline; X if (artline < 0) X artline = 0; X } X return AS_NORM; X case 'b': case Ctl('b'): /* back up a page */ X if (art <= lastart) { X ART_LINE target; X X reread = TRUE; X clear(); X do_fseek = TRUE; X target = topline - (LINES - 2); X artline = topline; X if (artline >= 0) do { X artline--; X } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0); X topline = artline; X if (artline < 0) X artline = 0; X } X return AS_NORM; X case '!': /* shell escape */ X if (escapade()) X return AS_INP; X return AS_ASK; X case 'C': { X cancel_article(); X return AS_ASK; X } X case 'R': X case 'r': { /* reply? */ X reply(); X return AS_ASK; X } X case 'F': X case 'f': { /* followup command */ X followup(); X forcegrow = TRUE; /* recalculate lastart */ X return AS_ASK; X } X case '|': X case 'w': case 'W': X case 's': case 'S': /* save command */ X case 'e': /* extract command */ X if (save_article() == SAVE_ABORT) X return AS_INP; X int_count = 0; X return AS_ASK; X case 'E': X if (uu_out != Nullfp) { X uud_end(); X } X return AS_ASK; X#ifdef DELAYMARK X case 'Y': /* yank back M articles */ X yankback(); X#ifdef USETHREADS X first_art(); X#else X art = firstart; /* from the beginning */ X#endif X return AS_NORM; /* pretend nothing happened */ X#endif X#ifdef STRICTCR X case '\n': X fputs(badcr,stdout) FLUSH; X return AS_ASK; X#endif X default: X printf("\n%s",hforhelp) FLUSH; X settle_down(); X return AS_ASK; X } X} X X#ifdef MAILCALL X/* see if there is any mail */ X Xvoid Xsetmail() X{ X if (! (mailcount++)) { X char *mailfile = filexp(getval("MAILFILE",MAILFILE)); X X if (stat(mailfile,&filestat) < 0 || !filestat.st_size X || filestat.st_atime > filestat.st_mtime) X mailcall = nullstr; X else X mailcall = getval("MAILCALL","(Mail) "); X } X mailcount %= 10; /* check every 10 articles */ X} X#endif X Xvoid Xsetdfltcmd() X{ X#ifdef USETHREADS X if (toread[ng] == unthreaded) { X#else X if (!toread[ng]) { X#endif X if (art > lastart) X dfltcmd = "qnp"; X else X dfltcmd = "npq"; X } X else { X#ifdef USETHREADS X if (selected_root_cnt && !selected_count) X dfltcmd = "+npq"; X else X# ifdef ARTSEARCH X if (!ThreadedGroup && srchahead) X dfltcmd = "^Nnpq"; X else X# endif X#else X# ifdef ARTSEARCH X if (srchahead) X dfltcmd = "^Nnpq"; X else X# endif X#endif X dfltcmd = "npq"; X } X} END_OF_FILE if test 34937 -ne `wc -c <'ng.c'`; then echo shar: \"'ng.c'\" unpacked with wrong size! fi # end of 'ng.c' fi if test -f 'rcstuff.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rcstuff.c'\" else echo shar: Extracting \"'rcstuff.c'\" \(23291 characters\) sed "s/^X//" >'rcstuff.c' <<'END_OF_FILE' X/* $Header: rcstuff.c,v 4.3.3.2 90/08/20 16:47:44 davison Trn $ X * X * $Log: rcstuff.c,v $ X * Revision 4.3.3.2 90/08/20 16:47:44 davison X * Removed ngmax array. X * X * Revision 4.3.3.1 90/06/20 22:39:28 davison X * Initial Trn Release X * X * Revision 4.3.2.5 90/05/04 00:44:07 sob X * Fixes to add_newsgroup() from lar@usl.edu. X * X * Revision 4.3.2.4 90/04/23 00:25:45 sob X * Changed atoi to atol. X * X * Revision 4.3.2.3 89/12/20 23:25:04 sob X * Changed the maximum lenght of a newsgroup name from 20 to 40 characters. X * X * Revision 4.3.2.2 89/11/26 18:22:26 sob X * Added changes to addnewgroup() to cause rn to ask once and only once X * to add a new group to .newsrc. X * Fix provided by Fletcher Mattox <fletcher@cs.utexas.edu> X * X * Revision 4.3.2.1 89/11/06 00:58:29 sob X * Added RRN support from NNTP 1.5 X * X * Revision 4.3.1.5 86/07/24 14:09:10 lwall X * Removed check for spool directory existence in get_ng. X * X * Revision 4.3.1.4 85/09/10 11:04:44 lwall X * Improved %m in in_char(). X * X * Revision 4.3.1.3 85/05/29 09:13:25 lwall X * %d that should be %ld. X * X * Revision 4.3.1.2 85/05/17 11:40:08 lwall X * Sped up "rn -c" by not mallocing unnecessarily. X * X * Revision 4.3.1.1 85/05/10 11:37:18 lwall X * Branch for patches. X * X * Revision 4.3 85/05/01 11:45:56 lwall X * Baseline for release with 4.3bsd. X * X */ X X#include "EXTERN.h" X#include "common.h" X#include "util.h" X#include "ngdata.h" X#include "term.h" X#include "final.h" X#include "rn.h" X#include "intrp.h" X#include "only.h" X#include "rcln.h" X#ifdef SERVER X#include "server.h" X#endif X#include "INTERN.h" X#include "rcstuff.h" X Xchar *rcname INIT(Nullch); /* path name of .newsrc file */ Xchar *rctname INIT(Nullch); /* path name of temp .newsrc file */ Xchar *rcbname INIT(Nullch); /* path name of backup .newsrc file */ Xchar *softname INIT(Nullch); /* path name of .rnsoft file */ XFILE *rcfp INIT(Nullfp); /* .newsrc file pointer */ X X#ifdef HASHNG X short hashtbl[HASHSIZ]; X#endif X Xbool Xrcstuff_init() X{ X register NG_NUM newng; X register char *s; X register int i; X register bool foundany = FALSE; X char *some_buf; X long length; X#ifdef SERVER X char *cp; X#endif /* SERVER */ X X#ifdef HASHNG X for (i=0; i<HASHSIZ; i++) X hashtbl[i] = -1; X#endif X X /* make filenames */ X X#ifdef SERVER X X if (cp = getenv("NEWSRC")) X rcname = savestr(filexp(cp)); X else X rcname = savestr(filexp(RCNAME)); X X#else /* not SERVER */ X X rcname = savestr(filexp(RCNAME)); X X#endif /* SERVER */ X X rctname = savestr(filexp(RCTNAME)); X rcbname = savestr(filexp(RCBNAME)); X softname = savestr(filexp(SOFTNAME)); X X /* make sure the .newsrc file exists */ X X newsrc_check(); X X /* open .rnsoft file containing soft ptrs to active file */ X X tmpfp = fopen(softname,"r"); X if (tmpfp == Nullfp) X writesoft = TRUE; X X /* read in the .newsrc file */ X X for (nextrcline = 0; X (some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch; X nextrcline++) { X /* for each line in .newsrc */ X char tmpbuf[10]; X X newng = nextrcline; /* get it into a register */ X length = len_last_line_got; /* side effect of get_a_line */ X if (length <= 1) { /* only a newline??? */ X nextrcline--; /* compensate for loop increment */ X continue; X } X if (newng >= MAXRCLINE) { /* check for overflow */ X fputs("Too many lines in .newsrc\n",stdout) FLUSH; X finalize(1); X } X if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch) X softptr[newng] = atol(tmpbuf); X else X softptr[newng] = 0; X some_buf[--length] = '\0'; /* wipe out newline */ X if (checkflag) /* no extra mallocs for -c */ X rcline[newng] = some_buf; X else if (some_buf == buf) { X rcline[newng] = savestr(some_buf); X /* make a semipermanent copy */ X } X else { X /*NOSTRICT*/ X#ifndef lint X some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1)); X#endif /* lint */ X rcline[newng] = some_buf; X } X#ifdef NOTDEF X if (strnEQ(some_buf,"to.",3)) { /* is this a non-newsgroup? */ X nextrcline--; /* destroy this line */ X continue; X } X#endif X if (*some_buf == ' ' || X *some_buf == '\t' || X strnEQ(some_buf,"options",7)) { /* non-useful line? */ X toread[newng] = TR_JUNK; X rcchar[newng] = ' '; X rcnums[newng] = 0; X continue; X } X for (s = rcline[newng]; *s && *s != ':' && *s != NEGCHAR; s++) ; X if (!*s && !checkflag) { X#ifndef lint X rcline[newng] = saferealloc(rcline[newng],(MEM_SIZE)length+2); X#endif /* lint */ X s = rcline[newng] + length; X *s = ':'; X *(s+1) = '\0'; X } X rcchar[newng] = *s; /* salt away the : or ! */ X rcnums[newng] = (char)(s - rcline[newng]); X rcnums[newng]++; /* remember where it was */ X *s = '\0'; /* null terminate newsgroup name */ X#ifdef HASHNG X if (!checkflag) X sethash(newng); X#endif X if (rcchar[newng] == NEGCHAR) { X toread[newng] = TR_UNSUB; X continue; X } X X /* now find out how much there is to read */ X X if (!inlist(buf) || (suppress_cn && foundany && !paranoid)) X toread[newng] = TR_NONE; /* no need to calculate now */ X else X set_toread(newng); X#ifdef VERBOSE X if (!checkflag && softmisses == 1) { X softmisses++; /* lie a little */ X fputs("(Revising soft pointers--be patient.)\n",stdout) FLUSH; X } X#endif X if (toread[newng] > TR_NONE) { /* anything unread? */ X if (!foundany) { X starthere = newng; X foundany = TRUE; /* remember that fact*/ X } X if (suppress_cn) { /* if no listing desired */ X if (checkflag) { /* if that is all they wanted */ X finalize(1); /* then bomb out */ X } X } X else { X#ifdef VERBOSE X IF(verbose) X printf("Unread news in %-40s %5ld article%s\n", X rcline[newng],(long)toread[newng], X toread[newng]==TR_ONE ? nullstr : "s") FLUSH; X ELSE X#endif X#ifdef TERSE X printf("%s: %ld article%s\n", X rcline[newng],(long)toread[newng], X toread[newng]==TR_ONE ? nullstr : "s") FLUSH; X#endif X if (int_count) { X countdown = 1; X int_count = 0; X } X if (countdown) { X if (! --countdown) { X fputs("etc.\n",stdout) FLUSH; X if (checkflag) X finalize(1); X suppress_cn = TRUE; X } X } X } X } X } X fclose(rcfp); /* close .newsrc */ X if (tmpfp != Nullfp) X fclose(tmpfp); /* close .rnsoft */ X if (checkflag) { /* were we just checking? */ X finalize(foundany); /* tell them what we found */ X } X if (paranoid) X cleanup_rc(); X X#ifdef DEBUGGING X if (debug & DEB_HASH) { X page_init(); X for (i=0; i<HASHSIZ; i++) { X sprintf(buf,"%d %d",i,hashtbl[i]); X print_lines(buf,NOMARKING); X } X } X#endif X X return foundany; X} X X/* try to find or add an explicitly specified newsgroup */ X/* returns TRUE if found or added, FALSE if not. */ X/* assumes that we are chdir'ed to SPOOL */ X X#ifdef SERVER Xstatic int addnewbydefault = 0; X#endif /* SERVER */ X Xbool Xget_ng(what,do_reloc) Xchar *what; Xbool do_reloc; X{ X char *ntoforget; X char promptbuf[128]; X#ifdef SERVER X char ser_line[256]; X#endif /* SERVER */ X X#ifdef VERBOSE X IF(verbose) X ntoforget = "Type n to forget about this newsgroup.\n"; X ELSE X#endif X#ifdef TERSE X ntoforget = "n to forget it.\n"; X#endif X if (index(what,'/')) { X dingaling(); X printf("\nBad newsgroup name.\n") FLUSH; X return FALSE; X } X set_ngname(what); X ng = find_ng(ngname); X if (ng == nextrcline) { /* not in .newsrc? */ X X#ifdef SERVER X sprintf(ser_line, "GROUP %s", ngname); X put_server(ser_line); X if (get_server(ser_line, sizeof(ser_line)) < 0) { X fprintf(stderr, "rrn: Unexpected close of server socket.\n"); X finalize(1); X } X if (*ser_line != CHAR_OK) { X if (atoi(ser_line) != ERR_NOGROUP) { X fprintf(stderr, "Server response to GROUP %s:\n%s\n", X ngname, ser_line); X } X#else /* not SERVER */ X X if ((softptr[ng] = findact(buf,ngname,strlen(ngname),0L)) < 0 ) { X X#endif /* SERVER */ X X dingaling(); X#ifdef VERBOSE X IF(verbose) X printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH; X ELSE X#endif X#ifdef TERSE X printf("\nNo %s!\n",ngname) FLUSH; X#endif X sleep(2); X return FALSE; X } X#ifdef SERVER X if (addnewbydefault) { X printf("(Adding %s to end of your .newsrc)\n", ngname); X ng = add_newsgroup(ngname, ':'); X do_reloc = FALSE; X } else { X#endif /* SERVER */ X#ifdef VERBOSE X IF(verbose) X sprintf(promptbuf,"\nNewsgroup %s not in .newsrc--add? [yn] ",ngname); X ELSE X#endif X#ifdef TERSE X sprintf(promptbuf,"\nAdd %s? [yn] ",ngname); X#endif Xreask_add: X in_char(promptbuf,'A'); X putchar('\n') FLUSH; X setdef(buf,"y"); X#ifdef VERIFY X printcmd(); X#endif X if (*buf == 'h') { X#ifdef VERBOSE X IF(verbose) X printf("Type y or SP to add %s to your .newsrc.\n", ngname) X FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("y or SP to add\n",stdout) FLUSH; X#endif X fputs(ntoforget,stdout) FLUSH; X goto reask_add; X } X else if (*buf == 'n' || *buf == 'q') { X ng = add_newsgroup(ngname, '!'); X return FALSE; X } X else if (*buf == 'y') { X ng = add_newsgroup(ngname, ':'); X do_reloc = FALSE; X } X#ifdef SERVER X else if (*buf == 'Y') { X fputs( X "(I'll add all new newsgroups to the end of your .newsrc.)\n", stdout); X addnewbydefault = 1; X printf("(Adding %s to end of your .newsrc)\n", ngname); X ng = add_newsgroup(ngname, ':'); X do_reloc = FALSE; X } X#endif /* SERVER */ X else { X fputs(hforhelp,stdout) FLUSH; X settle_down(); X goto reask_add; X } X#ifdef SERVER X } X#endif /* SERVER */ X } X else if (rcchar[ng] == NEGCHAR) { /* unsubscribed? */ X#ifdef VERBOSE X IF(verbose) X sprintf(promptbuf, X"\nNewsgroup %s is currently unsubscribed to--resubscribe? [yn] ",ngname) X FLUSH; X ELSE X#endif X#ifdef TERSE X sprintf(promptbuf,"\n%s unsubscribed--resubscribe? [yn] ",ngname) X FLUSH; X#endif Xreask_unsub: X in_char(promptbuf,'R'); X putchar('\n') FLUSH; X setdef(buf,"y"); X#ifdef VERIFY X printcmd(); X#endif X if (*buf == 'h') { X#ifdef VERBOSE X IF(verbose) X printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("y or SP to resubscribe.\n",stdout) FLUSH; X#endif X fputs(ntoforget,stdout) FLUSH; X goto reask_unsub; X } X else if (*buf == 'n' || *buf == 'q') { X return FALSE; X } X else if (*buf == 'y') { X rcchar[ng] = ':'; X } X else { X fputs(hforhelp,stdout) FLUSH; X settle_down(); X goto reask_unsub; X } X } X X /* now calculate how many unread articles in newsgroup */ X X set_toread(ng); X#ifdef RELOCATE X if (do_reloc) X ng = relocate_newsgroup(ng,-1); X#endif X return toread[ng] >= TR_NONE; X} X X/* add a newsgroup to the .newsrc file (eventually) */ X XNG_NUM Xadd_newsgroup(ngn, c) Xchar *ngn, c; X{ X register NG_NUM newng = nextrcline++; X /* increment max rcline index */ X X rcnums[newng] = strlen(ngn) + 1; X rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 1)); X strcpy(rcline[newng],ngn); /* and copy over the name */ X *(rcline[newng] + rcnums[newng]) = '\0'; X rcchar[newng] = c; /* subscribe or unsubscribe */ X toread[newng] = TR_NONE; /* just for prettiness */ X#ifdef HASHNG X sethash(newng); /* so we can find it again */ X#endif X#ifdef RELOCATE X return c=='!' ? newng : relocate_newsgroup(newng,-1); X#else X return newng; X#endif X} X X#ifdef RELOCATE XNG_NUM Xrelocate_newsgroup(ngx,newng) XNG_NUM ngx; XNG_NUM newng; X{ X char *dflt = (ngx!=current_ng ? "$^.L" : "$^L"); X char *tmprcline; X ART_UNREAD tmptoread; X char tmprcchar; X char tmprcnums; X ACT_POS tmpsoftptr; X register NG_NUM i; X#if defined(DEBUGGING) && !defined(USETHREADS) X ART_NUM tmpngmax; X#endif X#ifdef CACHEFIRST X ART_NUM tmpabs1st; X#endif X X starthere = 0; /* Disable this optimization */ X writesoft = TRUE; /* Update soft pointer file */ X if (ngx < nextrcline-1) { X#ifdef HASHNG X for (i=0; i<HASHSIZ; i++) { X if (hashtbl[i] > ngx) X --hashtbl[i]; X else if (hashtbl[i] == ngx) X hashtbl[i] = nextrcline-1; X } X#endif X tmprcline = rcline[ngx]; X tmptoread = toread[ngx]; X tmprcchar = rcchar[ngx]; X tmprcnums = rcnums[ngx]; X tmpsoftptr = softptr[ngx]; X#if defined(DEBUGGING) && !defined(USETHREADS) X tmpngmax = ngmax[ngx]; X#endif X#ifdef CACHEFIRST X tmpabs1st = abs1st[ngx]; X#endif X for (i=ngx+1; i<nextrcline; i++) { X rcline[i-1] = rcline[i]; X toread[i-1] = toread[i]; X rcchar[i-1] = rcchar[i]; X rcnums[i-1] = rcnums[i]; X softptr[i-1] = softptr[i]; X#if defined(DEBUGGING) && !defined(USETHREADS) X ngmax[i-1] = ngmax[i]; X#endif X#ifdef CACHEFIRST X abs1st[i-1] = abs1st[i]; X#endif X } X rcline[nextrcline-1] = tmprcline; X toread[nextrcline-1] = tmptoread; X rcchar[nextrcline-1] = tmprcchar; X rcnums[nextrcline-1] = tmprcnums; X softptr[nextrcline-1] = tmpsoftptr; X#if defined(DEBUGGING) && !defined(USETHREADS) X ngmax[nextrcline-1] = tmpngmax; X#endif X#ifdef CACHEFIRST X abs1st[nextrcline-1] = tmpabs1st; X#endif X } X if (current_ng > ngx) X current_ng--; X if (newng < 0) { X reask_reloc: X unflush_output(); /* disable any ^O in effect */ X#ifdef SERVER X if (addnewbydefault) { X buf[0] = '$'; X buf[1] = '\0'; X } else { X#endif /* SERVER */ X#ifdef VERBOSE X IF(verbose) X printf("\nPut newsgroup where? [%s] ", dflt); X ELSE X#endif X#ifdef TERSE X printf("\nPut where? [%s] ", dflt); X#endif X fflush(stdout); X reinp_reloc: X eat_typeahead(); X getcmd(buf); X#ifdef SERVER X } X#endif /* SERVER */ X if (errno || *buf == '\f') { X /* if return from stop signal */ X goto reask_reloc; /* give them a prompt again */ X } X setdef(buf,dflt); X#ifdef VERIFY X printcmd(); X#endif X if (*buf == 'h') { X#ifdef VERBOSE X IF(verbose) { X printf("\n\n\ XType ^ to put the newsgroup first (position 0).\n\ XType $ to put the newsgroup last (position %d).\n", nextrcline-1); X printf("\ XType . to put it before the current newsgroup (position %d).\n", current_ng); X printf("\ XType -newsgroup name to put it before that newsgroup.\n\ XType +newsgroup name to put it after that newsgroup.\n\ XType a number between 0 and %d to put it at that position.\n", nextrcline-1); X printf("\ XType L for a listing of newsgroups and their positions.\n") FLUSH; X } X ELSE X#endif X#ifdef TERSE X { X printf("\n\n\ X^ to put newsgroup first (pos 0).\n\ X$ to put last (pos %d).\n", nextrcline-1); X printf("\ X. to put before current newsgroup (pos %d).\n", current_ng); X printf("\ X-newsgroup to put before newsgroup.\n\ X+newsgroup to put after.\n\ Xnumber in 0-%d to put at that pos.\n", nextrcline-1); X printf("\ XL for list of .newsrc.\n") FLUSH; X } X#endif X goto reask_reloc; X } X else if (*buf == 'L') { X putchar('\n') FLUSH; X list_newsgroups(); X goto reask_reloc; X } X else if (isdigit(*buf)) { X if (!finish_command(TRUE)) /* get rest of command */ X goto reinp_reloc; X newng = atol(buf); X if (newng < 0) X newng = 0; X if (newng >= nextrcline) X return nextrcline-1; X } X else if (*buf == '^') { X putchar('\n') FLUSH; X newng = 0; X } X else if (*buf == '$') { X putchar('\n') FLUSH; X return nextrcline-1; X } X else if (*buf == '.') { X putchar('\n') FLUSH; X newng = current_ng; X } X else if (*buf == '-' || *buf == '+') { X if (!finish_command(TRUE)) /* get rest of command */ X goto reinp_reloc; X newng = find_ng(buf+1); X if (newng == nextrcline) { X fputs("Not found.",stdout) FLUSH; X goto reask_reloc; X } X if (*buf == '+') X newng++; X } X else { X printf("\n%s",hforhelp) FLUSH; X settle_down(); X goto reask_reloc; X } X } X if (newng < nextrcline-1) { X#ifdef HASHNG X for (i=0; i<HASHSIZ; i++) { X if (hashtbl[i] == nextrcline-1) X hashtbl[i] = newng; X else if (hashtbl[i] >= newng) X ++hashtbl[i]; X } X#endif X tmprcline = rcline[nextrcline-1]; X tmptoread = toread[nextrcline-1]; X tmprcchar = rcchar[nextrcline-1]; X tmprcnums = rcnums[nextrcline-1]; X tmpsoftptr = softptr[nextrcline-1]; X#if defined(DEBUGGING) && !defined(USETHREADS) X tmpngmax = ngmax[nextrcline-1]; X#endif X#ifdef CACHEFIRST X tmpabs1st = abs1st[nextrcline-1]; X#endif X for (i=nextrcline-2; i>=newng; i--) { X rcline[i+1] = rcline[i]; X toread[i+1] = toread[i]; X rcchar[i+1] = rcchar[i]; X rcnums[i+1] = rcnums[i]; X softptr[i+1] = softptr[i]; X#if defined(DEBUGGING) && !defined(USETHREADS) X ngmax[i+1] = ngmax[i]; X#endif X#ifdef CACHEFIRST X abs1st[i+1] = abs1st[i]; X#endif X } X rcline[newng] = tmprcline; X toread[newng] = tmptoread; X rcchar[newng] = tmprcchar; X rcnums[newng] = tmprcnums; X softptr[newng] = tmpsoftptr; X#if defined(DEBUGGING) && !defined(USETHREADS) X ngmax[newng] = tmpngmax; X#endif X#ifdef CACHEFIRST X abs1st[newng] = tmpabs1st; X#endif X } X if (current_ng >= newng) X current_ng++; X return newng; X} X#endif X X/* List out the newsrc with annotations */ X Xvoid Xlist_newsgroups() X{ X register NG_NUM i; X char tmpbuf[2048]; X static char *status[] = {"(READ)","(UNSUB)","(BOGUS)","(JUNK)"}; X int cmd; X X page_init(); X print_lines("\ X # Status Newsgroup\n\ X",STANDOUT); X for (i=0; i<nextrcline && !int_count; i++) { X if (toread[i] >= 0) X set_toread(i); X *(rcline[i] + rcnums[i] - 1) = rcchar[i]; X if (toread[i] > 0) X sprintf(tmpbuf,"%3d %6ld ",i,(long)toread[i]); X else X sprintf(tmpbuf,"%3d %7s ",i,status[-toread[i]]); X safecpy(tmpbuf+13,rcline[i],2034); X *(rcline[i] + rcnums[i] - 1) = '\0'; X if (cmd = print_lines(tmpbuf,NOMARKING)) { X if (cmd > 0) X pushchar(cmd); X break; X } X } X int_count = 0; X} X X/* find a newsgroup in .newsrc */ X XNG_NUM Xfind_ng(ngnam) Xchar *ngnam; X{ X register NG_NUM ngnum; X#ifdef HASHNG X register int hashix = hash(ngnam); X register int incr = 1; X X while ((ngnum = hashtbl[hashix]) >= 0) { X if (strEQ(rcline[ngnum], ngnam) && toread[ngnum] >= TR_UNSUB) X return ngnum; X hashix = (hashix + incr) % HASHSIZ; X incr += 2; /* offsets from original are in n*2 */ X } X return nextrcline; /* = notfound */ X X#else /* just do linear search */ X X for (ngnum = 0; ngnum < nextrcline; ngnum++) { X if (strEQ(rcline[ngnum],ngnam)) X break; X } X return ngnum; X#endif X} X Xvoid Xcleanup_rc() X{ X register NG_NUM ngx; X register NG_NUM bogosity = 0; X X#ifdef VERBOSE X IF(verbose) X fputs("Checking out your .newsrc--hang on a second...\n",stdout) X FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("Checking .newsrc--hang on...\n",stdout) FLUSH; X#endif X for (ngx = 0; ngx < nextrcline; ngx++) { X if (toread[ngx] >= TR_UNSUB) { X set_toread(ngx); /* this may reset newsgroup */ X /* or declare it bogus */ X } X if (toread[ngx] == TR_BOGUS) X bogosity++; X } X for (ngx = nextrcline-1; ngx >= 0 && toread[ngx] == TR_BOGUS; ngx--) X bogosity--; /* discount already moved ones */ X if (nextrcline > 5 && bogosity > nextrcline / 2) { X fputs( X"It looks like the active file is messed up. Contact your news administrator,\n\ X",stdout); X fputs( X"leave the \"bogus\" groups alone, and they may come back to normal. Maybe.\n\ X",stdout) FLUSH; X } X#ifdef RELOCATE X else if (bogosity) { X#ifdef VERBOSE X IF(verbose) X fputs("Moving bogus newsgroups to the end of your .newsrc.\n", X stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("Moving boguses to the end.\n",stdout) FLUSH; X#endif X for (; ngx >= 0; ngx--) { X if (toread[ngx] == TR_BOGUS) X relocate_newsgroup(ngx,nextrcline-1); X } X#ifdef DELBOGUS Xreask_bogus: X in_char("Delete bogus newsgroups? [ny] ", 'D'); X putchar('\n') FLUSH; X setdef(buf,"n"); X#ifdef VERIFY X printcmd(); X#endif X if (*buf == 'h') { X#ifdef VERBOSE X IF(verbose) X fputs("\ XType y to delete bogus newsgroups.\n\ XType n or SP to leave them at the end in case they return.\n\ X",stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("y to delete, n to keep\n",stdout) FLUSH; X#endif X goto reask_bogus; X } X else if (*buf == 'n' || *buf == 'q') X ; X else if (*buf == 'y') { X while (toread[nextrcline-1] == TR_BOGUS && nextrcline > 0) X --nextrcline; /* real tough, huh? */ X } X else { X fputs(hforhelp,stdout) FLUSH; X settle_down(); X goto reask_bogus; X } X#endif X } X#else X#ifdef VERBOSE X IF(verbose) X fputs("You should edit bogus newsgroups out of your .newsrc.\n", X stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("Edit boguses from .newsrc.\n",stdout) FLUSH; X#endif X#endif X paranoid = FALSE; X} X X#ifdef HASHNG X/* make an entry in the hash table for the current newsgroup */ X Xvoid Xsethash(thisng) XNG_NUM thisng; X{ X register int hashix = hash(rcline[thisng]); X register int incr = 1; X#ifdef DEBUGGING X static int hashhits = 0, hashtries = 0; X#endif X X#ifdef DEBUGGING X hashtries++; X#endif X while (hashtbl[hashix] >= 0) { X#ifdef DEBUGGING X hashhits++; X if (debug & DEB_HASH) { X printf(" Hash hits: %d / %d\n",hashhits, hashtries) FLUSH; X } X hashtries++; X#endif X hashix = (hashix + incr) % HASHSIZ; X incr += 2; /* offsets from original are in n*2 */ X } X hashtbl[hashix] = thisng; X} X Xshort prime[] = {1,2,-3,-5,7,11,-13,-17,19,23,-29,-31,37,41,-43,-47,53,57,-59, X -61,67,71,-73,-79,83,89,-97,-101,1,1,1,1,1,1,1,1,1,1,1,1}; X Xint Xhash(ngnam) Xregister char *ngnam; X{ X register int i = 0; X register int ch; X register int sum = 0; X#ifdef DEBUGGING X char *ngn = ngnam; X#endif X X while (ch = *ngnam++) { X sum += (ch + i) * prime[i]; /* gives ~ 10% hits at 25% full */ X i++; X } X#ifdef DEBUGGING X if (debug & DEB_HASH) X printf("hash(%s) => %d => %d\n",ngn, sum, (sum<0?-sum:sum)%HASHSIZ) X FLUSH; X#endif X if (sum < 0) X sum = -sum; X return sum % HASHSIZ; X} X X#endif X Xvoid Xnewsrc_check() X{ X rcfp = fopen(rcname,"r"); /* open it */ X if (rcfp == Nullfp) { /* not there? */ X#ifdef VERBOSE X IF(verbose) X fputs("\ XTrying to set up a .newsrc file--running newsetup...\n\n\ X",stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("Setting up .newsrc...\n",stdout) FLUSH; X#endif X if (doshell(sh,filexp(NEWSETUP)) || X (rcfp = fopen(rcname,"r")) == Nullfp) { X#ifdef VERBOSE X IF(verbose) X fputs("\ XCan't create a .newsrc--you must do it yourself.\n\ X",stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("(Fatal)\n",stdout) FLUSH; X#endif X finalize(1); X } X } X else { X UNLINK(rcbname); /* unlink backup file name */ X link(rcname,rcbname); /* and backup current name */ X } X} X X/* write out the (presumably) revised .newsrc */ X Xvoid Xwrite_rc() X{ X register NG_NUM tmpng; X register char *delim; X X rcfp = fopen(rctname, "w"); /* open .newsrc */ X if (rcfp == Nullfp) { X printf("Can't recreate .newsrc\n") FLUSH; X finalize(1); X } X X /* write out each line*/ X X for (tmpng = 0; tmpng < nextrcline; tmpng++) { X if (rcnums[tmpng]) { X delim = rcline[tmpng] + rcnums[tmpng] - 1; X *delim = rcchar[tmpng]; X } X else X delim = Nullch; X#ifdef DEBUGGING X if (debug & DEB_NEWSRC_LINE) X printf("%s\n",rcline[tmpng]) FLUSH; X#endif X fprintf(rcfp,"%s\n",rcline[tmpng]); X if (delim) X *delim = '\0'; /* might still need this line */ X } X X fclose(rcfp); /* close .newsrc */ X UNLINK(rcname); X link(rctname,rcname); X UNLINK(rctname); X X if (writesoft) { X tmpfp = fopen(filexp(softname), "w"); /* open .rnsoft */ X if (tmpfp == Nullfp) { X printf(cantcreate,filexp(softname)) FLUSH; X return; X } X for (tmpng = 0; tmpng < nextrcline; tmpng++) { X fprintf(tmpfp,"%ld\n",(long)softptr[tmpng]); X } X fclose(tmpfp); X } X} X Xvoid Xget_old_rc() X{ X UNLINK(rctname); X link(rcname,rctname); X UNLINK(rcname); X link(rcbname,rcname); X UNLINK(rcbname); X} END_OF_FILE if test 23291 -ne `wc -c <'rcstuff.c'`; then echo shar: \"'rcstuff.c'\" unpacked with wrong size! fi # end of 'rcstuff.c' fi echo shar: End of archive 3 \(of 14\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 14 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.