Newsgroups: comp.sources.unix From: davison@borland.com (Wayne Davison) Subject: v29i043: trn-3.6 - threaded newsreader based on RN, V3.6, Part06/14 References: <1.814959141.29825@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: davison@borland.com (Wayne Davison) Posting-Number: Volume 29, Issue 43 Archive-Name: trn-3.6/part06 #!/bin/sh # This is `part06' (part 6 of a multipart archive). # Do not concatenate these parts, unpack them in order with `/bin/sh'. # File `trn-3.6/ngdata.c' is being continued... # touch -am 1231235999 $$.touch >/dev/null 2>&1 if test ! -f 1231235999 && test -f $$.touch; then shar_touch=touch else shar_touch=: echo echo 'WARNING: not restoring timestamps. Consider getting and' echo "installing GNU \`touch', distributed in GNU File Utilities..." echo fi rm -f 1231235999 $$.touch # if test ! -r _sharseq.tmp; then echo 'Please unpack part 1 first!' exit 1 fi shar_sequence=`cat _sharseq.tmp` if test "$shar_sequence" != 6; then echo "Please unpack part $shar_sequence next!" exit 1 fi if test ! -f _sharnew.tmp; then echo 'x - still skipping trn-3.6/ngdata.c' else echo 'x - continuing file trn-3.6/ngdata.c' sed 's/^X//' << 'SHAR_EOF' >> 'trn-3.6/ngdata.c' && X#include "bits.h" X#include "head.h" X#include "rthread.h" X#include "rt-select.h" X#include "ng.h" X#include "intrp.h" X#include "kfile.h" X#include "final.h" X#include "rcln.h" X#include "term.h" X#include "util.h" X#include "nntp.h" X#include "ndir.h" X#include "INTERN.h" X#include "ngdata.h" X X/* open the active file */ X Xvoid Xngdata_init() X{ X#ifdef USE_NNTP X switch (nntp_list("active", "control", 7)) { X case 1: X do { X nntp_gets(ser_line, sizeof ser_line); X } while (!NNTP_LIST_END(ser_line)); X /* FALL THROUGH */ X case 0: X /*actfp = NULL;*/ X fuzzyGet = 0; X break; X case -1: X#endif X ngdatahash_init(); X if (fseek(actfp,0L,0) == -1) { /* just get to the beginning */ X printf("Error seeking in active file.\n") FLUSH; X finalize(1); X } X#ifdef USE_NNTP X break; X } X#endif X} X Xbool Xaccess_ng() X{ X#ifdef USE_NNTP X ART_NUM old_first = abs1st[ng]; X X if (!nntp_group(ngname,ng)) { X toread[ng] = TR_BOGUS; X return FALSE; X } X if ((lastart = getngsize(ng)) < 0) /* this cannot happen (laugh here) */ X return FALSE; X absfirst = abs1st[ng]; X lastart = ngmax[ng]; X if (absfirst > old_first) X checkexpired(ng,absfirst); X#else /* !USE_NNTP */ X 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 } 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 } X toread[ng] = TR_NONE; /* make this newsgroup temporarily invisible */ X return FALSE; X } X X /* chdir to newsgroup subdirectory */ X X if (chdir(ngdir)) { X printf(nocd,ngdir) FLUSH; X return FALSE; X } X if ((lastart = getngsize(ng)) < 0) /* this cannot happen (laugh here) */ X return FALSE; X absfirst = abs1st[ng]; X setfoundbits(); /* might reset absfirst */ X#endif /* !USE_NNTP */ X X dmcount = 0; X in_ng = TRUE; /* tell the world we are here */ X X build_cache(); X return TRUE; X} X Xvoid Xgrow_ng(newlast) XART_NUM newlast; X{ X ART_NUM tmpfirst; X X forcegrow = FALSE; X if (newlast > lastart) { X ART_NUM tmpart = art; X toread[ng] += (ART_UNREAD)(newlast-lastart); X grow_cache(newlast); X tmpfirst = lastart+1; X lastart = newlast; X thread_grow(); X#ifdef KILLFILES X#ifdef VERBOSE X IF(verbose) X sprintf(buf, X "%ld more article%s arrived -- processing memorized commands...\n\n", X (long)(lastart - tmpfirst + 1), X (lastart > tmpfirst ? "s have" : " has" ) ); X ELSE /* my, my, how clever we are */ X#endif X#ifdef TERSE X strcpy(buf, "More news -- auto-processing...\n\n"); X#endif X if (has_normal_kills) { X bool forcelast_save = forcelast; X ARTICLE *artp_save = artp; X kill_unwanted(tmpfirst,buf,TRUE); X artp = artp_save; X forcelast = forcelast_save; X } X#endif X art = tmpart; X } X} X Xvoid Xng_skip() X{ X#ifndef USE_NNTP /* never read it & cannot find it? */ X if (errno != ENOENT) { /* has it not been deleted? */ X clear(); X# ifdef VERBOSE X IF(verbose) X printf("\n(Article %ld exists but is unreadable.)\n",(long)art) X FLUSH; X ELSE X# endif X# ifdef TERSE X printf("\n(%ld unreadable.)\n",(long)art) FLUSH; X# endif X if (novice_delays) { X pad(just_a_sec); X sleep(2); X } X } X inc_art(selected_only,FALSE); /* try next article */ X X#else /* USE_NNTP */ X ART_NUM artnum; X X clear(); X# ifdef VERBOSE X IF(verbose) X fputs("Skipping unavailable article\n",stdout); X ELSE X# endif /* VERBOSE */ X# ifdef TERSE X fputs("Skipping\n",stdout); X# endif /* TERSE */ X if (novice_delays) { X pad(just_a_sec/3); X sleep(1); X } X art++; X artp++; X do { X /* tries to grab PREFETCH_SIZE XHDRS, flagging missing articles */ X (void) fetchsubj(art, FALSE); X artnum = art+PREFETCH_SIZE-1; X if (artnum > lastart) X artnum = lastart; X while (art <= artnum) { X if (!(artp->flags & AF_MISSING)) X return; X art++; X artp++; X } X } while (art <= lastart); X#endif /* USE_NNTP */ X} X X/* find the maximum article number of a newsgroup */ X XART_NUM Xgetngsize(num) Xregister NG_NUM num; X{ X register int len; X register char *nam; X char tmpbuf[LBUFLEN]; X ART_POS oldsoft; X long last, first; X char ch; X X nam = rcline[num]; X len = rcnums[num] - 1; X softtries++; X#ifdef DEBUG X if (debug & DEB_SOFT_POINTERS) X printf("Softptr = %ld\n",(long)softptr[num]) FLUSH; X#endif X oldsoft = softptr[num]; X#ifndef USE_NNTP X fseek(actfp,100000L,1); /* hopefully this forces a reread */ X#endif X if ((softptr[num] = findact(tmpbuf, nam, len, (long)oldsoft)) >= 0) { X if (softptr[num] != oldsoft) { X softmisses++; X writesoft = TRUE; X } X } X else { X softptr[num] = 0; X if (RCCHAR(rcchar[num]) == ':') X rcchar[num] = NEGCHAR; X return TR_BOGUS; X } X X#ifdef DEBUG X if (debug & DEB_SOFT_POINTERS) { X printf("Should be %ld\n",(long)softptr[num]) FLUSH; X } X#endif X#ifdef ANCIENT_NEWS X sscanf(tmpbuf+len+1, "%ld %c", &last, &ch); X first = 1; X#else X sscanf(tmpbuf+len+1, "%ld %ld %c", &last, &first, &ch); X#endif X if (!abs1st[num]) X abs1st[num] = (ART_NUM)first; X if (!in_ng) { X if (redirected) { X if (redirected != nullstr) X free(redirected); X redirected = Nullch; X } X switch (ch) { X case 'n': X moderated = getval("NOPOSTRING"," (no posting)"); X break; X case 'm': X moderated = getval("MODSTRING", " (moderated)"); X break; X case 'x': X redirected = nullstr; X moderated = " (DISABLED)"; X break; X case '=': X len = strlen(tmpbuf); X if (tmpbuf[len-1] == '\n') X tmpbuf[len-1] = '\0'; X redirected = savestr(rindex(tmpbuf, '=') + 1); X moderated = " (REDIRECTED)"; X break; X default: X moderated = nullstr; X break; X } X } X if (last < ngmax[num]) X return ngmax[num]; X return ngmax[num] = (ART_NUM)last; X} SHAR_EOF echo 'File trn-3.6/ngdata.c is complete' && $shar_touch -am 1004153894 'trn-3.6/ngdata.c' && chmod 0644 'trn-3.6/ngdata.c' || echo 'restore of trn-3.6/ngdata.c failed' shar_count="`wc -c < 'trn-3.6/ngdata.c'`" test 6701 -eq "$shar_count" || echo "trn-3.6/ngdata.c: original size 6701, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/ngdata.h ============== if test -f 'trn-3.6/ngdata.h' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/ngdata.h (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/ngdata.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ngdata.h' && X/* $Id: ngdata.h,v 3.0 1992/02/01 03:09:32 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X XEXT FILE *actfp INIT(Nullfp); /* the active file */ XEXT bool writesoft INIT(FALSE); /* rewrite the soft pointer file? */ XEXT int softtries INIT(0), softmisses INIT(0); X XEXT ART_NUM absfirst INIT(0); /* 1st real article in current newsgroup */ XEXT ART_NUM firstart INIT(0); /* minimum unread article number in newsgroup */ XEXT ART_NUM lastart INIT(0); /* maximum article number in newsgroup */ X X#ifdef USE_NNTP XEXT char active_name[MAXFILENAME]; XEXT time_t lastactfetch INIT(0); X#endif X XEXT ART_NUM *abs1st INIT(NULL); /* 1st real article in newsgroup */ X XEXT char *moderated; XEXT char *redirected; XEXT bool ThreadedGroup; XEXT long activeitems; /* number of enties in active file */ X Xvoid ngdata_init _((void)); Xbool access_ng _((void)); Xvoid grow_ng _((ART_NUM)); Xvoid ng_skip _((void)); XART_NUM getngsize _((NG_NUM)); Xvoid getngmissing _((void)); X XACT_POS findact _((char*,char*,int,long)); Xvoid ngdatahash_init _((void)); SHAR_EOF $shar_touch -am 0812152993 'trn-3.6/ngdata.h' && chmod 0644 'trn-3.6/ngdata.h' || echo 'restore of trn-3.6/ngdata.h failed' shar_count="`wc -c < 'trn-3.6/ngdata.h'`" test 1577 -eq "$shar_count" || echo "trn-3.6/ngdata.h: original size 1577, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/nghash.c ============== if test -f 'trn-3.6/nghash.c' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/nghash.c (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/nghash.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/nghash.c' && X/* $Id: nghash.c,v 3.0 1992/02/01 03:09:32 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#include "EXTERN.h" X#include "common.h" X#include "ndir.h" X#include "rcstuff.h" X#include "trn.h" X#include "intrp.h" X#include "final.h" X#include "rcln.h" X#include "util.h" X#include "nntp.h" X#include "ngdata.h" X#include "hash.h" X#include "term.h" X Xstatic HASHTABLE *acthash = 0; Xstatic char *actfile = 0; X Xvoid Xngdatahash_init() X{ X register long offset; X char *cp = filexp(ACTIVE); X struct stat actstat; X X#ifdef USE_NNTP X nntp_command("LIST"); /* tell server we want the active file */ X if (nntp_check(TRUE) != NNTP_CLASS_OK) { /* and then see if that's ok */ X printf("Can't get active file from server: \n%s\n", ser_line); X finalize(1); X } X time(&lastactfetch); X X strcpy(active_name, cp); X actfp = fopen(active_name, "w+"); /* and get ready */ X if (actfp == Nullfp) { X printf(cantopen,active_name) FLUSH; X finalize(1); X } X X while (1) { X nntp_gets(ser_line, sizeof ser_line); X if (NNTP_LIST_END(ser_line)) /* while there's another line */ X break; /* get it and write it to */ X fputs(ser_line, actfp); X putc('\n', actfp); X } X X fflush(actfp); X if (ferror(actfp)) { X printf("Error writing to active file %s.\n", active_name) FLUSH; X finalize(1); X } X fseek(actfp,0L,0); /* rewind file */ X X#else /* !USE_NNTP */ X actfp = fopen(cp, "r"); X if (actfp == Nullfp) { X printf(cantopen, cp) FLUSH; X finalize(1); X } X#endif X X /* rn was being a slug about rereading the active file over and X * over; try using a really big buffer to keep it in core. */ X (void) fstat(fileno(actfp), &actstat); X if (actfile) X free(actfile); X actfile = safemalloc(actstat.st_size + 1); X X /* X * NOTE: this won't work on machines with ints too small to hold X * the size of the active file. X */ X if (fread(actfile, 1, (int)actstat.st_size, actfp) != actstat.st_size) { X fprintf(stderr, "active file not read\n"); X actfile[0] = '\0'; X } X actfile[actstat.st_size] = '\0'; X X if (acthash) X hashdestroy(acthash); X acthash = hashcreate((int)(actstat.st_size/40), (int (*)())NULL); X X /* count entries */ X for (activeitems=0, offset=0; offset < actstat.st_size; activeitems++) { X register char *p; X HASHDATUM data; X X data.dat_ptr = actfile + offset; X if ((p = index(data.dat_ptr, '\n')) == NULL) X data.dat_len = actstat.st_size - offset; X else X data.dat_len = p - data.dat_ptr + 1; X if ((p = index(data.dat_ptr, ' ')) == NULL) X p = data.dat_ptr; X hashstore(acthash, data.dat_ptr, p - data.dat_ptr, data); X offset += data.dat_len; X } X} X XACT_POS Xfindact(outbuf, nam, len, suggestion) Xregister char *outbuf; Xregister char *nam; Xregister int len; Xregister long suggestion; X{ X register ACT_POS retval; X X#ifdef USE_NNTP X if (!actfp) { X switch (nntp_list("active", nam, len)) { X case 0: X return -1; X case 1: X strcpy(outbuf, ser_line); X do { X nntp_gets(ser_line, sizeof ser_line); X } while (!NNTP_LIST_END(ser_line)); X break; X } X return 0; X } X#endif X X /* see if we know the right place and can just return */ X if (suggestion != 0 && fseek(actfp, suggestion, 0) >= 0 X && fgets(outbuf, LBUFLEN, actfp) != NULL && outbuf[len] == ' ' X && strnEQ(outbuf, nam, len)) X return suggestion; X X /* hmm, apparently not, gotta look it up. */ X#ifdef DEBUG X if (debug & DEB_SOFT_POINTERS) X printf("Missed, looking for %s in %sLen = %d\n",nam,outbuf,len) FLUSH; X#endif X /* can we just do a quick hashed lookup? */ X if (acthash != NULL) { X HASHDATUM data; X X outbuf[0] = '\0'; X data = hashfetch(acthash, nam, len); X if (data.dat_ptr == NULL) X return -1; X else { X (void) bcopy(data.dat_ptr, outbuf, (int)data.dat_len); X outbuf[data.dat_len] = '\0'; X return data.dat_ptr - actfile; X } X } X X /* apparently not. gotta do it the slow way. */ X (void) fseek(actfp, 0L, 0); X for (retval=0; fgets(outbuf,LBUFLEN,actfp)!=NULL; retval+=strlen(outbuf)) X if (outbuf[len] == ' ' && strnEQ(outbuf, nam, len)) X break; X if (ferror(actfp)) { X perror("error on active file"); X sig_catcher(0); X /* NOTREACHED */ X } else if (feof(actfp)) X return -1; /* no such group */ X return retval; X} X X#ifdef EDIT_DISTANCE X X/* Edit Distance extension to trn X * X * Mark Maimone (mwm@cmu.edu) X * Carnegie Mellon Computer Science X * 9 May 1993 X * X * This code helps trn handle typos in newsgroup names much more X * gracefully. Instead of "... does not exist!!", it will pick the X * nearest one, or offer you a choice if there are several options. X */ X X#define LENGTH_HACK 5 /* Don't bother comparing strings with lengths X * that differ by more than this; making this X * smaller than MIN_DIST prunes the number of X * calls to edit_dist(). We won't catch any X * sequences of MIN_DIST INSERTs or DELETEs, X * but we (should!) save some time. */ X#define MAX_NG 9 /* Maximum number of options */ X X#define ABS(x) (((x) < 0) ? -(x) : (x)) X Xstatic void check_distance _((HASHDATUM*,int)); X Xstatic char **ngptrs; /* List of potential matches */ Xstatic int ngn; /* Length of list in ngptrs[] */ Xstatic int nglen; /* Length of name in ngname */ Xstatic int best_match; /* Value of best match */ X X/* find_close_match -- Finds the closest match for the string given in X * global ngname. If found, the result will be the corrected string X * returned in that global. X * X * We compare the (presumably misspelled) newsgroup name with all legal X * newsgroups, using the Edit Distance metric. The edit distance between X * two strings is the minimum number of simple operations required to X * convert one string to another (the implementation here supports INSERT, X * DELETE, CHANGE and SWAP). This gives every legal newsgroup an integer X * rank. X * X * You might want to present all of the closest matches, and let the user X * choose among them. But because I'm lazy I chose to only keep track of X * all with newsgroups with the *single* smallest error, in array ngptrs[]. X * A more flexible approach would keep around the 10 best matches, whether X * or not they had precisely the same edit distance, but oh well. X */ X Xint Xfind_close_match() X{ X int ret = 0; X X best_match = -1; X ngptrs = (char**)safemalloc(MAX_NG * sizeof (char*)); X ngn = 0; X nglen = strlen(ngname); X X/* Iterate over all legal newsgroups */ X hashwalk(acthash, check_distance, 0); X X/* ngn is the number of possibilities. If there's just one, go with it. */ X X switch (ngn) { X case 0: X break; X case 1: { X char *cp = index(ngptrs[0], ' '); X *cp = '\0'; X#ifdef VERBOSE X IF(verbose) X printf("(I assume you meant %s)\n", ngptrs[0]) FLUSH; X ELSE X#endif X#ifdef TERSE X printf("(Using %s)\n", ngptrs[0]) FLUSH; X#endif X set_ngname(ngptrs[0]); X *cp = ' '; X ret = 1; X break; X } X default: X ret = get_near_miss(); X break; X } /* switch */ X free((char*)ngptrs); X return ret; X} /* find_close_match */ X Xstatic void Xcheck_distance(data, ngnum) XHASHDATUM *data; Xint ngnum; X{ X int value, len; X char *cp; X if ((cp = index(data->dat_ptr, ' ')) == NULL) X return; X len = cp - data->dat_ptr; X X/* Efficiency: don't call edit_dist when the lengths are already too X * different */ X X if (ABS(len - nglen) > LENGTH_HACK) X return; X X value = edit_distn(ngname, strlen(ngname), data->dat_ptr, len); X if (value > MIN_DIST) X return; X X if (value < best_match) X ngn = 0; X if (best_match < 0 || value <= best_match) { X best_match = value; X if (ngn < MAX_NG) X ngptrs[ngn++] = data->dat_ptr; X } /* if */ X} X X/* Now we've got several potential matches, and have to choose between them X * somehow. Again, results will be returned in global ngname */ X Xint Xget_near_miss() X{ X char promptbuf[256]; X char options[MAX_NG+10], *op = options; X int i; X X#ifdef VERBOSE X IF(verbose) X printf("However, here are some close matches:\n") FLUSH; X#endif X if (ngn > 9) X ngn = 9; /* Since we're using single digits.... */ X for (i = 0; i < ngn; i++) { X char *cp = index(ngptrs[i], ' '); X *cp = '\0'; X printf(" %d. %s\n", i+1, ngptrs[i]); X sprintf(op++, "%d", i+1); /* Expensive, but avoids ASCII deps */ X *cp = ' '; X } X *op++ = 'n'; X *op = '\0'; X X#ifdef VERBOSE X IF(verbose) X sprintf(promptbuf, "Which of these would you like?"); X ELSE X#endif X#ifdef TERSE X sprintf(promptbuf, "Which?"); X#endif Xreask: X in_char(promptbuf, 'A', options); X#ifdef VERIFY X printcmd(); X#endif X putchar('\n') FLUSH; X switch (*buf) { X case 'n': X case 'N': X case 'q': X case 'Q': X case 'x': X case 'X': X return 0; X break; X case 'h': X case 'H': X#ifdef VERBOSE X IF(verbose) X fputs(" You entered an illegal newsgroup name, and these are the nearest possible\n matches. If you want one of these, then enter its number. Otherwise\n just say 'n'.\n", stdout) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("Illegal newsgroup, enter a number or 'n'.\n", stdout) FLUSH; X#endif X goto reask; X default: X if (isdigit(*buf)) { X char *s = index(options, *buf); X X i = s ? (s - options) : ngn; X if (i >= 0 && i < ngn) { X char *cp = index(ngptrs[i], ' '); X *cp = '\0'; X set_ngname(ngptrs[i]); X *cp = ' '; X return 1; X } /* if */ X } /* if */ X fputs(hforhelp, stdout) FLUSH; X break; X } /* switch */ X X settle_down(); X goto reask; X} /* get_near_miss */ X X#endif /* EDIT_DISTANCE */ SHAR_EOF $shar_touch -am 1004153894 'trn-3.6/nghash.c' && chmod 0644 'trn-3.6/nghash.c' || echo 'restore of trn-3.6/nghash.c failed' shar_count="`wc -c < 'trn-3.6/nghash.c'`" test 9954 -eq "$shar_count" || echo "trn-3.6/nghash.c: original size 9954, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/ngsrch.c ============== if test -f 'trn-3.6/ngsrch.c' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/ngsrch.c (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/ngsrch.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ngsrch.c' && X/* $Id: ngsrch.c,v 3.0 1992/02/01 03:09:32 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#include "EXTERN.h" X#include "common.h" X#include "rcstuff.h" X#include "final.h" X#include "search.h" X#include "trn.h" X#include "util.h" X#include "term.h" X#include "rcln.h" X#include "INTERN.h" X#include "ngsrch.h" X X#ifdef NGSEARCH XCOMPEX ngcompex; X#endif X Xvoid Xngsrch_init() X{ X#ifdef NGSEARCH X init_compex(&ngcompex) X#endif X ; X} X X#ifdef NGSEARCH Xint Xng_search(patbuf,get_cmd) Xchar *patbuf; /* if patbuf != buf, get_cmd must */ Xint get_cmd; /* be set to FALSE!!! */ X{ X char *pattern; /* unparsed pattern */ X register char cmdchr = *patbuf; /* what kind of search? */ X register char *s; X bool backward = cmdchr == '?'; /* direction of search */ X X int_count = 0; X if (get_cmd && buf == patbuf) X if (!finish_command(FALSE)) /* get rest of command */ X return NGS_ABORT; X for (pattern = patbuf+1; *pattern == ' '; pattern++) ; X if (*pattern) { X ng_doread = FALSE; X } X s = rindex(pattern,cmdchr); X if (s != Nullch && *(s-1) != '\\') { X *s++ = '\0'; X if (index(s,'r') != Nullch) X ng_doread = TRUE; X } X if ((s = ng_comp(&ngcompex,pattern,TRUE,TRUE)) != Nullch) { X /* compile regular expression */ X printf("\n%s\n",s) FLUSH; X return NGS_ERROR; X } X fputs("\nSearching...",stdout) FLUSH; /* give them something to read */ X fflush(stdout); X for (;;) { X if (int_count) { X int_count = 0; X return NGS_INTR; X } X if (backward) { X if (ng > 0) X --ng; X else X ng = nextrcline; X } X else { X if (ng >= nextrcline) X ng = 0; X else X ++ng; X } X if (ng == current_ng) X return NGS_NOTFOUND; X if (ng == nextrcline || toread[ng] < TR_NONE || !ng_wanted()) X continue; X if (toread[ng] == TR_NONE) X set_toread(ng); X X if (toread[ng] > TR_NONE) X return NGS_FOUND; X else if (toread[ng] == TR_NONE) X if (ng_doread) X return NGS_FOUND; X else X printf("\n[0 unread in %s -- skipping]",rcline[ng]) FLUSH; X } X} X Xbool Xng_wanted() X{ X return execute(&ngcompex,rcline[ng]) != Nullch; X} X#endif /* NGSEARCH */ X X#ifdef NGSORONLY Xchar * Xng_comp(compex,pattern,RE,fold) XCOMPEX *compex; Xchar *pattern; Xbool_int RE; Xbool_int fold; X{ X char ng_pattern[128]; X register char *s = pattern, *d = ng_pattern; X X if (!*s) { X if (compile(compex, "", RE, fold)) X return "No previous search pattern"; X else X return Nullch; /* reuse old pattern */ X } X for (; *s; s++) { X if (*s == '.') { X *d++ = '\\'; X *d++ = *s; X } X else if (*s == '?') { X *d++ = '.'; X } X else if (*s == '*') { X *d++ = '.'; X *d++ = *s; X } X#if OLD_RN_WAY X else if (strnEQ(s,"all",3)) { X *d++ = '.'; X *d++ = '*'; X s += 2; X } X#endif X else X *d++ = *s; X } X *d = '\0'; X return compile(compex,ng_pattern,RE,fold); X} X#endif X SHAR_EOF $shar_touch -am 0823175594 'trn-3.6/ngsrch.c' && chmod 0644 'trn-3.6/ngsrch.c' || echo 'restore of trn-3.6/ngsrch.c failed' shar_count="`wc -c < 'trn-3.6/ngsrch.c'`" test 3373 -eq "$shar_count" || echo "trn-3.6/ngsrch.c: original size 3373, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/ngsrch.h ============== if test -f 'trn-3.6/ngsrch.h' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/ngsrch.h (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/ngsrch.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ngsrch.h' && X/* $Id: ngsrch.h,v 3.0 1992/02/01 03:09:32 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#ifdef NGSEARCH X#define NGS_ABORT 0 X#define NGS_FOUND 1 X#define NGS_INTR 2 X#define NGS_NOTFOUND 3 X#define NGS_ERROR 4 X XEXT bool ng_doread INIT(FALSE); /* search read newsgroups? */ X#endif X Xvoid ngsrch_init _((void)); X#ifdef NGSEARCH Xint ng_search _((char*,int)); Xbool ng_wanted _((void)); X#endif X#ifdef NGSORONLY Xchar *ng_comp _((COMPEX*,char*,bool_int,bool_int)); X#endif SHAR_EOF $shar_touch -am 0711223693 'trn-3.6/ngsrch.h' && chmod 0644 'trn-3.6/ngsrch.h' || echo 'restore of trn-3.6/ngsrch.h failed' shar_count="`wc -c < 'trn-3.6/ngsrch.h'`" test 1025 -eq "$shar_count" || echo "trn-3.6/ngsrch.h: original size 1025, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/ngstuff.c ============== if test -f 'trn-3.6/ngstuff.c' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/ngstuff.c (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/ngstuff.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ngstuff.c' && X/* $Id: ngstuff.c,v 3.0 1991/09/09 20:23:31 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#include "EXTERN.h" X#include "common.h" X#include "term.h" X#include "util.h" X#include "util2.h" X#include "cache.h" X#include "bits.h" X#include "ngdata.h" X#include "ng.h" X#include "intrp.h" X#include "head.h" X#include "final.h" X#include "sw.h" X#include "rthread.h" X#include "rt-select.h" X#include "rt-wumpus.h" X#include "trn.h" X#include "rcstuff.h" X#include "respond.h" X#include "kfile.h" X#include "decode.h" X#include "INTERN.h" X#include "ngstuff.h" X Xvoid Xngstuff_init() X{ X ; X} X X/* do a shell escape */ X Xint Xescapade() X{ X register char *s; X bool interactive = (buf[1] == FINISHCMD); X bool docd; X char whereiam[512]; X X if (!finish_command(interactive)) /* get remainder of command */ X return -1; X s = buf+1; X docd = *s != '!'; X if (!docd) { X s++; X } X else { X getwd(whereiam); X if (chdir(cwd)) { X printf(nocd,cwd) FLUSH; X sig_catcher(0); X } X } X while (*s == ' ') s++; X /* skip leading spaces */ X interp(cmd_buf, (sizeof cmd_buf), s);/* interpret any % escapes */ X resetty(); /* make sure tty is friendly */ X doshell(Nullch,cmd_buf); /* invoke the shell */ X noecho(); /* and make terminal */ X crmode(); /* unfriendly again */ X if (docd) { X if (chdir(whereiam)) { X printf(nocd,whereiam) FLUSH; X sig_catcher(0); X } X } X#ifdef MAILCALL X mailcount = 0; /* force recheck */ X#endif X return 0; X} X X/* process & command */ X Xint Xswitcheroo() X{ X if (!finish_command(TRUE)) /* get rest of command */ X return -1; /* if rubbed out, try something else */ X if (!buf[1]) X pr_switches(); X else if (buf[1] == '&') { X if (!buf[2]) { X page_init(); X show_macros(); X } X else { X char tmpbuf[LBUFLEN]; X register char *s; X X for (s=buf+2; isspace(*s); s++); X mac_line(s,tmpbuf,(sizeof tmpbuf)); X } X } X else { X bool docd = (instr(buf,"-d", TRUE) != Nullch); X char whereami[512]; X X if (docd) X getwd(whereami); X sw_list(buf+1); X if (docd) { X cwd_check(); X if (chdir(whereami)) { /* -d does chdirs */ X printf(nocd,whereami) FLUSH; X sig_catcher(0); X } X } X } X return 0; X} X X/* process range commands */ X Xint Xnumnum() X{ X ART_NUM min, max; X char *cmdlst = Nullch; X register char *s, *c; X ART_NUM oldart = art; X char tmpbuf[LBUFLEN]; X bool justone = TRUE; /* assume only one article */ X X perform_cnt = 0; X if (!finish_command(TRUE)) /* get rest of command */ X return NN_INP; X if (lastart < 1) { X fputs("\nNo articles\n",stdout) FLUSH; X return NN_ASK; X } X#ifdef ARTSEARCH X if (srchahead) X srchahead = -1; X#endif X for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++) X if (!isdigit(*s)) X justone = FALSE; X if (*s) { X cmdlst = savestr(s); X justone = FALSE; X } X else if (!justone) X cmdlst = savestr("m"); X *s++ = ','; X *s = '\0'; X safecpy(tmpbuf,buf,LBUFLEN); X for (s = tmpbuf; c = index(s,','); s = ++c) { X *c = '\0'; X if (*s == '.') X min = oldart; X else X min = atol(s); X if (min < absfirst) { X min = absfirst; X printf("(First article is %ld)\n",(long)absfirst) FLUSH; X pad(just_a_sec/3); X } X if ((s=index(s,'-')) != Nullch) { X s++; X if (*s == '$') X max = lastart; X else if (*s == '.') X max = oldart; X else X max = atol(s); X } X else X max = min; X if (max>lastart) { X max = lastart; X if (min > max) X min = max; X printf("(Last article is %ld)\n",(long)lastart) FLUSH; X pad(just_a_sec/3); X } X if (max < min) { X fputs("\nBad range\n",stdout) FLUSH; X if (cmdlst) X free(cmdlst); X return NN_ASK; X } X if (justone) { X art = min; X return NN_REREAD; X } X for (art=min, artp=article_ptr(min); art<=max; art++, artp++) { X if (perform(cmdlst,TRUE)) { X#ifdef VERBOSE X IF(verbose) X printf("\n(Interrupted at article %ld)\n",(long)art) X FLUSH; X ELSE X#endif X#ifdef TERSE X printf("\n(Intr at %ld)\n",(long)art) FLUSH; X#endif X if (cmdlst) X free(cmdlst); X return NN_ASK; X } X } X } X art = oldart; X if (cmdlst) X free(cmdlst); X return NN_NORM; X} X Xint Xthread_perform() X{ X register SUBJECT *sp; X register ARTICLE *ap; X bool want_read; X char *cmdstr; X int len, ret = 1; X int bits; X bool one_thread = FALSE; X X if (!finish_command(TRUE)) /* get rest of command */ X return 0; X if (!buf[1]) X return -1; X len = 1; X if (buf[1] == ':') { X bits = 0; X len++; X } X else X bits = SF_VISIT; X if (buf[len] == '.') { X if (!artp) X return -1; X one_thread = TRUE; X len++; X } X cmdstr = savestr(buf+len); X want_read = (sel_rereading || *cmdstr == 'm'); X X perform_cnt = 0; X page_line = 1; X len = strlen(cmdstr); X X /* A few commands can just loop through the subjects. */ X if ((len == 1 && (*cmdstr == 't' || *cmdstr == 'J')) X || (len == 2 X && (((*cmdstr == '+' || *cmdstr == '-') && cmdstr[0] == cmdstr[1]) X || *cmdstr == 'T'))) { X if (one_thread) X sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); X else X sp = next_subj(Nullsubj,bits); X for ( ; sp; sp = next_subj(sp,bits)) { X if (!(sp->flags & sel_mask) ^ !bits) X continue; X artp = first_art(sp); X if (artp) { X art = article_num(artp); X if (perform(cmdstr, FALSE)) { X fputs("\nInterrupted\n", stdout) FLUSH; X goto break_out; X } X } X#ifdef VERBOSE X IF(verbose) X if (mode != 't' && *cmdstr != 't' && *cmdstr != 'T') X putchar('\n') FLUSH; X#endif X if (one_thread) X break; X } X } else if (strEQ(cmdstr, "E")) { X /* The 'E'nd-decode command doesn't do any looping at all. */ X if (decode_fp) X decode_end(); X else X ret = 2; X } else if (*cmdstr == 'p') { X ART_NUM oldart = art; X art = lastart+1; X followup(); X forcegrow = TRUE; X art = oldart; X } else { X /* The rest loop through the articles. */ X /* Use the explicit article-order if it exists */ X if (artptr_list) { X ARTICLE **app, **limit = artptr_list + artptr_list_size; X sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); X for (app = artptr_list; app < limit; app++) { X ap = *app; X if (one_thread && ap->subj->thread != sp->thread) X continue; X if ((!(ap->flags & AF_READ) ^ want_read) X && !(ap->flags & sel_mask) ^ !!bits) { X art = article_num(ap); X artp = ap; X if (perform(cmdstr, TRUE)) { X fputs("\nInterrupted\n", stdout) FLUSH; X goto break_out; X } X } X } X } else { X if (one_thread) X sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); X else X sp = next_subj(Nullsubj,bits); X for ( ; sp; sp = next_subj(sp,bits)) { X for (ap = first_art(sp); ap; ap = next_art(ap)) X if ((!(ap->flags & AF_READ) ^ want_read) X && !(ap->flags & sel_mask) ^ !!bits) { X art = article_num(ap); X artp = ap; X if (perform(cmdstr, TRUE)) { X fputs("\nInterrupted\n", stdout) FLUSH; X goto break_out; X } X } X if (one_thread) X break; X } X } X } X break_out: X free(cmdstr); X return ret; X} X Xint Xperform(cmdlst,toplevel) Xregister char *cmdlst; Xint toplevel; X{ X register int ch; X bool saveit = FALSE; X X if (toplevel) { X printf("%-6ld ",art); X fflush(stdout); X } X perform_cnt++; X for (; ch = *cmdlst; cmdlst++) { X if (isspace(ch) || ch == ':') X continue; X if (ch == 'j' && !saveit) { X if (!was_read(art)) { X mark_as_read(); X#ifdef VERBOSE X IF(verbose) X fputs("\tJunked",stdout); X#endif X } X if (sel_rereading) X deselect_article(artp); X } else if (ch == '+') { X if (saveit || cmdlst[1] == '+') { X if (sel_mode == SM_THREAD) X select_arts_thread(artp, saveit? AUTO_SELECTALL : 0); X else X select_arts_subject(artp, saveit? AUTO_SELECTALL : 0); X if (cmdlst[1] == '+') X cmdlst++; X } else X select_article(artp, AUTO_ECHO); X } else if (ch == '.') { X select_subthread(artp, saveit? AUTO_SELECT : 0); X } else if (ch == '-') { X if (cmdlst[1] == '-') { X if (sel_mode == SM_THREAD) X deselect_arts_thread(artp); X else X deselect_arts_subject(artp); X cmdlst++; X } else X deselect_article(artp); X } else if (ch == ',') { X kill_subthread(artp, saveit? (KF_ALL|KF_KILLFILE) : KF_ALL); X } else if (ch == 'J' || ch == 'j') { X if (sel_mode == SM_THREAD) X kill_arts_thread(artp, saveit? (KF_ALL|KF_KILLFILE) : KF_ALL); X else X kill_arts_subject(artp, saveit? (KF_ALL|KF_KILLFILE) : KF_ALL); X } else if (ch == 'x') { X if (!was_read(art)) { X oneless(artp); X#ifdef VERBOSE X IF(verbose) X fputs("\tKilled",stdout); X#endif X } X if (sel_rereading) X deselect_article(artp); X } else if (ch == 't') { X entire_tree(artp); X } else if (ch == 'T') { X saveit = TRUE; X } else if (ch == 'm') { X if ((article_ptr(art)->flags & (AF_READ|AF_MISSING)) == AF_READ) { X unmark_as_read(); X#ifdef VERBOSE X IF(verbose) X fputs("\tMarked unread",stdout); X#endif X } X } X else if (ch == 'M') { X delay_unmark(artp); X oneless(artp); X#ifdef VERBOSE X IF(verbose) X fputs("\tWill return",stdout); X#endif X } X else if (ch == '=') { X printf("\t%s",fetchsubj(art,FALSE)); X#ifdef VERBOSE X IF(verbose) X ; X ELSE X#endif X putchar('\n') FLUSH; /* ghad! */ X } X else if (ch == 'C') { X#ifdef ASYNC_PARSE X printf("\t%sancelled",(cancel_article() ? "Not c" : "C")); X#else X notincl("C"); X return -1; X#endif X } X else if (ch == '%') { X#ifdef ASYNC_PARSE X char tmpbuf[512]; X X if (one_command) X interp(tmpbuf, (sizeof tmpbuf), cmdlst); X else X cmdlst = dointerp(tmpbuf, (sizeof tmpbuf), cmdlst, ":") - 1; X perform_cnt--; X if (perform(tmpbuf,FALSE)) X return -1; X#else X notincl("%"); X return -1; X#endif X } X else if (index("!&sSwWe|",ch)) { X if (one_command) X strcpy(buf,cmdlst); X else X cmdlst = cpytill(buf,cmdlst,':') - 1; X /* we now have the command in buf */ X if (ch == '!') { X escapade(); X#ifdef VERBOSE X IF(verbose) X fputs("\tShell escaped",stdout); X#endif X } X else if (ch == '&') { X switcheroo(); X#ifdef VERBOSE X IF(verbose) X if (buf[1] && buf[1] != '&') X fputs("\tSwitched",stdout); X#endif X } X else { X putchar('\t'); X save_article(); X#ifdef VERBOSE X IF(verbose) X ; X ELSE X#endif X putchar('\n') FLUSH; X } X } X else { X printf("\t???%s\n",cmdlst); X return -1; X } X#ifdef VERBOSE X fflush(stdout); X#endif X if (one_command) X break; X } X if (toplevel) { X#ifdef VERBOSE X IF(verbose) X putchar('\n') FLUSH; X#endif X } X if (int_count) { X int_count = 0; X return -1; X } X return 0; X} SHAR_EOF $shar_touch -am 1118220194 'trn-3.6/ngstuff.c' && chmod 0644 'trn-3.6/ngstuff.c' || echo 'restore of trn-3.6/ngstuff.c failed' shar_count="`wc -c < 'trn-3.6/ngstuff.c'`" test 11106 -eq "$shar_count" || echo "trn-3.6/ngstuff.c: original size 11106, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/ngstuff.h ============== if test -f 'trn-3.6/ngstuff.h' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/ngstuff.h (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/ngstuff.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ngstuff.h' && X/* $Id: ngstuff.h,v 3.0 1991/09/09 20:23:31 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#define NN_NORM 0 X#define NN_INP 1 X#define NN_REREAD 2 X#define NN_ASK 3 X XEXT bool one_command INIT(FALSE); /* no ':' processing in perform() */ X Xvoid ngstuff_init _((void)); Xint escapade _((void)); Xint switcheroo _((void)); Xint numnum _((void)); Xint perform _((char*,int)); Xint thread_perform _((void)); SHAR_EOF $shar_touch -am 1118220194 'trn-3.6/ngstuff.h' && chmod 0644 'trn-3.6/ngstuff.h' || echo 'restore of trn-3.6/ngstuff.h failed' shar_count="`wc -c < 'trn-3.6/ngstuff.h'`" test 956 -eq "$shar_count" || echo "trn-3.6/ngstuff.h: original size 956, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/nntp.c ============== if test -f 'trn-3.6/nntp.c' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/nntp.c (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/nntp.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/nntp.c' && X/* $Id: nntp.c,v 3.0 1991/11/22 04:12:21 davison Trn $ X*/ X/* The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#include "EXTERN.h" X#include "common.h" X#include "util.h" X#include "init.h" X#include "trn.h" X#include "ngdata.h" X#include "rcln.h" X#include "cache.h" X#include "bits.h" X#include "head.h" X#include "term.h" X#include "final.h" X#include "artio.h" X#include "nntp.h" X X#ifdef USE_NNTP X Xint Xnntp_list(type, arg, len) Xchar *type; Xchar *arg; Xint len; X{ X int pos; X sprintf(ser_line, "list %s ", type); X pos = strlen(ser_line); X strncpy(ser_line+pos, arg, len); X ser_line[pos+len] = '\0'; X nntp_command(ser_line); X if (nntp_check(FALSE) != NNTP_CLASS_OK) { X if (*ser_line == NNTP_CLASS_FATAL) X return -1; X return 0; X } X nntp_gets(ser_line, sizeof ser_line); X#if defined(DEBUG) && defined(FLUSH) X if (debug & DEB_NNTP) X printf("<%s\n", ser_line) FLUSH; X#endif X if (NNTP_LIST_END(ser_line)) X return 0; X return 1; X} X X/* try to access the specified group */ X Xbool Xnntp_group(group, num) Xchar *group; XNG_NUM num; X{ X sprintf(ser_line, "GROUP %s", group); X nntp_command(ser_line); X if (nntp_check(FALSE) != NNTP_CLASS_OK) { X int ser_int = atoi(ser_line); X if (ser_int != NNTP_NOSUCHGROUP_VAL X && ser_int != NNTP_SYNTAX_VAL) { X if (ser_int != NNTP_AUTH_NEEDED_VAL && ser_int != NNTP_ACCESS_VAL X && ser_int != NNTP_AUTH_REJECT_VAL) { X fprintf(stderr, "\nServer's response to GROUP %s:\n%s\n", X group, ser_line); X finalize(1); X } X } X return FALSE; X } X if (num >= 0) { X long count, first, last; X X (void) sscanf(ser_line,"%*d%ld%ld%ld",&count,&first,&last); X /* NNTP mangles the high/low values when no articles are present. */ X if (!count) X abs1st[num] = ngmax[num]+1; X else { X abs1st[num] = (ART_NUM)first; X if (last > ngmax[num]) X ngmax[num] = (ART_NUM)last; X } X } X return TRUE; X} X X/* check on an article's existence */ X Xbool Xnntp_stat(artnum) XART_NUM artnum; X{ X sprintf(ser_line, "STAT %ld", (long)artnum); X nntp_command(ser_line); X return (nntp_check(TRUE) == NNTP_CLASS_OK); X} X X/* check on an article's existence by its message id */ X XART_NUM Xnntp_stat_id(msgid) Xchar *msgid; X{ X long artnum; X X sprintf(ser_line, "STAT %s", msgid); X nntp_command(ser_line); X if (nntp_check(TRUE) != NNTP_CLASS_OK X || sscanf(ser_line, "%*d%ld", &artnum) != 1) X return 0; X return (ART_NUM)artnum; X} X X/* prepare to get the header */ X Xbool Xnntp_header(artnum) XART_NUM artnum; X{ X sprintf(ser_line, "HEAD %ld", (long)artnum); X nntp_command(ser_line); X return (nntp_check(TRUE) == NNTP_CLASS_OK); X} X X/* copy the body of an article to a temporary file */ X Xstatic bool nntp_copybody _((char*,int,long)); Xstatic long body_pos = -1; Xstatic long body_end = 0; X XFILE * Xnntp_body(artnum) XART_NUM artnum; X{ X char *artname; X FILE *fp; X X if (!parseheader(artnum)) X return Nullfp; X artname = nntp_artname(); X if (!(fp = fopen(artname, "w+"))) { X fprintf(stderr, "\nUnable to write temporary file: '%s'.\n", X artname); X finalize(1); X } X sprintf(ser_line, "BODY %ld", (long)artnum); X nntp_command(ser_line); X if (nntp_check(TRUE) != NNTP_CLASS_OK) { /* and get it's reaction */ X fclose(fp); X errno = ENOENT; /* Simulate file-not-found */ X return Nullfp; X } X fwrite(headbuf, 1, strlen(headbuf), fp); X body_pos = 0; X body_end = ftell(fp); X htype[PAST_HEADER].ht_minpos = body_end; X#ifdef GRAB_IT_ALL X nntp_copybody(ser_line, sizeof ser_line, 0x7fffffffL); X#endif X fseek(fp, 0L, 0); X return fp; X} X Xlong Xnntp_artsize() X{ X return body_pos < 0 ? body_end : -1; X} X Xstatic bool Xnntp_copybody(s, limit, pos) Xchar *s; Xint limit; Xlong pos; X{ X int len; X bool had_nl = TRUE; X bool found_nl; X while (pos > body_end || !had_nl) { X found_nl = nntp_gets(s, limit-1); X if (had_nl) { X if (NNTP_LIST_END(s)) { X fseek(artfp, body_pos, 0); X body_pos = -1; X return FALSE; X } X if (s[0] == '.') { X strcpy(s, s+1); X } X } X len = strlen(s); X if (found_nl) X strcpy(s+len, "\n"); X fputs(s, artfp); X body_end = ftell(artfp); X had_nl = found_nl; X } X return TRUE; X} X Xint Xnntp_finishbody(bmode) Xint bmode; X{ X char b[NNTP_STRLEN]; X if (body_pos < 0) X return FALSE; X if (bmode == FB_OUTPUT) { X#ifdef VERBOSE X IF(verbose) X printf("Receiving the rest of the article..."), fflush(stdout); X ELSE X#endif X#ifdef TERSE X printf("Receiving..."), fflush(stdout); X#endif X } X if (body_end != body_pos) X fseek(artfp, body_end, 0); X if (bmode != FB_BACKGROUND) X nntp_copybody(b, sizeof b, 0x7fffffffL); X else { X while (nntp_copybody(b, sizeof b, body_end+1)) { X if (input_pending()) X break; X } X if (body_pos >= 0) X fseek(artfp, body_pos, 0); X } X if (bmode == FB_OUTPUT) { X carriage_return(); X erase_eol(); /* erase the prompt */ X carriage_return(); /* Resets kernel's tab column counter to 0 */ X } X return TRUE; X} X Xvoid Xnntp_seekart(pos) Xlong pos; X{ X if (body_pos >= 0) { X if (body_end < pos) { X char b[NNTP_STRLEN]; X fseek(artfp, body_end, 0); X nntp_copybody(b, sizeof b, pos); X if (body_pos >= 0) X body_pos = pos; X } X else X body_pos = pos; X } X fseek(artfp, pos, 0); X} X Xlong Xnntp_tellart() X{ X return body_pos < 0 ? ftell(artfp) : body_pos; X} X Xchar * Xnntp_readart(s, limit) Xchar *s; Xint limit; X{ X if (body_pos >= 0) { X if (body_pos == body_end) { X if (!nntp_copybody(s, limit, body_pos+1)) X return Nullch; X body_pos = body_end; X return s; X } X s = fgets(s, limit, artfp); X body_pos = ftell(artfp); X if (body_pos == body_end) X fseek(artfp, body_pos, 0); X return s; X } X return fgets(s, limit, artfp); X} X X/* This is a 1-relative list */ Xstatic int maxdays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; X Xtime_t Xnntp_time() X{ X char *s; X int year, month, day, hh, mm; X time_t ss; X X nntp_command("XDATE" + (CompliantServer? 0 : 1)); X if (nntp_check(FALSE) != NNTP_CLASS_INF) X return time((time_t*)NULL); X X s = rindex(ser_line, ' ') + 1; X month = (s[4] - '0') * 10 + (s[5] - '0'); X day = (s[6] - '0') * 10 + (s[7] - '0'); X hh = (s[8] - '0') * 10 + (s[9] - '0'); X mm = (s[10] - '0') * 10 + (s[11] - '0'); X ss = (s[12] - '0') * 10 + (s[13] - '0'); X s[4] = '\0'; X year = atoi(s); X X /* This simple algorithm will be valid until the year 2400 */ X if (year % 4) X maxdays[2] = 28; X else X maxdays[2] = 29; X if (month < 1 || month > 12 || day < 1 || day > maxdays[month] X || hh < 0 || hh > 23 || mm < 0 || mm > 59 X || ss < 0 || ss > 59) X return time((time_t*)NULL); X X for (month--; month; month--) X day += maxdays[month]; X X ss = ((((year-1970) * 365 + (year-1968)/4 + day - 1) * 24L + hh) * 60 X + mm) * 60 + ss; X X return ss; X} X Xbool Xnntp_newgroups(t) Xtime_t t; X{ X struct tm *ts; X X ts = gmtime(&t); X sprintf(ser_line, "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT", X ts->tm_year % 100, ts->tm_mon+1, ts->tm_mday, X ts->tm_hour, ts->tm_min, ts->tm_sec); X nntp_command(ser_line); X return (nntp_check(TRUE) == NNTP_CLASS_OK); X} X Xbool Xnntp_listgroup() X{ X#ifdef NO_LISTGROUP X static bool listgroup_works = FALSE; X#else X static bool listgroup_works = TRUE; X#endif X X if (!listgroup_works) X return FALSE; X nntp_command("XLISTGROUP" + (CompliantServer? 0 : 1)); X if (nntp_check(FALSE) != NNTP_CLASS_OK) { X listgroup_works = FALSE; X return FALSE; X } X return TRUE; X} X Xchar * Xnntp_artname() X{ X static char artname[20]; X sprintf(artname,"rrn.%ld",our_pid); X return artname; X} X Xchar Xnntp_handle_timeout(strict) Xbool_int strict; X{ X static bool handling_timeout = FALSE; X char last_command_save[NNTP_STRLEN]; X char ch; X X if (handling_timeout) X return NNTP_CLASS_FATAL; X handling_timeout = TRUE; X strcpy(last_command_save, last_command); X nntp_close(FALSE); X if (!nntp_connect(0) || (in_ng && !nntp_group(ngname, -1))) { X nntp_error("\n503 Server timed out.\n"); X finalize(1); X } X nntp_command(last_command_save); X ch = nntp_check(strict); X handling_timeout = FALSE; X return ch; X} X X/* cleanup the odds and ends associated with NNTP usage */ X Xvoid Xnntp_cleanup() X{ X UNLINK(nntp_artname()); X if (*active_name) X UNLINK(active_name); X nntp_close(TRUE); X} X X#ifdef USE_XTHREAD X Xstatic long rawbytes = -1; /* bytes remaining to be transfered */ X X/* nntp_readcheck -- get a line of text from the server, interpreting X** it as a status message for a binary command. Call this once X** before calling nntp_read() for the actual data transfer. X*/ Xlong Xnntp_readcheck() X{ X /* try to get the status line and the status code */ X if (nntp_check(FALSE) != NNTP_CLASS_OK) X return rawbytes = -1; X X /* try to get the number of bytes being transfered */ X if (sscanf(ser_line, "%*d%ld", &rawbytes) != 1) X return rawbytes = -1; X return rawbytes; X} X X/* nntp_read -- read data from the server in binary format. This call must X** be preceeded by an appropriate binary command and an nntp_readcheck call. X*/ Xlong Xnntp_read(buf, n) Xchar *buf; Xlong n; X{ X /* if no bytes to read, then just return EOF */ X if (rawbytes < 0) X return 0; X X#ifdef HAS_SIGHOLD X sighold(SIGINT); X#endif X X /* try to read some data from the server */ X if (rawbytes) { X n = fread(buf, 1, n > rawbytes ? rawbytes : n, ser_rd_fp); X rawbytes -= n; X } else X n = 0; X X /* if no more left, then fetch the end-of-command signature */ X if (!rawbytes) { X char buf[5]; /* "\r\n.\r\n" */ X X fread(buf, 1, 5, ser_rd_fp); X rawbytes = -1; X } X#ifdef HAS_SIGHOLD X sigrelse(SIGINT); X#endif X return n; X} X#endif /* USE_XTHREAD */ X X#endif /* USE_NNTP */ SHAR_EOF $shar_touch -am 1118220194 'trn-3.6/nntp.c' && chmod 0644 'trn-3.6/nntp.c' || echo 'restore of trn-3.6/nntp.c failed' shar_count="`wc -c < 'trn-3.6/nntp.c'`" test 9741 -eq "$shar_count" || echo "trn-3.6/nntp.c: original size 9741, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/cache.c ============== if test -f 'trn-3.6/cache.c' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/cache.c (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/cache.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/cache.c' && X/* $Id: cache.c,v 3.0 1992/02/01 03:09:32 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#include "EXTERN.h" X#include "common.h" X#include "INTERN.h" X#include "cache.h" X#include "EXTERN.h" X#include "intrp.h" X#include "search.h" X#include "ng.h" X#include "trn.h" X#include "ngdata.h" X#include "term.h" X#include "final.h" X#include "artsrch.h" X#include "head.h" X#include "bits.h" X#include "rcstuff.h" X#include "hash.h" X#include "nntp.h" X#include "rthread.h" X#include "rt-ov.h" X#include "rt-page.h" X#include "rt-process.h" X#include "rt-select.h" X#include "rt-util.h" X#include "util.h" X#include "util2.h" X X#ifdef PENDING X# ifdef ARTSEARCH X COMPEX srchcompex; /* compiled regex for searchahead */ X# endif X#endif X XHASHTABLE *subj_hash = 0; XHASHTABLE *shortsubj_hash = 0; X Xint subject_cmp _((char *,int,HASHDATUM)); X Xvoid Xcache_init() X{ X#ifdef PENDING X# ifdef ARTSEARCH X init_compex(&srchcompex) X# endif X#endif X ; X} X XNG_NUM cached_ng = -1; Xtime_t cached_time = 0; XART_NUM cached_cnt = 0; XART_NUM cached_absfirst = 0; X Xvoid Xbuild_cache() X{ X if (cached_ng == ng && cached_absfirst == absfirst X && time((time_t*)NULL) < cached_time + 6*60*60L) { X grow_cache(lastart); X rc_to_bits(); X if (sel_mode == SM_ARTICLE) X set_selector(sel_mode, sel_artsort); X else X set_selector(sel_threadmode, sel_threadsort); X thread_grow(); X return; X } X X close_cache(); X X cached_ng = ng; X cached_absfirst = absfirst; X cached_time = time((time_t*)NULL); X cached_cnt = lastart-absfirst+2 + 10; X article_list = (ARTICLE*) X safemalloc((MEM_SIZE)(cached_cnt * sizeof (ARTICLE))); X bzero((char*)article_list, cached_cnt * sizeof (ARTICLE)); X subj_hash = hashcreate(201, subject_cmp); /*TODO: pick a better size */ X X rc_to_bits(); /* sets firstart */ X first_cached = thread_always? absfirst : firstart; X last_cached = first_cached-1; X cached_all_in_range = FALSE; X#ifdef PENDING X subj_to_get = xref_to_get = firstart; X#endif X#ifndef USE_NNTP X setmissingbits(); X#endif X X /* Cache as much data in advance as possible, possibly threading X ** articles as we go. */ X thread_open(); X} X X#define FIXPTR(p) if ((p) && (p) >= oldlist_start && (p) <= oldlist_end)\ X (p) = (article_list + ((p) - oldlist_start)); else X Xvoid Xgrow_cache(newlast) XART_NUM newlast; X{ X ART_NUM new_cnt = newlast-absfirst+2; X X if (new_cnt > cached_cnt) { X ARTICLE *oldlist_start = article_list; X ARTICLE *oldlist_end = oldlist_start + cached_cnt; X new_cnt += 10; X article_list = (ARTICLE*)saferealloc((char*)article_list, X (MEM_SIZE)(new_cnt * sizeof (ARTICLE))); X bzero((char*)(article_list+cached_cnt), X (new_cnt-cached_cnt) * sizeof (ARTICLE)); X if (article_list != oldlist_start) { X register ARTICLE *ap; X register SUBJECT *sp; X for (sp = first_subject; sp; sp = sp->next) { X FIXPTR(sp->thread); X FIXPTR(sp->articles); X if (sp->thread) { X for (ap = sp->thread; ap; ap = bump_art(ap)) { X FIXPTR(ap->child1); X FIXPTR(ap->parent); X FIXPTR(ap->sibling); X FIXPTR(ap->subj_next); X } X } else { X for (ap = sp->articles; ap; ) { X FIXPTR(ap->subj_next); X ap = ap->subj_next; X } X } X } X FIXPTR(artp); X FIXPTR(curr_artp); X FIXPTR(recent_artp); X } X cached_cnt = new_cnt; X } X cached_time = time((time_t*)NULL); X} X Xvoid Xclose_cache() X{ X SUBJECT *sp, *next; X ARTICLE *ap; X ART_NUM i; X X if (subj_hash) { X hashdestroy(subj_hash); X subj_hash = 0; X } X if (shortsubj_hash) { X hashdestroy(shortsubj_hash); X shortsubj_hash = 0; X } X /* Free all the subjects. */ X for (sp = first_subject; sp; sp = next) { X next = sp->next; X free(sp->str); X free((char*)sp); X } X first_subject = last_subject = Nullsubj; X subject_count = 0; /* just to be sure */ X parsed_art = 0; X X if (artptr_list) { X free((char*)artptr_list); X artptr_list = Null(ARTICLE**); X } X artptr = Null(ARTICLE**); X thread_close(); X X if (cached_cnt) { X for (i = 0, ap = article_list; i < cached_cnt; i++, ap++) X clear_article(ap); X free((char*)article_list); X cached_cnt = 0; X } X cached_ng = -1; X} X X/* The article has all it's data in place, so add it to the list of articles X** with the same subject. X*/ Xvoid Xcache_article(ap) Xregister ARTICLE *ap; X{ X register ARTICLE *next, *ap2; X X if (!(next = ap->subj->articles) || ap->date < next->date) X ap->subj->articles = ap; X else { X while ((next = (ap2 = next)->subj_next) && next->date <= ap->date) X ; X ap2->subj_next = ap; X } X ap->subj_next = next; X ap->flags |= AF_CACHED; X X if (!(ap->flags & AF_READ) ^ sel_rereading) { X if (ap->subj->flags & SF_WASSELECTED) X select_article(ap, 0); X else X ap->subj->flags |= SF_VISIT; X } X X if (join_subject_len != 0) X check_for_near_subj(ap); X} X Xvoid Xcheck_for_near_subj(ap) XARTICLE *ap; X{ X register SUBJECT *sp; X if (!shortsubj_hash) { X shortsubj_hash = hashcreate(201, subject_cmp); /*TODO: pick a better size */ X sp = first_subject; X } X else { X sp = ap->subj; X if (sp->next) X sp = 0; X } X while (sp) { X if (strlen(sp->str+4) >= join_subject_len && sp->thread) { X SUBJECT *sp2; X HASHDATUM data; X data = hashfetch(shortsubj_hash, sp->str+4, join_subject_len); X if (!(sp2 = (SUBJECT*)data.dat_ptr)) { X data.dat_ptr = (char*)sp; X hashstorelast(data); X } X else if (sp->thread != sp2->thread) { X merge_threads(sp2, sp); X } X } X sp = sp->next; X } X} X Xvoid Xchange_join_subject_len(len) Xint len; X{ X if (join_subject_len != len) { X if (shortsubj_hash) { X hashdestroy(shortsubj_hash); X shortsubj_hash = 0; X } X join_subject_len = len; X if (len && first_subject && first_subject->articles) X check_for_near_subj(first_subject->articles); X } X} X Xvoid Xcheck_poster(ap) Xregister ARTICLE *ap; X{ X if (auto_select_postings && !(ap->flags & AF_MISSING) && ap->from) { X if (ap->flags & AF_FROMTRUNCED) { X strcpy(cmd_buf,realname); X if (strEQ(ap->from,compress_name(cmd_buf,16))) { X untrim_cache = TRUE; X fetchfrom(article_num(ap),FALSE); X untrim_cache = FALSE; X } X } X if (!(ap->flags & AF_FROMTRUNCED)) { X char *s = cmd_buf, *u, *h; X strcpy(s,ap->from); X if ((h=index(s,'<')) != Nullch) { /* grab the good part */ X s = h+1; X if ((h=index(s,'>')) != Nullch) X *h = '\0'; X } else if ((h=index(s,'(')) != Nullch) { X while (h-- != s && *h == ' ') X ; X h[1] = '\0'; /* or strip the comment */ X } X if ((h=index(s,'%'))!=Nullch || (h=index(s,'@'))!=Nullch) { X *h++ = '\0'; X u = s; X } else if ((u=rindex(s,'!')) != Nullch) { X *u++ = '\0'; X h = s; X } else X h = u = s; X if (strEQ(u,loginName)) { X if (instr(h,phostname,FALSE)) { X switch (auto_select_postings) { X case '.': X select_subthread(ap,AUTO_SELECT); X break; X case '+': X select_arts_thread(ap,AUTO_SELECT); X break; X case 'p': X if (ap->parent) X select_subthread(ap->parent,AUTO_SELECT); X else X select_subthread(ap,AUTO_SELECT); X break; X } X } else { X#ifdef REPLYTO_POSTER_CHECKING X char *reply_buf = fetchlines(article_num(ap),REPLY_LINE); X if (instr(reply_buf,loginName,TRUE)) X select_subthread(ap,AUTO_SELECT); X free(reply_buf); X#endif X } X } X } X } X} X X/* The article turned out to be a duplicate, so remove it from the cached X** list and possibly destroy the subject (should only happen if the data X** was corrupt and the duplicate id got a different subject). X*/ Xvoid Xuncache_article(ap, remove_empties) Xregister ARTICLE *ap; Xbool_int remove_empties; X{ X register ARTICLE *next; X X if (ap->subj) { X if ((ap->flags & (AF_CACHED|AF_MISSING)) == AF_CACHED) { X if ((next = ap->subj->articles) == ap) X ap->subj->articles = ap->subj_next; X else { X register ARTICLE *ap2; X while (next) { X if ((ap2 = next->subj_next) == ap) { X next->subj_next = ap->subj_next; X break; X } X next = ap2; X } X } X } X if (remove_empties && !ap->subj->articles) { X register SUBJECT *sp = ap->subj; X if (sp == first_subject) X first_subject = sp->next; X else X sp->prev->next = sp->next; X if (sp == last_subject) X last_subject = sp->prev; X else X sp->next->prev = sp->prev; X hashdelete(subj_hash, sp->str+4, strlen(sp->str+4)); X free((char*)sp); X ap->subj = Nullsubj; X subject_count--; X } X } X onemissing(ap); X} X X/* get the header line from an article's cache or parse the article trying */ X Xchar * Xfetchcache(artnum,which_line,fill_cache) XART_NUM artnum; Xint which_line; Xbool_int fill_cache; X{ X register char *s; X register ARTICLE *ap; X register bool cached = (htype[which_line].ht_flags & HT_CACHED); X X /* find_article() returns a Nullart if the artnum value is invalid */ X if (!(ap = find_article(artnum)) || (ap->flags & AF_MISSING)) X return nullstr; X if (cached && (s=get_cached_line(ap,which_line,untrim_cache)) != Nullch) X return s; X if (!fill_cache) X return Nullch; X if (!parseheader(artnum)) X return nullstr; X if (cached) X return get_cached_line(ap,which_line,untrim_cache); X return Nullch; X} X X/* Return a pointer to a cached header line for the indicated article. X** Truncated headers (e.g. from a .thread file) are optionally ignored. X*/ Xchar * Xget_cached_line(ap, which_line, no_truncs) Xregister ARTICLE *ap; Xint which_line; Xbool_int no_truncs; X{ X register char *s; X X switch (which_line) { X case SUBJ_LINE: X if (!ap->subj || (no_truncs && (ap->subj->flags & SF_SUBJTRUNCED))) X s = Nullch; X else X s = ap->subj->str + ((ap->flags & AF_HAS_RE) ? 0 : 4); X break; X case FROM_LINE: X if (no_truncs && (ap->flags & AF_FROMTRUNCED)) X s = Nullch; X else X s = ap->from; X break; X#ifdef DBM_XREFS X case NGS_LINE: X#else X case XREF_LINE: X#endif X s = ap->xrefs; X break; X case MESSID_LINE: X s = ap->msgid; X break; X default: X s = Nullch; X break; X } X return s; X} X Xvoid Xset_subj_line(ap, subj, size) Xregister ARTICLE *ap; Xregister char *subj; /* not yet allocated, so we can tweak it first */ Xregister int size; X{ X HASHDATUM data; X SUBJECT *sp; X char *newsubj, *subj_start; X char *t, *f; X int i; X X while (*subj && *subj != '\n' && (unsigned char)*subj <= ' ') X subj++; X if (subj != (subj_start = get_subject_start(subj))) { X if ((size -= subj_start - subj) < 0) X size = 0; X ap->flags |= AF_HAS_RE; X } X if (ap->subj && strnEQ(ap->subj->str+4, subj_start, size)) X return; X X newsubj = safemalloc(size + 4 + 1); X strcpy(newsubj, "Re: "); X for (t = newsubj + 4, f = subj_start, i = size; i--; ) { X if ((unsigned char)*f <= ' ') { X while (i && ((unsigned char)*++f <= ' ')) X i--, size--; X *t++ = ' '; X } else if (*f != '\n') X *t++ = *f++; X else X f++; X } X while (size > 4 && t[-1] == ' ') X t--, size--; X *t = '\0'; X X if (ap->subj) { X /* This only happens when we freshen truncated subjects */ X hashdelete(subj_hash, ap->subj->str+4, strlen(ap->subj->str+4)); X free(ap->subj->str); X ap->subj->str = newsubj; X data.dat_ptr = (char*)ap->subj; X hashstore(subj_hash, newsubj + 4, size, data); X } else { X data = hashfetch(subj_hash, newsubj + 4, size); X if (!(sp = (SUBJECT*)data.dat_ptr)) { X sp = (SUBJECT*)safemalloc(sizeof (SUBJECT)); X bzero((char*)sp, sizeof (SUBJECT)); X subject_count++; X if ((sp->prev = last_subject) != NULL) X sp->prev->next = sp; X else X first_subject = sp; X last_subject = sp; X sp->str = newsubj; X sp->thread_link = sp; X sp->flags = 0; X X data.dat_ptr = (char*)sp; X hashstorelast(data); X } else X free(newsubj); X ap->subj = sp; X } X} X Xvoid Xset_cached_line(ap, which_line, s) Xregister ARTICLE *ap; Xregister int which_line; Xregister char *s; /* already allocated, ready to save */ X{ X char *cp; X /* SUBJ_LINE is handled specially above */ X switch (which_line) { X case FROM_LINE: X ap->flags &= ~AF_FROMTRUNCED; X if (ap->from) X free(ap->from); X ap->from = s; X break; X#ifdef DBM_XREFS X case NGS_LINE: X if (ap->xrefs && ap->xrefs != nullstr) X free(ap->xrefs); X if (!index(s, ',')) { /* if no comma, no Xref! */ X free(s); X s = nullstr; X } X ap->xrefs = s; X break; X#else X case XREF_LINE: X if (ap->xrefs && ap->xrefs != nullstr) X free(ap->xrefs); X /* Exclude an xref for just this group or "(none)". */ X cp = index(s, ':'); X if (!cp || !index(cp+1, ':')) { X free(s); X s = nullstr; X } X ap->xrefs = s; X break; X#endif X case MESSID_LINE: X if (ap->msgid) X free(ap->msgid); X ap->msgid = s; X break; X } X} X Xint Xsubject_cmp(key, keylen, data) Xchar *key; Xint keylen; XHASHDATUM data; X{ X /* We already know that the lengths are equal, just compare the strings */ X return bcmp(key, ((SUBJECT*)data.dat_ptr)->str+4, keylen); X} X X/* see what we can do while they are reading */ X X#ifdef PENDING Xvoid Xlook_ahead() X{ X#ifdef ARTSEARCH X register char *h, *s; X X#ifdef DEBUG X if (debug && srchahead) { X printf("(%ld)",(long)srchahead); X fflush(stdout); X } X#endif X#endif X X if (ThreadedGroup) { X artp = curr_artp; X inc_art(selected_only,FALSE); X if (artp) X parseheader(art); X } X else X#ifdef ARTSEARCH X if (srchahead && srchahead < art) { /* in ^N mode? */ X char *pattern; X X pattern = buf+1; X strcpy(pattern,": *"); X h = pattern + strlen(pattern); X interp(h,(sizeof buf) - (h-buf),"%\\s"); X { /* compensate for notesfiles */ X register int i; X for (i = 24; *h && i--; h++) X if (*h == '\\') X h++; X *h = '\0'; X } X#ifdef DEBUG X if (debug & DEB_SEARCH_AHEAD) { X fputs("(hit CR)",stdout); X fflush(stdout); X fgets(buf+128, sizeof buf-128, stdin); X printf("\npattern = %s\n",pattern); X } X#endif X if ((s = compile(&srchcompex,pattern,TRUE,TRUE)) != Nullch) { X /* compile regular expression */ X printf("\n%s\n",s) FLUSH; X srchahead = 0; X } X if (srchahead) { X srchahead = art; X for (;;) { X srchahead++; /* go forward one article */ X if (srchahead > lastart) { /* out of articles? */ X#ifdef DEBUG X if (debug) X fputs("(not found)",stdout); X#endif X break; X } X if (!was_read(srchahead) && X wanted(&srchcompex,srchahead,0)) { X /* does the shoe fit? */ X#ifdef DEBUG X if (debug) X printf("(%ld)",(long)srchahead); X#endif X parseheader(srchahead); X break; X } X if (input_pending()) X break; X } X fflush(stdout); X } X } X else X#endif /* ARTSEARCH */ X { X if (art+1 <= lastart) /* how about a pre-fetch? */ X parseheader(art+1); /* look for the next article */ X } X} X#endif /* PENDING */ X X/* see what else we can do while they are reading */ X Xvoid Xcache_until_key() X{ X#ifdef PENDING X if (!in_ng || input_pending()) X return; X X untrim_cache = TRUE; X sentinel_artp = curr_artp; X X#ifdef USE_NNTP X if (nntp_finishbody(FB_BACKGROUND)) X return; X#endif X X /* Prioritize our caching based on what mode we're in */ X if (mode == 't') { X if (cache_subjects()) X if (cache_xrefs()) X if (chase_xrefs(TRUE)) X if (ThreadedGroup) X cache_all_arts(); X else X cache_unread_arts(); X } else { X if (!ThreadedGroup || cache_all_arts()) X if (cache_subjects()) X if (cache_unread_arts()) X if (cache_xrefs()) X chase_xrefs(TRUE); X } X X setspin(SPIN_OFF); X untrim_cache = FALSE; X#endif X} X X#ifdef PENDING Xbool Xcache_subjects() X{ X register ARTICLE *ap; X register ART_NUM an; X X if (subj_to_get > lastart) X return TRUE; X setspin(SPIN_BACKGROUND); X for (an = subj_to_get, ap = article_ptr(an); an <= lastart; ap++, an++) { X if (input_pending()) X break; X if (!(ap->flags & AF_READ)) X fetchsubj(an,FALSE); X } X subj_to_get = an; X return subj_to_get > lastart; X} X Xbool Xcache_xrefs() X{ X register ARTICLE *ap; X register ART_NUM an; X X if (olden_days || xref_to_get > lastart) X return TRUE; X setspin(SPIN_BACKGROUND); X for (an = xref_to_get, ap = article_ptr(an); an <= lastart; ap++, an++) { X if (input_pending()) X break; X if (!(ap->flags & AF_READ)) X fetchxref(an,FALSE); X } X xref_to_get = an; X return xref_to_get > lastart; X} X Xbool Xcache_all_arts() X{ X int old_last_cached = last_cached; X if (!cached_all_in_range) X last_cached = first_cached-1; X if (last_cached >= lastart && first_cached <= absfirst) X return TRUE; X X /* turn it on as late as possible to avoid fseek()ing openart */ X setspin(SPIN_BACKGROUND); X if (last_cached < lastart) { X if (ov_opened) X ov_data(last_cached+1, lastart, TRUE); X if (!art_data(last_cached+1, lastart, TRUE, TRUE)) { X last_cached = old_last_cached; X return FALSE; X } X cached_all_in_range = TRUE; X } X if (first_cached > absfirst) { X if (ov_opened) X ov_data(absfirst, first_cached-1, TRUE); X else X art_data(absfirst, first_cached-1, TRUE, TRUE); X /* If we got interrupted, make a quick exit */ X if (first_cached > absfirst) { X last_cached = old_last_cached; X return FALSE; X } X } X /* We're all done threading the group, so if the current article is X ** still in doubt, tell them it's missing. */ X if (curr_artp && !(curr_artp->flags & AF_CACHED) && !input_pending()) X pushchar('\f' | 0200); X /* A completely empty group needs a count & a sort */ X if (mode != 't' && !article_count && !selected_only) X thread_grow(); X return TRUE; X} X Xbool Xcache_unread_arts() X{ X if (last_cached >= lastart) X return TRUE; X setspin(SPIN_BACKGROUND); X return art_data(last_cached+1, lastart, TRUE, FALSE); X} X#endif X Xbool Xart_data(first, last, cheating, all_articles) XART_NUM first, last; Xbool_int cheating; Xbool_int all_articles; X{ X register ARTICLE *ap; X register ART_NUM i; X int cachemask = (ThreadedGroup ? AF_THREADED : AF_CACHED) X + (all_articles? 0 : AF_READ); X X setspin(cheating? SPIN_BACKGROUND : SPIN_FOREGROUND); X assert(first >= absfirst && last <= lastart); X for (i = first, ap = article_ptr(first); i <= last; i++, ap++) { X if (ap->flags & cachemask) X continue; X if (int_count) { X int_count = 0; X break; X } X if (cheating) { X if (input_pending()) X break; X /* If the current article is no longer a '?', let them know. */ X if (curr_artp != sentinel_artp) { X pushchar('\f' | 0200); X break; X } X } X /* This parses the header which will cache/thread the article */ X (void) parseheader(i); X } X setspin(SPIN_POP); X if (--i > last_cached) X last_cached = i; X if (i == last) { X if (first < first_cached) X first_cached = first; X return TRUE; X } X return FALSE; X} X Xbool Xcache_range(first,last) XART_NUM first; XART_NUM last; X{ X bool success = TRUE; X bool all_arts = (sel_rereading || thread_always); X ART_NUM count = 0; X X if (sel_rereading && !cached_all_in_range) { X first_cached = first; X last_cached = first-1; X } X if (first < first_cached) X count = first_cached-first; X if (last > last_cached) X count += last-last_cached; X if (!count) X return TRUE; X X if (first_cached > last_cached) { X if (sel_rereading) { X if (first_subject) X count -= toread[ng]; X } else if (first == firstart && last == lastart && !all_arts) X count = toread[ng]; X } X X printf("\n%sing %ld article%s.", ThreadedGroup? "Thread" : "Cach", X (long)count, count==1? nullstr : "s") FLUSH; X X setspin(SPIN_FOREGROUND); X X if (first < first_cached) { X if (ov_opened) { X ov_data(absfirst,first_cached-1,FALSE); X if ((success = (first_cached == absfirst)) != FALSE) X ov_close(); X } else { X success = art_data(first, first_cached-1, FALSE, all_arts); X cached_all_in_range = (all_arts && success); X } X } X if (success && last_cached < last) { X if (ov_opened) X ov_data(last_cached+1, last, FALSE); X success = art_data(last_cached+1, last, FALSE, all_arts); X cached_all_in_range = (all_arts && success); X } X setspin(SPIN_POP); X return success; X} X Xvoid Xclear_article(ap) Xregister ARTICLE *ap; X{ X if (ap->from) X free(ap->from); X if (ap->msgid) X free(ap->msgid); X if (ap->xrefs && ap->xrefs != nullstr) X free(ap->xrefs); X} SHAR_EOF $shar_touch -am 1110023094 'trn-3.6/cache.c' && chmod 0644 'trn-3.6/cache.c' || echo 'restore of trn-3.6/cache.c failed' shar_count="`wc -c < 'trn-3.6/cache.c'`" test 20441 -eq "$shar_count" || echo "trn-3.6/cache.c: original size 20441, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/only.c ============== if test -f 'trn-3.6/only.c' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/only.c (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/only.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/only.c' && X/* $Id: only.c,v 3.0 1991/09/09 20:23:31 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#include "EXTERN.h" X#include "common.h" X#include "search.h" X#include "util.h" X#include "final.h" X#include "ngsrch.h" X#include "INTERN.h" X#include "only.h" X Xvoid Xonly_init() X{ X ; X} X Xvoid Xsetngtodo(pat) Xchar *pat; X{ X#ifdef ONLY X#ifdef SPEED_OVER_MEM X char *s; X#endif X X if (!*pat) X return; X if (maxngtodo < NGMAX) { X ngtodo[maxngtodo] = savestr(pat); X#ifdef SPEED_OVER_MEM X#ifndef lint X compextodo[maxngtodo] = (COMPEX*)safemalloc(sizeof(COMPEX)); X#endif /* lint */ X init_compex(compextodo[maxngtodo]); X compile(compextodo[maxngtodo],pat,TRUE,TRUE); X if ((s = ng_comp(compextodo[maxngtodo],pat,TRUE,TRUE)) != Nullch) { X /* compile regular expression */ X printf("\n%s\n",s) FLUSH; X finalize(1); X } X#endif X maxngtodo++; X } X#else X notincl("o"); X#endif X} X X/* if command line list is non-null, is this newsgroup wanted? */ X Xbool Xinlist(ngnam) Xchar *ngnam; X{ X#ifdef ONLY X register int i; X#ifdef SPEED_OVER_MEM X X if (maxngtodo == 0) X return TRUE; X for (i=0; i 1 ? ", etc." : nullstr) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("\nExiting \"only\".\n",stdout) FLUSH; X#endif X for (whicharg = 0; whicharg < maxngtodo; whicharg++) { X free(ngtodo[whicharg]); X#ifdef SPEED_OVER_MEM X free_compex(compextodo[whicharg]); X#ifndef lint X free((char*)compextodo[whicharg]); X#endif /* lint */ X#endif X } X maxngtodo = 0; X emptyOnly = FALSE; X } X} X#endif SHAR_EOF $shar_touch -am 0711223693 'trn-3.6/only.c' && chmod 0644 'trn-3.6/only.c' || echo 'restore of trn-3.6/only.c failed' shar_count="`wc -c < 'trn-3.6/only.c'`" test 2817 -eq "$shar_count" || echo "trn-3.6/only.c: original size 2817, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/only.h ============== if test -f 'trn-3.6/only.h' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/only.h (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/only.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/only.h' && X/* $Id: only.h,v 3.0 1991/09/09 20:23:31 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#ifndef NBRA X#include "search.h" X#endif X X#ifdef ONLY X EXT char *ngtodo[NGMAX]; /* restrictions in effect */ X# ifdef SPEED_OVER_MEM X EXT COMPEX *compextodo[NGMAX]; /* restrictions in compiled form */ X# endif X#endif X XEXT int maxngtodo INIT(0); /* 0 => no restrictions */ X /* >0 => # of entries in ngtodo */ XEXT bool emptyOnly INIT(FALSE); X Xvoid only_init _((void)); Xbool inlist _((char*)); /* return TRUE if ngname is in command line list */ X /* or if there was no list */ Xvoid setngtodo _((char*)); X#ifdef ONLY Xvoid end_only _((void)); X#endif SHAR_EOF $shar_touch -am 0711223693 'trn-3.6/only.h' && chmod 0644 'trn-3.6/only.h' || echo 'restore of trn-3.6/only.h failed' shar_count="`wc -c < 'trn-3.6/only.h'`" test 1208 -eq "$shar_count" || echo "trn-3.6/only.h: original size 1208, current size $shar_count" rm -f _sharnew.tmp fi # ============= trn-3.6/rcln.c ============== if test -f 'trn-3.6/rcln.c' && test X"$1" != X"-c"; then echo 'x - skipping trn-3.6/rcln.c (file already exists)' rm -f _sharnew.tmp else > _sharnew.tmp echo 'x - extracting trn-3.6/rcln.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rcln.c' && X/* $Id: rcln.c,v 3.0 1992/02/01 03:09:32 davison Trn $ X */ X/* This software is Copyright 1991 by Stan Barber. X * X * Permission is hereby granted to copy, reproduce, redistribute or otherwise X * use this software as long as: there is no monetary profit gained X * specifically from the use or reproduction of this software, it is not X * sold, rented, traded or otherwise marketed, and this copyright notice is X * included prominently in any copy made. X * X * The authors make no claims as to the fitness or correctness of this software X * for any use whatsoever, and it is provided as is. Any use of this software X * is at the user's own risk. X */ X X#include "EXTERN.h" X#include "common.h" X#include "util.h" X#include "util2.h" X#include "rcstuff.h" X#include "ngdata.h" X#include "INTERN.h" X#include "rcln.h" X Xvoid Xrcln_init() X{ X ; X} X X#ifdef CATCHUP Xvoid Xcatch_up(ngx, leave_count) XNG_NUM ngx; Xint leave_count; X{ X char tmpbuf[128]; X X if (leave_count) { X#ifdef VERBOSE X IF(verbose) X printf("\nMarking all but %d articles in %s as read.\n", X leave_count,rcline[ngx]) FLUSH; X ELSE X#endif X#ifdef TERSE X printf("\nAll but %d marked as read.\n",leave_count) FLUSH; X#endif X checkexpired(ngx, getngsize(ngx) - leave_count + 1); X } X else { X#ifdef VERBOSE X IF(verbose) X printf("\nMarking %s as all read.\n",rcline[ngx]) FLUSH; X ELSE X#endif X#ifdef TERSE X fputs("\nMarked read\n",stdout) FLUSH; X#endif X sprintf(tmpbuf,"%s: 1-%ld", rcline[ngx],(long)getngsize(ngx)); X free(rcline[ngx]); X rcline[ngx] = savestr(tmpbuf); X *(rcline[ngx] + rcnums[ngx] - 1) = '\0'; X toread[ngx] = TR_NONE; X } X write_rc(); X} X#endif X X/* add an article number to a newsgroup, if it isn't already read */ X Xint Xaddartnum(artnum,ngnam) XART_NUM artnum; Xchar *ngnam; X{ X register NG_NUM ngnum = find_ng(ngnam); X register char *s, *t, *maxt = Nullch; X ART_NUM min = 0, max = -1, lastnum = 0; X char *mbuf; X bool morenum; X X if (!artnum) X return 0; X if (ngnum == nextrcline || !rcnums[ngnum]) X /* not found in newsrc? */ X return 0; X if (!abs1st[ngnum]) X#ifndef ANCIENT_NEWS X /* now is a good time to trim down */ X set_toread(ngnum); /* the list due to expires if we */ X /* have not yet. */ X#endif X#ifdef DEBUG X if (artnum > ngmax[ngnum] + 100) { /* allow for incoming articles */ X printf("\nCorrupt Xref line!!! %ld --> %s(1..%ld)\n", X artnum,ngnam, X ngmax[ngnum]) FLUSH; X paranoid = TRUE; /* paranoia reigns supreme */ X return -1; /* hope this was the first newsgroup */ X } X#endif X X if (toread[ngnum] == TR_BOGUS) X return 0; X#ifdef DEBUG X if (debug & DEB_XREF_MARKER) { X printf("%ld->\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum], X rcline[ngnum] + rcnums[ngnum]) FLUSH; X } X#endif X s = rcline[ngnum] + rcnums[ngnum]; X while (*s == ' ') s++; /* skip spaces */ X t = s; X while (isdigit(*s) && artnum >= (min = atol(s))) { X /* while it might have been read */ X for (t = s; isdigit(*t); t++) ; /* skip number */ X if (*t == '-') { /* is it a range? */ X t++; /* skip to next number */ X if (artnum <= (max = atol(t))) X return 0; /* it is in range => already read */ X lastnum = max; /* remember it */ X maxt = t; /* remember position in case we */ X /* want to overwrite the max */ X while (isdigit(*t)) t++; /* skip second number */ X } X else { X if (artnum == min) /* explicitly a read article? */ X return 0; X lastnum = min; /* remember what the number was */ X maxt = Nullch; /* last one was not a range */ X } X while (*t && !isdigit(*t)) t++; /* skip comma and any spaces */ X s = t; X } X X /* we have not read it, so insert the article number before s */ X X morenum = isdigit(*s); /* will it need a comma after? */ X *(rcline[ngnum] + rcnums[ngnum] - 1) = RCCHAR(rcchar[ngnum]); X mbuf = safemalloc((MEM_SIZE)(strlen(s) + (s-rcline[ngnum]) + 6+2+1)); X strcpy(mbuf,rcline[ngnum]); /* make new rc line */ X if (maxt && lastnum && artnum == lastnum+1) X /* can we just extend last range? */ X t = mbuf + (maxt-rcline[ngnum]);/* then overwrite previous max */ X else { X t = mbuf + (t-rcline[ngnum]); /* point t into new line instead */ X if (lastnum) { /* have we parsed any line? */ X if (!morenum) /* are we adding to the tail? */ X *t++ = ','; /* supply comma before */ X if (!maxt && artnum == lastnum+1 && *(t-1) == ',') X /* adjacent singletons? */ X *(t-1) = '-'; /* turn them into a range */ X } X } X if (morenum) { /* is there more to life? */ X if (min == artnum+1) { /* can we consolidate further? */ X bool range_before = (*(t-1) == '-'); X bool range_after; X char *nextmax; X X for (nextmax = s; isdigit(*nextmax); nextmax++) ; X range_after = *nextmax++ == '-'; X X if (range_before) X *t = '\0'; /* artnum is redundant */ X else X sprintf(t,"%ld-",(long)artnum);/* artnum will be new min */ X X if (range_after) X s = nextmax; /* *s is redundant */ X /* else X s = s */ /* *s is new max */ X } X else X sprintf(t,"%ld,",(long)artnum); /* put the number and comma */ X } X else X sprintf(t,"%ld",(long)artnum); /* put the number there (wherever) */ X strcat(t,s); /* copy remainder of line */ X#ifdef DEBUG X if (debug & DEB_XREF_MARKER) { X printf("%s\n",mbuf) FLUSH; X } X#endif X free(rcline[ngnum]); X rcline[ngnum] = mbuf; /* pull the switcheroo */ X *(rcline[ngnum] + rcnums[ngnum] - 1) = '\0'; X /* wipe out : or ! */ X if (toread[ngnum] > TR_NONE) /* lest we turn unsub into bogus */ X --toread[ngnum]; X return 0; X} X X#ifdef MCHASE X/* delete an article number from a newsgroup, if it is there */ X Xvoid Xsubartnum(artnum,ngnam) Xregister ART_NUM artnum; Xchar *ngnam; X{ X register NG_NUM ngnum = find_ng(ngnam); X register char *s, *t; X register ART_NUM min, max; X char *mbuf; X int curlen; X X if (!artnum) X return; X if (ngnum == nextrcline || !rcnums[ngnum]) X return; /* not found in newsrc? */ X#ifdef DEBUG X if (debug & DEB_XREF_MARKER) { X printf("%ld<-\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum], X rcline[ngnum] + rcnums[ngnum]) FLUSH; X } X#endif X s = rcline[ngnum] + rcnums[ngnum]; X while (*s == ' ') s++; /* skip spaces */ X X /* a little optimization, since it is almost always the last number */ X X for (t=s; *t; t++) ; /* find end of string */ X curlen = t-rcline[ngnum]; X for (t--; isdigit(*t); t--) ; /* find previous delim */ X if (*t == ',' && atol(t+1) == artnum) { X *t = '\0'; X if (toread[ngnum] >= TR_NONE) X ++toread[ngnum]; X#ifdef DEBUG X if (debug & DEB_XREF_MARKER) X printf("%s%c %s\n",rcline[ngnum],rcchar[ngnum],s) FLUSH; X#endif X return; X } X X /* not the last number, oh well, we may need the length anyway */ X X while (isdigit(*s) && artnum >= (min = atol(s))) { X /* while it might have been read */ X for (t = s; isdigit(*t); t++) ; /* skip number */ X if (*t == '-') { /* is it a range? */ X t++; /* skip to next number */ X max = atol(t); X while (isdigit(*t)) t++; /* skip second number */ X if (artnum <= max) { X /* it is in range => already read */ X if (artnum == min) { X min++; X artnum = 0; X } X else if (artnum == max) { X max--; X artnum = 0; X } X *(rcline[ngnum] + rcnums[ngnum] - 1) = RCCHAR(rcchar[ngnum]); X mbuf = safemalloc((MEM_SIZE)(curlen + (artnum?(6+1)*2+1:1+1))); X *s = '\0'; X strcpy(mbuf,rcline[ngnum]); /* make new rc line */ X s = mbuf + (s-rcline[ngnum]); X /* point s into mbuf now */ X if (artnum) { /* split into two ranges? */ X prange(s,min,artnum-1); X s += strlen(s); X *s++ = ','; X prange(s,artnum+1,max); X } X else /* only one range */ X prange(s,min,max); X strcat(s,t); /* copy remainder over */ X#ifdef DEBUG X if (debug & DEB_XREF_MARKER) { X printf("%s\n",mbuf) FLUSH; X } X#endif X free(rcline[ngnum]); X rcline[ngnum] = mbuf; /* pull the switcheroo */ X *(rcline[ngnum] + rcnums[ngnum] - 1) = '\0'; X /* wipe out : or ! */ X if (toread[ngnum] >= TR_NONE) X ++toread[ngnum]; X return; X } X } X else { X if (artnum == min) { /* explicitly a read article? */ X if (*t == ',') /* pick a comma, any comma */ X t++; X else if (s[-1] == ',') X s--; X else if (s[-2] == ',') /* (in case of space) */ X s -= 2; X strcpy(s,t); /* no need to realloc */ X if (toread[ngnum] >= TR_NONE) X ++toread[ngnum]; X#ifdef DEBUG X if (debug & DEB_XREF_MARKER) { X printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum], X rcline[ngnum] + rcnums[ngnum]) FLUSH; X } X#endif X return; X } X } X while (*t && !isdigit(*t)) t++; /* skip comma and any spaces */ X s = t; X } X} X Xvoid Xprange(where,min,max) Xchar *where; XART_NUM min,max; X{ X if (min == max) X sprintf(where,"%ld",(long)min); X else X sprintf(where,"%ld-%ld",(long)min,(long)max); X} X#endif X X/* calculate the number of unread articles for a newsgroup */ X Xvoid Xset_toread(ngnum) Xregister NG_NUM ngnum; X{ X register char *s, *c, *h; X char tmpbuf[64], *mybuf = tmpbuf; X char *nums; X int length; X bool virgin_ng = (!abs1st[ngnum]); X ART_NUM ngsize = getngsize(ngnum); X ART_NUM unread = ngsize; X ART_NUM newmax; X X if (ngsize == TR_BOGUS) { X printf("\nWarning! Bogus newsgroup: %s\n",rcline[ngnum]) FLUSH; X paranoid = TRUE; X toread[ngnum] = TR_BOGUS; X return; X } X if (virgin_ng) { X sprintf(tmpbuf," 1-%ld",(long)ngsize); X if (strNE(tmpbuf,rcline[ngnum]+rcnums[ngnum])) X checkexpired(ngnum,abs1st[ngnum]); /* this might realloc rcline */ X } X nums = rcline[ngnum]+rcnums[ngnum]; X length = strlen(nums); X if (length+5+1 > sizeof tmpbuf) X mybuf = safemalloc((MEM_SIZE)(length+5+1)); X strcpy(mybuf,nums); X mybuf[length++] = ','; X mybuf[length] = '\0'; X for (s = mybuf; isspace(*s); s++) X ; X for ( ; (c = index(s,',')) != Nullch ; s = ++c) { X /* for each range */ X *c = '\0'; /* keep index from running off */ X if ((h = index(s,'-')) != Nullch) /* find - in range, if any */ X unread -= (newmax = atol(h+1)) - atol(s) + 1; X else if (newmax = atol(s)) X unread--; /* recalculate length */ X if (newmax > ngsize) { /* paranoia check */ X if (newmax > ngsize + 100) { X unread = -1; X break; X } else { X unread += newmax - ngsize; X ngmax[ngnum] = ngsize = newmax; X } X } X } X if (unread >= 0) /* reasonable number? */ X toread[ngnum] = (ART_UNREAD)unread; X /* remember how many are left */ X else { /* SOMEONE RESET THE NEWSGROUP!!! */ X toread[ngnum] = (ART_UNREAD)ngsize; X /* assume nothing carried over */ X printf("\nWarning! Somebody reset %s--assuming nothing read.\n", X rcline[ngnum]) FLUSH; X *(rcline[ngnum] + rcnums[ngnum]) = '\0'; X paranoid = TRUE; /* enough to make a guy paranoid */ X } X if (mybuf != tmpbuf) X free(mybuf); X if (rcchar[ngnum] == NEGCHAR) X toread[ngnum] = TR_UNSUB; X} X X/* make sure expired articles are marked as read */ X Xvoid Xcheckexpired(ngnum,a1st) Xregister NG_NUM ngnum; Xregister ART_NUM a1st; X{ X register char *s; X register ART_NUM num, lastnum = 0; X char *mbuf, *newnum; X X if (a1st<=1) X return; X#ifdef DEBUG X if (debug & DEB_XREF_MARKER) { X printf("1-%ld->\n%s%c%s\n",(long)(a1st-1),rcline[ngnum],rcchar[ngnum], X rcline[ngnum] + rcnums[ngnum]) FLUSH; X } X#endif X for (s = rcline[ngnum] + rcnums[ngnum]; isspace(*s); s++); X while (*s && (num = atol(s)) <= a1st) { X while (isdigit(*s)) s++; X while (*s && !isdigit(*s)) s++; X lastnum = num; X } X if (*s) { X if (s[-1] == '-') { /* landed in a range? */ X if (lastnum != 1) { X if (3 + strlen(s) > strlen(rcline[ngnum]+rcnums[ngnum])) { X mbuf = safemalloc((MEM_SIZE)(rcnums[ngnum] + 3 + X strlen(s) + 1)); X strcpy(mbuf, rcline[ngnum]); X sprintf(mbuf+rcnums[ngnum]," 1-%s",s); X free(rcline[ngnum]); X rcline[ngnum] = mbuf; X } else { X sprintf(rcline[ngnum]+rcnums[ngnum]," 1-%s",s); X } X } X goto ret; X } X } X /* s now points to what should follow first range */ X if (s - rcline[ngnum] > rcnums[ngnum] + 6+4+1) X mbuf = rcline[ngnum]; X else { X mbuf = safemalloc((MEM_SIZE)(rcnums[ngnum] + strlen(s) + 6+4+1)); X strcpy(mbuf,rcline[ngnum]); SHAR_EOF : || echo 'restore of trn-3.6/rcln.c failed' fi echo 'End of archive part 6' echo 'File trn-3.6/rcln.c is continued in part 7' echo 7 > _sharseq.tmp exit 0