trn-3.6/0000775000000000000000000000000012547226367007073 5ustar trn-3.6/sw.c0000664000000000000000000003623205663312266007671 0ustar /* $Id: sw.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "util2.h" #include "cache.h" #include "head.h" #include "only.h" #include "term.h" #include "trn.h" #include "ng.h" #include "ngdata.h" #include "artstate.h" #include "intrp.h" #include "rt-page.h" #include "rt-util.h" #include "charsubst.h" #include "INTERN.h" #include "sw.h" void sw_init(argc,argv,tcbufptr) int argc; char *argv[]; char **tcbufptr; { register int i; if (argc >= 2 && strEQ(argv[1],"-c")) checkflag=TRUE; /* so we can optimize for -c */ interp(*tcbufptr,1024,GLOBINIT); sw_file(tcbufptr,FALSE); if (!use_threads || !*safecpy(*tcbufptr,getenv("TRNINIT"),1024)) safecpy(*tcbufptr,getenv("RNINIT"),1024); if (**tcbufptr) { if (**tcbufptr == '/') { sw_file(tcbufptr,TRUE); } else sw_list(*tcbufptr); } for (i = 1; i < argc; i++) decode_switch(argv[i]); } void sw_file(tcbufptr,bleat) char **tcbufptr; bool_int bleat; { int initfd = open(*tcbufptr,0); if (initfd >= 0) { fstat(initfd,&filestat); if (filestat.st_size > 1024) *tcbufptr = saferealloc(*tcbufptr,(MEM_SIZE)filestat.st_size); if (filestat.st_size) { read(initfd,*tcbufptr,(int)filestat.st_size); (*tcbufptr)[filestat.st_size-1] = '\0'; /* wipe out last newline */ sw_list(*tcbufptr); } else **tcbufptr = '\0'; close(initfd); } else { if (bleat) printf(cantopen,*tcbufptr) FLUSH; **tcbufptr = '\0'; } } /* decode a list of space separated switches */ void sw_list(swlist) char *swlist; { char *tmplist = safemalloc((MEM_SIZE) strlen(swlist) + 2); /* semi-automatic string */ register char *s, *p, inquote = 0; strcpy(tmplist,swlist); p = tmplist; for (s = p;;) { while (isspace(*s)) s++; /* skip any initial spaces */ if (*s != '#') { if (s != p) strcpy(p, s); break; } while (*s && *s++ != '\n') ; /* skip comments */ } while (*p) { /* "String, or nothing" */ if (!inquote && isspace(*p)) { /* word delimiter? */ *p++ = '\0'; /* chop here */ for (s = p;;) { while (isspace(*s)) s++; if (*s != '#') { if (s != p) strcpy(p, s); break; } while (*s && *s++ != '\n') ; } } else if (inquote == *p) { strcpy(p,p+1); /* delete trailing quote */ inquote = 0; /* no longer quoting */ } else if (!inquote && (*p == '"' || *p == '\'')) { /* OK, I know when I am not wanted */ inquote = *p; /* remember single or double */ strcpy(p,p+1); /* delete the quote */ } /* (crude, but effective) */ else if (*p == '\\') { /* quoted something? */ if (p[1] == '\n') /* newline? */ strcpy(p,p+2); /* "I didn't see anything" */ else { strcpy(p,p+1); /* delete the backwhack */ p++; /* leave the whatever alone */ } } else p++; /* normal char, leave it alone */ } *++p = '\0'; /* put an extra null on the end */ if (inquote) printf("Unmatched %c in switch\n",inquote) FLUSH; for (p = tmplist; *p; /* p += strlen(p)+1 */ ) { decode_switch(p); while (*p++) ; /* point at null + 1 */ } free(tmplist); /* this oughta be in Ada */ } /* decode a single switch */ void decode_switch(s) register char *s; { while (isspace(*s)) /* ignore leading spaces */ s++; #ifdef DEBUG if (debug) printf("Switch: %s\n",s) FLUSH; #endif if (*s != '-' && *s != '+') { /* newsgroup pattern */ setngtodo(s); emptyOnly = TRUE; } else { /* normal switch */ bool upordown = *s == '-' ? TRUE : FALSE; char tmpbuf[LBUFLEN]; s++; switch (*s) { #ifdef TERMMOD case '=': { char *beg = s+1; while (*s && *s != '-' && *s != '+') s++; cpytill(tmpbuf,beg,*s); if (upordown ? strEQ(getenv("TERM"),tmpbuf) : strNE(getenv("TERM"),tmpbuf) ) { decode_switch(s); } break; } #endif #ifdef BAUDMOD case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (upordown ? (just_a_sec*10 <= atoi(s)) : (just_a_sec*10 >= atoi(s)) ) { while (isdigit(*s)) s++; decode_switch(s); } break; #endif case '/': if (checkflag) break; #ifdef SETENV export("SAVEDIR", upordown ? "%p/%c" : "%p" ); export("SAVENAME", upordown ? "%a" : "%^C"); #else notincl("-/"); #endif break; case 'a': thread_always = upordown; break; case 'A': auto_arrow_macros = upordown; break; case 'b': breadth_first = upordown; break; case 'B': bkgnd_spinner = upordown; break; case 'c': checkflag = upordown; break; case 'C': s++; if (*s == '=') s++; docheckwhen = atoi(s); break; case 'd': { if (checkflag) break; s++; if (*s == '=') s++; if (cwd) { chdir(cwd); free(cwd); } cwd = savestr(s); break; } #ifdef DEBUG case 'D': s++; if (*s == '=') s++; if (*s) if (upordown) debug |= atoi(s); else debug &= ~atoi(s); else if (upordown) debug |= 1; else debug = 0; break; #endif case 'e': erase_screen = upordown; break; case 'E': #ifdef SETENV s++; if (*s == '=') s++; strcpy(tmpbuf,s); s = index(tmpbuf,'='); if (s) { *s++ = '\0'; export(tmpbuf,s); } else export(tmpbuf,nullstr); #else notincl("-E"); #endif break; case 'f': novice_delays = !upordown; break; case 'F': s++; indstr = savestr(s); break; #ifdef INNERSEARCH case 'g': gline = atoi(s+1)-1; break; #endif #ifdef EDIT_DISTANCE case 'G': fuzzyGet = upordown && (actfp || mode == 'i'); break; #endif case 'H': case 'h': { register int len, i; char *t; int flag = (*s == 'h' ? HT_HIDE : HT_MAGIC); if (checkflag) break; s++; len = strlen(s); for (t=s; *t; t++) if (isupper(*t)) *t = tolower(*t); for (i=HEAD_FIRST; i 0 || try_ov > 0) { printf("-Z"); if (try_ov > 0) putchar('o'); if (try_mt > 0) putchar('t'); putchar(' '); } else printf("+Z "); fputs("\n\n",stdout) FLUSH; #ifdef ONLY if (maxngtodo) { #ifdef VERBOSE IF(verbose) fputs("Current restriction:",stdout); ELSE #endif #ifdef TERSE fputs("Only:",stdout); #endif for (i=0; i trn.fuzz 2>&1 sabertrn: $(c) parsedate.c #load $(c) parsedate.c $(libs) depend: config.h Makefile makedepend ./makedepend # AUTOMATICALLY GENERATED MAKE DEPENDENCIES--PUT NOTHING BELOW THIS LINE addng.o: EXTERN.h addng.o: INTERN.h addng.o: addng.c addng.o: addng.h addng.o: common.h addng.o: config.h addng.o: final.h addng.o: intrp.h addng.o: last.h addng.o: ngdata.h addng.o: nntp.h addng.o: nntpclient.h addng.o: only.h addng.o: rcstuff.h addng.o: search.h addng.o: trn.h addng.o: util.h art.o: EXTERN.h art.o: INTERN.h art.o: art.c art.o: art.h art.o: artio.h art.o: artstate.h art.o: backpage.h art.o: bits.h art.o: cache.h art.o: charsubst.h art.o: common.h art.o: config.h art.o: final.h art.o: head.h art.o: help.h art.o: intrp.h art.o: ng.h art.o: ngdata.h art.o: ngstuff.h art.o: nntp.h art.o: nntpclient.h art.o: rcstuff.h art.o: rt-select.h art.o: rt-util.h art.o: rt-wumpus.h art.o: rthread.h art.o: search.h art.o: sw.h art.o: term.h art.o: trn.h art.o: util.h art.o: util2.h artio.o: EXTERN.h artio.o: INTERN.h artio.o: art.h artio.o: artio.c artio.o: artio.h artio.o: bits.h artio.o: cache.h artio.o: common.h artio.o: config.h artio.o: final.h artio.o: head.h artio.o: ngdata.h artio.o: nntp.h artio.o: nntpclient.h artio.o: rthread.h artsrch.o: EXTERN.h artsrch.o: INTERN.h artsrch.o: artio.h artsrch.o: artsrch.c artsrch.o: artsrch.h artsrch.o: bits.h artsrch.o: cache.h artsrch.o: common.h artsrch.o: config.h artsrch.o: final.h artsrch.o: head.h artsrch.o: intrp.h artsrch.o: kfile.h artsrch.o: ng.h artsrch.o: ngdata.h artsrch.o: ngstuff.h artsrch.o: nntp.h artsrch.o: nntpclient.h artsrch.o: rt-select.h artsrch.o: rthread.h artsrch.o: search.h artsrch.o: term.h artsrch.o: util.h autosub.o: EXTERN.h autosub.o: INTERN.h autosub.o: autosub.c autosub.o: autosub.h autosub.o: common.h autosub.o: config.h autosub.o: final.h autosub.o: ngsrch.h autosub.o: search.h autosub.o: util.h backpage.o: EXTERN.h backpage.o: INTERN.h backpage.o: backpage.c backpage.o: backpage.h backpage.o: common.h backpage.o: config.h backpage.o: final.h backpage.o: intrp.h bits.o: EXTERN.h bits.o: INTERN.h bits.o: artio.h bits.o: bits.c bits.o: bits.h bits.o: cache.h bits.o: common.h bits.o: config.h bits.o: final.h bits.o: head.h bits.o: intrp.h bits.o: ndir.h bits.o: ng.h bits.o: ngdata.h bits.o: nntp.h bits.o: nntpclient.h bits.o: rcln.h bits.o: rcstuff.h bits.o: rt-select.h bits.o: rt-util.h bits.o: rthread.h bits.o: term.h bits.o: trn.h bits.o: util.h bits.o: util2.h cache.o: EXTERN.h cache.o: INTERN.h cache.o: artsrch.h cache.o: bits.h cache.o: cache.c cache.o: cache.h cache.o: common.h cache.o: config.h cache.o: final.h cache.o: hash.h cache.o: head.h cache.o: intrp.h cache.o: ng.h cache.o: ngdata.h cache.o: nntp.h cache.o: nntpclient.h cache.o: rcstuff.h cache.o: rt-ov.h cache.o: rt-page.h cache.o: rt-process.h cache.o: rt-select.h cache.o: rt-util.h cache.o: rthread.h cache.o: search.h cache.o: term.h cache.o: trn.h cache.o: util.h cache.o: util2.h charsubst.o: EXTERN.h charsubst.o: INTERN.h charsubst.o: artstate.h charsubst.o: charsubst.c charsubst.o: charsubst.h charsubst.o: common.h charsubst.o: config.h charsubst.o: search.h decode.o: EXTERN.h decode.o: INTERN.h decode.o: common.h decode.o: config.h decode.o: decode.c decode.o: decode.h edit_dist.o: EXTERN.h edit_dist.o: common.h edit_dist.o: config.h edit_dist.o: edit_dist.c edit_dist.o: util.h final.o: EXTERN.h final.o: INTERN.h final.o: artio.h final.o: bits.h final.o: cache.h final.o: common.h final.o: config.h final.o: final.c final.o: final.h final.o: init.h final.o: intrp.h final.o: last.h final.o: ng.h final.o: ngdata.h final.o: nntp.h final.o: nntpclient.h final.o: rcstuff.h final.o: term.h final.o: util.h hash.o: EXTERN.h hash.o: INTERN.h hash.o: common.h hash.o: config.h hash.o: final.h hash.o: hash.c hash.o: hash.h hash.o: util.h head.o: EXTERN.h head.o: INTERN.h head.o: artio.h head.o: cache.h head.o: common.h head.o: config.h head.o: final.h head.o: hash.h head.o: head.c head.o: head.h head.o: ng.h head.o: ngdata.h head.o: nntp.h head.o: nntpclient.h head.o: rt-process.h head.o: rt-util.h head.o: rthread.h head.o: util.h help.o: EXTERN.h help.o: INTERN.h help.o: common.h help.o: config.h help.o: help.c help.o: help.h help.o: term.h help.o: trn.h init.o: EXTERN.h init.o: INTERN.h init.o: addng.h init.o: art.h init.o: artio.h init.o: artsrch.h init.o: backpage.h init.o: bits.h init.o: cache.h init.o: common.h init.o: config.h init.o: decode.h init.o: final.h init.o: head.h init.o: help.h init.o: init.c init.o: init.h init.o: intrp.h init.o: kfile.h init.o: last.h init.o: ng.h init.o: ngdata.h init.o: ngsrch.h init.o: ngstuff.h init.o: nntp.h init.o: nntpclient.h init.o: only.h init.o: rcln.h init.o: rcstuff.h init.o: respond.h init.o: rthread.h init.o: search.h init.o: sw.h init.o: term.h init.o: trn.h init.o: util.h intrp.o: EXTERN.h intrp.o: INTERN.h intrp.o: artio.h intrp.o: artsrch.h intrp.o: bits.h intrp.o: cache.h intrp.o: common.h intrp.o: config.h intrp.o: final.h intrp.o: head.h intrp.o: init.h intrp.o: intrp.c intrp.o: intrp.h intrp.o: ng.h intrp.o: ngdata.h intrp.o: nntp.h intrp.o: nntpclient.h intrp.o: rcstuff.h intrp.o: respond.h intrp.o: rt-select.h intrp.o: rt-util.h intrp.o: rthread.h intrp.o: search.h intrp.o: term.h intrp.o: trn.h intrp.o: util.h intrp.o: util2.h kfile.o: EXTERN.h kfile.o: INTERN.h kfile.o: artsrch.h kfile.o: bits.h kfile.o: cache.h kfile.o: common.h kfile.o: config.h kfile.o: hash.h kfile.o: intrp.h kfile.o: kfile.c kfile.o: kfile.h kfile.o: ng.h kfile.o: ngdata.h kfile.o: ngstuff.h kfile.o: rcstuff.h kfile.o: rt-process.h kfile.o: rt-select.h kfile.o: rthread.h kfile.o: search.h kfile.o: term.h kfile.o: trn.h kfile.o: util.h last.o: EXTERN.h last.o: INTERN.h last.o: common.h last.o: config.h last.o: intrp.h last.o: last.c last.o: last.h last.o: trn.h last.o: util.h ndir.o: EXTERN.h ndir.o: INTERN.h ndir.o: common.h ndir.o: config.h ndir.o: ndir.c ndir.o: ndir.h ng.o: EXTERN.h ng.o: INTERN.h ng.o: art.h ng.o: artio.h ng.o: artsrch.h ng.o: artstate.h ng.o: backpage.h ng.o: bits.h ng.o: cache.h ng.o: charsubst.h ng.o: common.h ng.o: config.h ng.o: decode.h ng.o: final.h ng.o: head.h ng.o: help.h ng.o: intrp.h ng.o: kfile.h ng.o: last.h ng.o: ng.c ng.o: ng.h ng.o: ngdata.h ng.o: ngstuff.h ng.o: nntp.h ng.o: nntpclient.h ng.o: rcln.h ng.o: rcstuff.h ng.o: respond.h ng.o: rt-select.h ng.o: rt-wumpus.h ng.o: rthread.h ng.o: search.h ng.o: sw.h ng.o: term.h ng.o: trn.h ng.o: util.h ngdata.o: EXTERN.h ngdata.o: INTERN.h ngdata.o: bits.h ngdata.o: cache.h ngdata.o: common.h ngdata.o: config.h ngdata.o: final.h ngdata.o: head.h ngdata.o: intrp.h ngdata.o: kfile.h ngdata.o: ndir.h ngdata.o: ng.h ngdata.o: ngdata.c ngdata.o: ngdata.h ngdata.o: nntp.h ngdata.o: nntpclient.h ngdata.o: rcln.h ngdata.o: rcstuff.h ngdata.o: rt-select.h ngdata.o: rthread.h ngdata.o: term.h ngdata.o: trn.h ngdata.o: util.h nghash.o: EXTERN.h nghash.o: common.h nghash.o: config.h nghash.o: final.h nghash.o: hash.h nghash.o: intrp.h nghash.o: ndir.h nghash.o: ngdata.h nghash.o: nghash.c nghash.o: nntp.h nghash.o: nntpclient.h nghash.o: rcln.h nghash.o: rcstuff.h nghash.o: term.h nghash.o: trn.h nghash.o: util.h ngsrch.o: EXTERN.h ngsrch.o: INTERN.h ngsrch.o: common.h ngsrch.o: config.h ngsrch.o: final.h ngsrch.o: ngsrch.c ngsrch.o: ngsrch.h ngsrch.o: rcln.h ngsrch.o: rcstuff.h ngsrch.o: search.h ngsrch.o: term.h ngsrch.o: trn.h ngsrch.o: util.h ngstuff.o: EXTERN.h ngstuff.o: INTERN.h ngstuff.o: bits.h ngstuff.o: cache.h ngstuff.o: common.h ngstuff.o: config.h ngstuff.o: decode.h ngstuff.o: final.h ngstuff.o: head.h ngstuff.o: intrp.h ngstuff.o: kfile.h ngstuff.o: ng.h ngstuff.o: ngdata.h ngstuff.o: ngstuff.c ngstuff.o: ngstuff.h ngstuff.o: rcstuff.h ngstuff.o: respond.h ngstuff.o: rt-select.h ngstuff.o: rt-wumpus.h ngstuff.o: rthread.h ngstuff.o: sw.h ngstuff.o: term.h ngstuff.o: trn.h ngstuff.o: util.h ngstuff.o: util2.h nntp.o: EXTERN.h nntp.o: artio.h nntp.o: bits.h nntp.o: cache.h nntp.o: common.h nntp.o: config.h nntp.o: final.h nntp.o: head.h nntp.o: init.h nntp.o: ngdata.h nntp.o: nntp.c nntp.o: nntp.h nntp.o: nntpclient.h nntp.o: rcln.h nntp.o: term.h nntp.o: trn.h nntp.o: util.h nntpauth.o: EXTERN.h nntpauth.o: INTERN.h nntpauth.o: common.h nntpauth.o: config.h nntpauth.o: nntp.h nntpauth.o: nntpauth.c nntpauth.o: nntpauth.h nntpauth.o: nntpclient.h nntpauth.o: util2.h nntpclient.o: EXTERN.h nntpclient.o: INTERN.h nntpclient.o: common.h nntpclient.o: config.h nntpclient.o: nntpclient.c nntpclient.o: nntpclient.h nntpinit.o: EXTERN.h nntpinit.o: common.h nntpinit.o: config.h nntpinit.o: nntpclient.h nntpinit.o: nntpinit.c nntplist.o: EXTERN.h nntplist.o: common.h nntplist.o: config.h nntplist.o: nntpclient.h nntplist.o: nntplist.c only.o: EXTERN.h only.o: INTERN.h only.o: common.h only.o: config.h only.o: final.h only.o: ngsrch.h only.o: only.c only.o: only.h only.o: search.h only.o: util.h parsedate.o: config.h parsedate.o: parsedate.y rcln.o: EXTERN.h rcln.o: INTERN.h rcln.o: common.h rcln.o: config.h rcln.o: ngdata.h rcln.o: rcln.c rcln.o: rcln.h rcln.o: rcstuff.h rcln.o: util.h rcln.o: util2.h rcstuff.o: EXTERN.h rcstuff.o: INTERN.h rcstuff.o: autosub.h rcstuff.o: bits.h rcstuff.o: cache.h rcstuff.o: common.h rcstuff.o: config.h rcstuff.o: final.h rcstuff.o: hash.h rcstuff.o: intrp.h rcstuff.o: ngdata.h rcstuff.o: nntp.h rcstuff.o: nntpclient.h rcstuff.o: only.h rcstuff.o: rcln.h rcstuff.o: rcstuff.c rcstuff.o: rcstuff.h rcstuff.o: search.h rcstuff.o: term.h rcstuff.o: trn.h rcstuff.o: util.h respond.o: EXTERN.h respond.o: INTERN.h respond.o: artio.h respond.o: cache.h respond.o: charsubst.h respond.o: common.h respond.o: config.h respond.o: decode.h respond.o: final.h respond.o: head.h respond.o: intrp.h respond.o: ng.h respond.o: ngdata.h respond.o: nntp.h respond.o: nntpclient.h respond.o: respond.c respond.o: respond.h respond.o: term.h respond.o: trn.h respond.o: util.h respond.o: util2.h rt-mt.o: EXTERN.h rt-mt.o: INTERN.h rt-mt.o: bits.h rt-mt.o: cache.h rt-mt.o: common.h rt-mt.o: config.h rt-mt.o: hash.h rt-mt.o: intrp.h rt-mt.o: ng.h rt-mt.o: ngdata.h rt-mt.o: nntp.h rt-mt.o: nntpclient.h rt-mt.o: rcln.h rt-mt.o: rt-mt.c rt-mt.o: rt-mt.h rt-mt.o: rt-process.h rt-mt.o: rthread.h rt-mt.o: trn.h rt-mt.o: util.h rt-ov.o: EXTERN.h rt-ov.o: INTERN.h rt-ov.o: bits.h rt-ov.o: cache.h rt-ov.o: common.h rt-ov.o: config.h rt-ov.o: final.h rt-ov.o: hash.h rt-ov.o: head.h rt-ov.o: ng.h rt-ov.o: ngdata.h rt-ov.o: nntp.h rt-ov.o: nntpclient.h rt-ov.o: overview.h rt-ov.o: rt-ov.c rt-ov.o: rt-ov.h rt-ov.o: rt-process.h rt-ov.o: rt-util.h rt-ov.o: rthread.h rt-ov.o: term.h rt-ov.o: trn.h rt-ov.o: util.h rt-page.o: EXTERN.h rt-page.o: INTERN.h rt-page.o: cache.h rt-page.o: common.h rt-page.o: config.h rt-page.o: ngdata.h rt-page.o: rt-page.c rt-page.o: rt-page.h rt-page.o: rt-select.h rt-page.o: rt-util.h rt-page.o: rthread.h rt-page.o: term.h rt-page.o: trn.h rt-page.o: util.h rt-process.o: EXTERN.h rt-process.o: INTERN.h rt-process.o: bits.h rt-process.o: cache.h rt-process.o: common.h rt-process.o: config.h rt-process.o: final.h rt-process.o: hash.h rt-process.o: intrp.h rt-process.o: kfile.h rt-process.o: ng.h rt-process.o: ngdata.h rt-process.o: rcln.h rt-process.o: rt-process.c rt-process.o: rt-process.h rt-process.o: rt-select.h rt-process.o: rthread.h rt-process.o: trn.h rt-process.o: util.h rt-select.o: EXTERN.h rt-select.o: INTERN.h rt-select.o: artsrch.h rt-select.o: bits.h rt-select.o: cache.h rt-select.o: common.h rt-select.o: config.h rt-select.o: final.h rt-select.o: help.h rt-select.o: kfile.h rt-select.o: ng.h rt-select.o: ngdata.h rt-select.o: ngstuff.h rt-select.o: rt-page.h rt-select.o: rt-select.c rt-select.o: rt-select.h rt-select.o: rt-util.h rt-select.o: rthread.h rt-select.o: search.h rt-select.o: term.h rt-select.o: trn.h rt-select.o: util.h rt-util.o: EXTERN.h rt-util.o: INTERN.h rt-util.o: artio.h rt-util.o: cache.h rt-util.o: common.h rt-util.o: config.h rt-util.o: ngdata.h rt-util.o: nntp.h rt-util.o: nntpclient.h rt-util.o: rt-select.h rt-util.o: rt-util.c rt-util.o: rt-util.h rt-util.o: rthread.h rt-util.o: term.h rt-wumpus.o: EXTERN.h rt-wumpus.o: INTERN.h rt-wumpus.o: artio.h rt-wumpus.o: backpage.h rt-wumpus.o: cache.h rt-wumpus.o: common.h rt-wumpus.o: config.h rt-wumpus.o: final.h rt-wumpus.o: head.h rt-wumpus.o: ng.h rt-wumpus.o: ngdata.h rt-wumpus.o: rt-select.h rt-wumpus.o: rt-wumpus.c rt-wumpus.o: rt-wumpus.h rt-wumpus.o: rthread.h rt-wumpus.o: term.h rt-wumpus.o: util.h rthread.o: EXTERN.h rthread.o: INTERN.h rthread.o: artstate.h rthread.o: bits.h rthread.o: cache.h rthread.o: common.h rthread.o: config.h rthread.o: final.h rthread.o: hash.h rthread.o: head.h rthread.o: intrp.h rthread.o: kfile.h rthread.o: ng.h rthread.o: ngdata.h rthread.o: nntp.h rthread.o: nntpclient.h rthread.o: rcln.h rthread.o: rcstuff.h rthread.o: rt-mt.h rthread.o: rt-ov.h rthread.o: rt-page.h rthread.o: rt-process.h rthread.o: rt-select.h rthread.o: rt-util.h rthread.o: rt-wumpus.h rthread.o: rthread.c rthread.o: rthread.h rthread.o: search.h rthread.o: trn.h rthread.o: util.h search.o: EXTERN.h search.o: INTERN.h search.o: common.h search.o: config.h search.o: search.c search.o: search.h search.o: util.h strftime.o: EXTERN.h strftime.o: common.h strftime.o: config.h strftime.o: strftime.c sw.o: EXTERN.h sw.o: INTERN.h sw.o: artstate.h sw.o: cache.h sw.o: charsubst.h sw.o: common.h sw.o: config.h sw.o: head.h sw.o: intrp.h sw.o: ng.h sw.o: ngdata.h sw.o: only.h sw.o: rt-page.h sw.o: rt-util.h sw.o: search.h sw.o: sw.c sw.o: sw.h sw.o: term.h sw.o: trn.h sw.o: util.h sw.o: util2.h term.o: EXTERN.h term.o: INTERN.h term.o: cache.h term.o: common.h term.o: config.h term.o: final.h term.o: help.h term.o: intrp.h term.o: term.c term.o: term.h term.o: util.h term.o: util2.h test.o: test.c trn-artchk.o: EXTERN.h trn-artchk.o: common.h trn-artchk.o: config.h trn-artchk.o: nntpclient.h trn-artchk.o: trn-artchk.c trn.o: EXTERN.h trn.o: INTERN.h trn.o: addng.h trn.o: cache.h trn.o: common.h trn.o: config.h trn.o: final.h trn.o: help.h trn.o: init.h trn.o: intrp.h trn.o: kfile.h trn.o: last.h trn.o: ng.h trn.o: ngdata.h trn.o: ngsrch.h trn.o: ngstuff.h trn.o: nntp.h trn.o: nntpclient.h trn.o: only.h trn.o: patchlevel.h trn.o: rcln.h trn.o: rcstuff.h trn.o: search.h trn.o: sw.h trn.o: term.h trn.o: trn.c trn.o: trn.h trn.o: util.h trn.o: util2.h unipatch.o: unipatch.c unship.o: EXTERN.h unship.o: common.h unship.o: config.h unship.o: decode.h unship.o: respond.h unship.o: unship.c util.o: EXTERN.h util.o: INTERN.h util.o: common.h util.o: config.h util.o: final.h util.o: nntp.h util.o: nntpauth.h util.o: nntpclient.h util.o: term.h util.o: util.c util.o: util.h util2.o: EXTERN.h util2.o: INTERN.h util2.o: common.h util2.o: config.h util2.o: nntp.h util2.o: nntpauth.h util2.o: nntpclient.h util2.o: util.h util2.o: util2.c util2.o: util2.h uudecode.o: EXTERN.h uudecode.o: common.h uudecode.o: config.h uudecode.o: decode.h uudecode.o: respond.h uudecode.o: uudecode.c Makefile: Makefile.SH config.sh ; /bin/sh $(srcdir)/Makefile.SH Pnews: Pnews.SH config.sh ; /bin/sh $(srcdir)/Pnews.SH Policy.sh: Policy.sh.SH config.sh ; /bin/sh $(srcdir)/Policy.sh.SH Rnmail: Rnmail.SH config.sh ; /bin/sh $(srcdir)/Rnmail.SH Speller: Speller.SH config.sh ; /bin/sh $(srcdir)/Speller.SH config.h: config.h.SH config.sh ; /bin/sh $(srcdir)/config.h.SH filexp: filexp.SH config.sh ; /bin/sh $(srcdir)/filexp.SH makedepend: makedepend.SH config.sh ; /bin/sh $(srcdir)/makedepend.SH makedir: makedir.SH config.sh ; /bin/sh $(srcdir)/makedir.SH mbox.saver: mbox.saver.SH config.sh ; /bin/sh $(srcdir)/mbox.saver.SH newsetup: newsetup.SH config.sh ; /bin/sh $(srcdir)/newsetup.SH newsgroups: newsgroups.SH config.sh ; /bin/sh $(srcdir)/newsgroups.SH newsnews: newsnews.SH config.sh ; /bin/sh $(srcdir)/newsnews.SH norm.saver: norm.saver.SH config.sh ; /bin/sh $(srcdir)/norm.saver.SH # WARNING: Put nothing here or make depend will gobble it up! trn-3.6/rcln.c0000664000000000000000000003065507041327652010176 0ustar /* $Id: rcln.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "util2.h" #include "rcstuff.h" #include "ngdata.h" #include "INTERN.h" #include "rcln.h" void rcln_init() { ; } #ifdef CATCHUP void catch_up(ngx, leave_count) NG_NUM ngx; int leave_count; { char tmpbuf[128]; if (leave_count) { #ifdef VERBOSE IF(verbose) printf("\nMarking all but %d articles in %s as read.\n", leave_count,rcline[ngx]) FLUSH; ELSE #endif #ifdef TERSE printf("\nAll but %d marked as read.\n",leave_count) FLUSH; #endif checkexpired(ngx, getngsize(ngx) - leave_count + 1); } else { #ifdef VERBOSE IF(verbose) printf("\nMarking %s as all read.\n",rcline[ngx]) FLUSH; ELSE #endif #ifdef TERSE fputs("\nMarked read\n",stdout) FLUSH; #endif sprintf(tmpbuf,"%s: 1-%ld", rcline[ngx],(long)getngsize(ngx)); free(rcline[ngx]); rcline[ngx] = savestr(tmpbuf); *(rcline[ngx] + rcnums[ngx] - 1) = '\0'; toread[ngx] = TR_NONE; } write_rc(); } #endif /* add an article number to a newsgroup, if it isn't already read */ int addartnum(artnum,ngnam) ART_NUM artnum; char *ngnam; { register NG_NUM ngnum = find_ng(ngnam); register char *s, *t, *maxt = Nullch; ART_NUM min = 0, max = -1, lastnum = 0; char *mbuf; bool_int morenum; if (!artnum) return 0; if (ngnum == nextrcline || !rcnums[ngnum]) /* not found in newsrc? */ return 0; if (!abs1st[ngnum]) #ifndef ANCIENT_NEWS /* now is a good time to trim down */ set_toread(ngnum); /* the list due to expires if we */ /* have not yet. */ #endif #ifdef DEBUG if (artnum > ngmax[ngnum] + 100) { /* allow for incoming articles */ printf("\nCorrupt Xref line!!! %ld --> %s(1..%ld)\n", artnum,ngnam, ngmax[ngnum]) FLUSH; paranoid = TRUE; /* paranoia reigns supreme */ return -1; /* hope this was the first newsgroup */ } #endif if (toread[ngnum] == TR_BOGUS) return 0; #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%ld->\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif s = rcline[ngnum] + rcnums[ngnum]; while (*s == ' ') s++; /* skip spaces */ t = s; while (isdigit(*s) && artnum >= (min = atol(s))) { /* while it might have been read */ for (t = s; isdigit(*t); t++) ; /* skip number */ if (*t == '-') { /* is it a range? */ t++; /* skip to next number */ if (artnum <= (max = atol(t))) return 0; /* it is in range => already read */ lastnum = max; /* remember it */ maxt = t; /* remember position in case we */ /* want to overwrite the max */ while (isdigit(*t)) t++; /* skip second number */ } else { if (artnum == min) /* explicitly a read article? */ return 0; lastnum = min; /* remember what the number was */ maxt = Nullch; /* last one was not a range */ } while (*t && !isdigit(*t)) t++; /* skip comma and any spaces */ s = t; } /* we have not read it, so insert the article number before s */ morenum = isdigit(*s); /* will it need a comma after? */ *(rcline[ngnum] + rcnums[ngnum] - 1) = RCCHAR(rcchar[ngnum]); mbuf = safemalloc((MEM_SIZE)(strlen(s) + (s-rcline[ngnum]) + 6+2+1)); strcpy(mbuf,rcline[ngnum]); /* make new rc line */ if (maxt && lastnum && artnum == lastnum+1) /* can we just extend last range? */ t = mbuf + (maxt-rcline[ngnum]);/* then overwrite previous max */ else { t = mbuf + (t-rcline[ngnum]); /* point t into new line instead */ if (lastnum) { /* have we parsed any line? */ if (!morenum) /* are we adding to the tail? */ *t++ = ','; /* supply comma before */ if (!maxt && artnum == lastnum+1 && *(t-1) == ',') /* adjacent singletons? */ *(t-1) = '-'; /* turn them into a range */ } } if (morenum) { /* is there more to life? */ if (min == artnum+1) { /* can we consolidate further? */ bool range_before = (*(t-1) == '-'); bool range_after; char *nextmax; for (nextmax = s; isdigit(*nextmax); nextmax++) ; range_after = *nextmax++ == '-'; if (range_before) *t = '\0'; /* artnum is redundant */ else sprintf(t,"%ld-",(long)artnum);/* artnum will be new min */ if (range_after) s = nextmax; /* *s is redundant */ /* else s = s */ /* *s is new max */ } else sprintf(t,"%ld,",(long)artnum); /* put the number and comma */ } else sprintf(t,"%ld",(long)artnum); /* put the number there (wherever) */ strcat(t,s); /* copy remainder of line */ #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%s\n",mbuf) FLUSH; } #endif free(rcline[ngnum]); rcline[ngnum] = mbuf; /* pull the switcheroo */ *(rcline[ngnum] + rcnums[ngnum] - 1) = '\0'; /* wipe out : or ! */ if (toread[ngnum] > TR_NONE) /* lest we turn unsub into bogus */ --toread[ngnum]; return 0; } #ifdef MCHASE /* delete an article number from a newsgroup, if it is there */ void subartnum(artnum,ngnam) register ART_NUM artnum; char *ngnam; { register NG_NUM ngnum = find_ng(ngnam); register char *s, *t; register ART_NUM min, max; char *mbuf; int curlen; if (!artnum) return; if (ngnum == nextrcline || !rcnums[ngnum]) return; /* not found in newsrc? */ #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%ld<-\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif s = rcline[ngnum] + rcnums[ngnum]; while (*s == ' ') s++; /* skip spaces */ /* a little optimization, since it is almost always the last number */ for (t=s; *t; t++) ; /* find end of string */ curlen = t-rcline[ngnum]; for (t--; isdigit(*t); t--) ; /* find previous delim */ if (*t == ',' && atol(t+1) == artnum) { *t = '\0'; if (toread[ngnum] >= TR_NONE) ++toread[ngnum]; #ifdef DEBUG if (debug & DEB_XREF_MARKER) printf("%s%c %s\n",rcline[ngnum],rcchar[ngnum],s) FLUSH; #endif return; } /* not the last number, oh well, we may need the length anyway */ while (isdigit(*s) && artnum >= (min = atol(s))) { /* while it might have been read */ for (t = s; isdigit(*t); t++) ; /* skip number */ if (*t == '-') { /* is it a range? */ t++; /* skip to next number */ max = atol(t); while (isdigit(*t)) t++; /* skip second number */ if (artnum <= max) { /* it is in range => already read */ if (artnum == min) { min++; artnum = 0; } else if (artnum == max) { max--; artnum = 0; } *(rcline[ngnum] + rcnums[ngnum] - 1) = RCCHAR(rcchar[ngnum]); mbuf = safemalloc((MEM_SIZE)(curlen + (artnum?(6+1)*2+1:1+1))); *s = '\0'; strcpy(mbuf,rcline[ngnum]); /* make new rc line */ s = mbuf + (s-rcline[ngnum]); /* point s into mbuf now */ if (artnum) { /* split into two ranges? */ prange(s,min,artnum-1); s += strlen(s); *s++ = ','; prange(s,artnum+1,max); } else /* only one range */ prange(s,min,max); strcat(s,t); /* copy remainder over */ #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%s\n",mbuf) FLUSH; } #endif free(rcline[ngnum]); rcline[ngnum] = mbuf; /* pull the switcheroo */ *(rcline[ngnum] + rcnums[ngnum] - 1) = '\0'; /* wipe out : or ! */ if (toread[ngnum] >= TR_NONE) ++toread[ngnum]; return; } } else { if (artnum == min) { /* explicitly a read article? */ if (*t == ',') /* pick a comma, any comma */ t++; else if (s[-1] == ',') s--; else if (s[-2] == ',') /* (in case of space) */ s -= 2; strcpy(s,t); /* no need to realloc */ if (toread[ngnum] >= TR_NONE) ++toread[ngnum]; #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif return; } } while (*t && !isdigit(*t)) t++; /* skip comma and any spaces */ s = t; } } void prange(where,min,max) char *where; ART_NUM min,max; { if (min == max) sprintf(where,"%ld",(long)min); else sprintf(where,"%ld-%ld",(long)min,(long)max); } #endif /* calculate the number of unread articles for a newsgroup */ void set_toread(ngnum) register NG_NUM ngnum; { register char *s, *c, *h; char tmpbuf[64], *mybuf = tmpbuf; char *nums; int length; bool virgin_ng = (!abs1st[ngnum]); ART_NUM ngsize = getngsize(ngnum); ART_NUM unread = ngsize; ART_NUM newmax; if (ngsize == TR_BOGUS) { printf("\nWarning! Bogus newsgroup: %s\n",rcline[ngnum]) FLUSH; paranoid = TRUE; toread[ngnum] = TR_BOGUS; return; } if (virgin_ng) { sprintf(tmpbuf," 1-%ld",(long)ngsize); if (strNE(tmpbuf,rcline[ngnum]+rcnums[ngnum])) checkexpired(ngnum,abs1st[ngnum]); /* this might realloc rcline */ } nums = rcline[ngnum]+rcnums[ngnum]; length = strlen(nums); if (length+5+1 > sizeof tmpbuf) mybuf = safemalloc((MEM_SIZE)(length+5+1)); strcpy(mybuf,nums); mybuf[length++] = ','; mybuf[length] = '\0'; for (s = mybuf; isspace(*s); s++) ; for ( ; (c = index(s,',')) != Nullch ; s = ++c) { /* for each range */ *c = '\0'; /* keep index from running off */ if ((h = index(s,'-')) != Nullch) /* find - in range, if any */ unread -= (newmax = atol(h+1)) - atol(s) + 1; else if (newmax = atol(s)) unread--; /* recalculate length */ if (newmax > ngsize) { /* paranoia check */ if (newmax > ngsize + 100) { unread = -1; break; } else { unread += newmax - ngsize; ngmax[ngnum] = ngsize = newmax; } } } if (unread >= 0) /* reasonable number? */ toread[ngnum] = (ART_UNREAD)unread; /* remember how many are left */ else { /* SOMEONE RESET THE NEWSGROUP!!! */ toread[ngnum] = (ART_UNREAD)ngsize; /* assume nothing carried over */ printf("\nWarning! Somebody reset %s--assuming nothing read.\n", rcline[ngnum]) FLUSH; *(rcline[ngnum] + rcnums[ngnum]) = '\0'; paranoid = TRUE; /* enough to make a guy paranoid */ } if (mybuf != tmpbuf) free(mybuf); if (rcchar[ngnum] == NEGCHAR) toread[ngnum] = TR_UNSUB; } /* make sure expired articles are marked as read */ void checkexpired(ngnum,a1st) register NG_NUM ngnum; register ART_NUM a1st; { register char *s; register ART_NUM num, lastnum = 0; char *mbuf, *newnum; if (a1st<=1) return; #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("1-%ld->\n%s%c%s\n",(long)(a1st-1),rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif for (s = rcline[ngnum] + rcnums[ngnum]; isspace(*s); s++); while (*s && (num = atol(s)) <= a1st) { while (isdigit(*s)) s++; while (*s && !isdigit(*s)) s++; lastnum = num; } if (*s) { if (s[-1] == '-') { /* landed in a range? */ if (lastnum != 1) { if (3 + strlen(s) > strlen(rcline[ngnum]+rcnums[ngnum])) { mbuf = safemalloc((MEM_SIZE)(rcnums[ngnum] + 3 + strlen(s) + 1)); strcpy(mbuf, rcline[ngnum]); sprintf(mbuf+rcnums[ngnum]," 1-%s",s); free(rcline[ngnum]); rcline[ngnum] = mbuf; } else { sprintf(rcline[ngnum]+rcnums[ngnum]," 1-%s",s); } } goto ret; } } /* s now points to what should follow first range */ if (s - rcline[ngnum] > rcnums[ngnum] + 6+4+1) mbuf = rcline[ngnum]; else { mbuf = safemalloc((MEM_SIZE)(rcnums[ngnum] + strlen(s) + 6+4+1)); strcpy(mbuf,rcline[ngnum]); } newnum = mbuf+rcnums[ngnum]; sprintf(newnum," 1-%ld",(long)(a1st - (lastnum != a1st))); if (*s) sprintf(newnum+strlen(newnum),",%s",s); if (!checkflag && mbuf == rcline[ngnum]) { rcline[ngnum] = saferealloc(rcline[ngnum], (MEM_SIZE)(rcnums[ngnum] + strlen(newnum) + 1)); } else { if (!checkflag) free(rcline[ngnum]); rcline[ngnum] = mbuf; } ret:; /* semicolon in case DEBUG undefined */ #ifdef DEBUG if (debug & DEB_XREF_MARKER) { printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum], rcline[ngnum] + rcnums[ngnum]) FLUSH; } #endif } trn-3.6/respond.c0000664000000000000000000005342605654303433010712 0ustar /* $Id: respond.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction or this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "intrp.h" #include "cache.h" #include "head.h" #include "term.h" #include "ngdata.h" #include "ng.h" #include "util.h" #include "util2.h" #include "trn.h" #include "artio.h" #include "nntp.h" #include "final.h" #include "decode.h" #include "charsubst.h" #include "INTERN.h" #include "respond.h" static char nullart[] = "\nNull article\n"; bool cut_line(); void respond_init() { if (actfp) export("NEWSACTIVE", filexp(ACTIVE)); } int save_article() { bool_int use_pref; register char *s, *c; char altbuf[CBUFLEN]; int iter; bool interactive = (buf[1] == FINISHCMD); char cmd = *buf; if (!finish_command(interactive)) /* get rest of command */ return SAVE_ABORT; if ((use_pref = isupper(cmd)) != 0) cmd = tolower(cmd); parseheader(art); #ifdef MIMESTORE savefrom = (!mime_article && (cmd == 'w' || cmd == 'e')) #else savefrom = (cmd == 'w' || cmd == 'e') #endif ? htype[PAST_HEADER].ht_minpos : 0; if (artopen(art) == Nullfp) { #ifdef VERBOSE IF(verbose) fputs("\n\ Saving null articles is not very productive! :-)\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif return SAVE_DONE; } if (chdir(cwd)) { printf(nocd,cwd) FLUSH; sig_catcher(0); } if (cmd == 'e') { /* is this an extract command? */ static bool custom_extract = FALSE; int cnt = 0; bool found_cut = FALSE; char art_buf[LBUFLEN], *cmdstr; s = buf+1; /* skip e */ while (*s == ' ') s++; /* skip leading spaces */ safecpy(altbuf,filexp(s),sizeof altbuf); s = altbuf; if (*s) { cmdstr = cpytill(buf,s,'|'); /* check for | */ s = buf + strlen(buf)-1; while (*s == ' ') s--; /* trim trailing spaces */ *++s = '\0'; if (*cmdstr) { s = cmdstr+1; /* skip | */ while (*s == ' ') s++; if (*s) { /* if new command, use it */ if (extractprog) free(extractprog); extractprog = savestr(s); /* put extracter in %e */ } else cmdstr = extractprog; } else cmdstr = Nullch; s = buf; } else { if (extractdest) strcpy(s, extractdest); if (custom_extract) cmdstr = extractprog; else cmdstr = Nullch; } if (cmdstr) { if (strEQ(extractprog,"-")) cmdstr = Nullch; else if (decode_fp != Nullfp) decode_end(); } custom_extract = (cmdstr != 0); seekart((long)savefrom); if (*s != '/') { /* relative path? */ c = (s==buf ? altbuf : buf); interp(c, (sizeof buf), getval("SAVEDIR",SAVEDIR)); if (makedir(c,MD_DIR)) /* ensure directory exists */ strcpy(c,cwd); if (*s) { while (*c) c++; *c++ = '/'; strcpy(c,s); /* add filename */ } s = (s==buf ? altbuf : buf); } if (*s != '/') { /* path still relative? */ c = (s==buf ? altbuf : buf); sprintf(c, "%s/%s", cwd, s); s = c; /* absolutize it */ } if (decode_fp != Nullfp) { printf("Continuing %s:%s\n", decode_fname, cmd != '\0' && strNE(extractdest,s) ? " (Ignoring conflicting directory)" : nullstr ) FLUSH; #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif if (decode_type == UUDECODE) uudecode(artfp); else unship(artfp); } else { if (extractdest) free(extractdest); s = extractdest = savestr(s); /* make it handy for %E */ if (makedir(s, MD_DIR)) { /* ensure directory exists */ int_count++; #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif return SAVE_DONE; } if (chdir(s)) { printf(nocd,s) FLUSH; sig_catcher(0); } s = getwd(buf); /* simplify path for output */ while(readart(art_buf,LBUFLEN) != Nullch) { if (*art_buf <= ' ') continue; /* Ignore empty or initially-whitespace lines */ #ifdef MIMESTORE if (mime_article) { char oldmode = mode; if (!custom_extract) { printf("Extracting MIME article into %s:\n", s) FLUSH; extractprog = savestr(filexp(getval("MIMESTORE",MIMESTORE))); } else printf("Extracting MIME article into %s using %s\n", s, extractprog) FLUSH; cnt = 0; #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif interp(cmd_buf, sizeof cmd_buf, getval("EXMIMESAVER",EXMIMESAVER)); termlib_reset(); mode = 'x'; resetty(); /* restore tty state */ doshell(SH,cmd_buf); noecho(); /* revert to cbreaking */ crmode(); termlib_init(); mode = oldmode; break; } #endif if (found_cut && custom_extract) { printf("Extracting data into %s using %s:\n", s, extractprog) FLUSH; goto extract_it; } if (((*art_buf == '#' || *art_buf == ':') && (strnEQ(art_buf+1, "! /bin/sh", 9) || strnEQ(art_buf+1, "!/bin/sh", 8) || strnEQ(art_buf+2, "This is ", 8))) || strnEQ(art_buf, "sed ", 4) || strnEQ(art_buf, "cat ", 4) || strnEQ(art_buf, "echo ", 5)) { savefrom = tellart()-strlen(art_buf)-NL_SIZE+1; seekart((long)savefrom); if (custom_extract) { printf("Extracting shar into %s using %s:\n", s, extractprog) FLUSH; goto extract_it; } /* Check for special-case of shar'ed-uuencoded file */ while(readart(art_buf,LBUFLEN) != Nullch) { if (*art_buf == '#' || *art_buf == ':' || strnEQ(art_buf, "echo ", 5) || strnEQ(art_buf, "sed ", 4)) continue; if (strnEQ(art_buf, "Xbegin ", 7)) { decode_type = UUDECODE; goto decode_it; } break; } printf("Extracting shar into %s:\n", s) FLUSH; if (extractprog) free(extractprog); extractprog = savestr(filexp(getval("UNSHAR",UNSHAR))); extract_it: cnt = 0; #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif interp(cmd_buf,(sizeof cmd_buf),getval("EXSAVER",EXSAVER)); termlib_reset(); resetty(); /* restore tty state */ doshell(SH,cmd_buf); noecho(); /* revert to cbreaking */ crmode(); termlib_init(); break; } else if (!custom_extract && (strEQ(art_buf,"$\n") || strEQ(art_buf,"$ f\n"))) { savefrom = tellart()-strlen(art_buf)-NL_SIZE+1; if (found_cut || (readart(art_buf,LBUFLEN) != Nullch && (strnEQ(art_buf, "ship ", 5) || strnEQ(art_buf, "cont ", 5)))) { decode_type = UNSHIP; goto decode_it; } } else if (!custom_extract && (strEQ(art_buf,"table\n") || strnEQ(art_buf,"begin ", 6))) { decode_type = UUDECODE; savefrom = tellart()-strlen(art_buf)-NL_SIZE+1; decode_it: printf("Extracting %s file into %s:\n", decode_type == UNSHIP? "shipped":"uuencoded", s) FLUSH; if (extractprog) free(extractprog); extractprog = savestr("-"); seekart((long)savefrom); cnt = 0; #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif if (decode_type == UUDECODE) { uud_start(); uudecode(artfp); } else unship(artfp); break; } else { if (cut_line(art_buf)) { savefrom = tellart(); found_cut = TRUE; } else if (found_cut || ++cnt == 300) { break; } } }/* while */ if (cnt) { if (custom_extract) printf("Didn't find cut line for extraction to '%s'.\n", extractprog) FLUSH; else printf("Unable to determine type of file.\n") FLUSH; } }/* if */ } else if ((s = index(buf,'|')) != Nullch) { /* is it a pipe command? */ s++; /* skip the | */ while (*s == ' ') s++; safecpy(altbuf,filexp(s),sizeof altbuf); if (savedest) free(savedest); savedest = savestr(altbuf); #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif interp(cmd_buf, (sizeof cmd_buf), getval("PIPESAVER",PIPESAVER)); /* then set up for command */ termlib_reset(); resetty(); /* restore tty state */ if (use_pref) /* use preferred shell? */ doshell(Nullch,cmd_buf); /* do command with it */ else doshell(sh,cmd_buf); /* do command with sh */ noecho(); /* and stop echoing */ crmode(); /* and start cbreaking */ termlib_init(); } else { /* normal save */ bool there, mailbox; char *savename = getval("SAVENAME",SAVENAME); s = buf+1; /* skip s or S */ if (*s == '-') { /* if they are confused, skip - also */ #ifdef VERBOSE IF(verbose) fputs("Warning: '-' ignored. This isn't readnews.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("'-' ignored.\n",stdout) FLUSH; #endif s++; } for (; *s == ' '; s++); /* skip spaces */ safecpy(altbuf,filexp(s),sizeof altbuf); s = altbuf; if (*s != '/') { interp(buf, (sizeof buf), getval("SAVEDIR",SAVEDIR)); if (makedir(buf,MD_DIR)) /* ensure directory exists */ strcpy(buf,cwd); if (*s) { for (c = buf; *c; c++) ; *c++ = '/'; strcpy(c,s); /* add filename */ } s = buf; } for (iter = 0; (there = stat(s,&filestat) >= 0) && S_ISDIR(filestat.st_mode); iter++) { /* is it a directory? */ c = (s+strlen(s)); *c++ = '/'; /* put a slash before filename */ interp(c, s==buf?(sizeof buf):(sizeof altbuf), iter ? "News" : savename ); /* generate a default name somehow or other */ } makedir(s,MD_FILE); if (*s != '/') { /* relative path? */ c = (s==buf ? altbuf : buf); sprintf(c, "%s/%s", cwd, s); s = c; /* absolutize it */ } if (savedest) free(savedest); s = savedest = savestr(s); /* doesn't move any more */ /* make it handy for %b */ if (!there) { if (mbox_always) mailbox = TRUE; else if (norm_always) mailbox = FALSE; else { char *dflt = (instr(savename,"%a", TRUE) ? "nyq" : "ynq"); sprintf(cmd_buf, "\nFile %s doesn't exist--\n use mailbox format?",s); reask_save: in_char(cmd_buf, 'M', dflt); putchar('\n') FLUSH; #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) printf("\n\ Type y to create %s as a mailbox.\n\ Type n to create it as a normal file.\n\ Type q to abort the save.\n\ ",s) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\ y to create mailbox.\n\ n to create normal file.\n\ q to abort.\n\ ",stdout) FLUSH; #endif goto reask_save; } else if (*buf == 'n') { mailbox = FALSE; } else if (*buf == 'y') { mailbox = TRUE; } else if (*buf == 'q') { goto s_bomb; } else { fputs(hforhelp,stdout) FLUSH; settle_down(); goto reask_save; } } } else if (S_ISCHR(filestat.st_mode)) mailbox = FALSE; else { int tmpfd; tmpfd = open(s,0); if (tmpfd == -1) mailbox = FALSE; else { if (read(tmpfd,buf,LBUFLEN)) { c = buf; if (!isspace(MBOXCHAR)) /* if non-zero, */ while (isspace(*c)) /* check the first character */ c++; mailbox = (*c == MBOXCHAR); } else { mailbox = mbox_always; /* if zero length, recheck -M */ } close(tmpfd); } } safecpy(cmd_buf, filexp(mailbox ? getval("MBOXSAVER",MBOXSAVER) : getval("NORMSAVER",NORMSAVER) ), sizeof cmd_buf); /* format the command */ #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif termlib_reset(); resetty(); /* make terminal behave */ if (doshell(use_pref?Nullch:SH,cmd_buf)) { termlib_init(); fputs("Not saved",stdout); } else { termlib_init(); printf("%s to %s %s", there?"Appended":"Saved", mailbox?"mailbox":"file", s); } if (interactive) putchar('\n') FLUSH; noecho(); /* make terminal do what we want */ crmode(); } s_bomb: #ifdef USE_NNTP if (chdir(spool)) { #else if (chdir(spool) || chdir(ngdir)) { #endif printf(nocd,ngdir) FLUSH; sig_catcher(0); } #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif return SAVE_DONE; } int cancel_article() { char *ngs_buf; char *from_buf; char *reply_buf; int myuid = getuid(); int r = -1; if (artopen(art) == Nullfp) { #ifdef VERBOSE IF(verbose) fputs("\n\ Canceling null articles is your idea of fun? :-)\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif return r; } reply_buf = fetchlines(art,REPLY_LINE); from_buf = fetchlines(art,FROM_LINE); ngs_buf = fetchlines(art,NGS_LINE); if (!instr(from_buf,phostname,FALSE) || (!instr(from_buf,loginName,TRUE) && !instr(reply_buf,loginName,TRUE) && #ifdef NEWS_ADMIN myuid != newsuid && #endif myuid != ROOTID ) ) { #ifdef DEBUG if (debug) printf("\n%s@%s != %s\n",loginName,phostname,from_buf) FLUSH; #endif #ifdef VERBOSE IF(verbose) fputs("\nYou can't cancel someone else's article\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNot your article\n",stdout) FLUSH; #endif } else { tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == Nullfp) { printf(cantcreate,headname) FLUSH; goto no_cancel; } interp(buf, (sizeof buf), getval("CANCELHEADER",CANCELHEADER)); fputs(buf,tmpfp); fclose(tmpfp); fputs("\nCanceling...\n",stdout) FLUSH; r = doshell(sh,filexp(getval("CANCEL",CANCEL))); } no_cancel: free(ngs_buf); free(from_buf); free(reply_buf); return r; } int supersede_article() /* Supersedes: */ { char *ngs_buf; char *from_buf; char *reply_buf; int myuid = getuid(); int r = -1; bool incl_body = (*buf == 'Z'); if (artopen(art) == Nullfp) { #ifdef VERBOSE IF(verbose) fputs("\n\ Superceding null articles is your idea of fun? :-)\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs(nullart,stdout) FLUSH; #endif return r; } reply_buf = fetchlines(art,REPLY_LINE); from_buf = fetchlines(art,FROM_LINE); ngs_buf = fetchlines(art,NGS_LINE); if (!instr(from_buf,phostname,FALSE) || (!instr(from_buf,loginName,TRUE) && !instr(reply_buf,loginName,TRUE) && #ifdef NEWS_ADMIN myuid != newsuid && #endif myuid != ROOTID ) ) { #ifdef DEBUG if (debug) printf("\n%s@%s != %s\n",loginName,phostname,from_buf) FLUSH; #endif #ifdef VERBOSE IF(verbose) fputs("\nYou can't supersede someone else's article\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNot your article\n",stdout) FLUSH; #endif } else { tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == Nullfp) { printf(cantcreate,headname) FLUSH; goto no_commute; } interp(buf, (sizeof buf), getval("SUPERSEDEHEADER",SUPERSEDEHEADER)); fputs(buf,tmpfp); if (incl_body && artfp != Nullfp) { parseheader(art); seekart((long)htype[PAST_HEADER].ht_minpos); while (readart(buf,LBUFLEN) != Nullch) { fputs(buf,tmpfp); } } fclose(tmpfp); safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)), sizeof cmd_buf); invoke(cmd_buf,origdir); r = 0; } no_commute: free(ngs_buf); free(from_buf); free(reply_buf); return r; } void reply() { bool incl_body = (*buf == 'R'); #ifdef CHARSUBST char hbuf[4*LBUFLEN]; #endif char *maildoer = savestr(getval("MAILPOSTER",MAILPOSTER)); artopen(art); tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == Nullfp) { printf(cantcreate,headname) FLUSH; goto no_reply; } interp(buf, (sizeof buf), getval("MAILHEADER",MAILHEADER)); fputs(buf,tmpfp); if (!instr(maildoer,"%h",TRUE)) #ifdef VERBOSE IF(verbose) printf("\n%s\n(Above lines saved in file %s)\n",buf,headname) FLUSH; ELSE #endif #ifdef TERSE printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH; #endif if (incl_body && artfp != Nullfp) { interp(buf, (sizeof buf), getval("YOUSAID",YOUSAID)); fprintf(tmpfp,"%s\n",buf); parseheader(art); seekart((long)htype[PAST_HEADER].ht_minpos); while (readart(buf,LBUFLEN) != Nullch) { #ifdef CHARSUBST strcharsubst(buf,hbuf); fprintf(tmpfp,"%s%s",indstr,hbuf); #else fprintf(tmpfp,"%s%s",indstr,buf); #endif } fprintf(tmpfp,"\n"); } fclose(tmpfp); safecpy(cmd_buf,filexp(maildoer),sizeof cmd_buf); invoke(cmd_buf,origdir); UNLINK(headname); /* kill the header file */ no_reply: free(maildoer); } void forward() { #ifdef CHARSUBST char hbuf[4*LBUFLEN]; #endif char *maildoer = savestr(getval("FORWARDPOSTER",FORWARDPOSTER)); artopen(art); tmpfp = fopen(headname,"w"); /* open header file */ if (tmpfp == Nullfp) { printf(cantcreate,headname) FLUSH; goto no_forward; } interp(buf, (sizeof buf), getval("FORWARDHEADER",FORWARDHEADER)); fputs(buf,tmpfp); if (!instr(maildoer,"%h",TRUE)) #ifdef VERBOSE IF(verbose) printf("\n%s\n(Above lines saved in file %s)\n",buf,headname) FLUSH; ELSE #endif #ifdef TERSE printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH; #endif if (artfp != Nullfp) { interp(buf, (sizeof buf), getval("FORWARDMSG",FORWARDMSG)); if (*buf) fprintf(tmpfp,"%s\n",buf); parseheader(art); seekart(0L); while (readart(buf,LBUFLEN) != Nullch) { #ifdef CHARSUBST strcharsubst(buf,hbuf); fprintf(tmpfp,"%s",hbuf); #else fprintf(tmpfp,"%s",buf); #endif } interp(buf, (sizeof buf), getval("FORWARDMSGEND",FORWARDMSGEND)); if (*buf) fprintf(tmpfp,"%s\n",buf); } fclose(tmpfp); safecpy(cmd_buf,filexp(maildoer),sizeof cmd_buf); invoke(cmd_buf,origdir); UNLINK(headname); /* kill the header file */ no_forward: free(maildoer); } void followup() { bool incl_body = (*buf == 'F'); char hbuf[4*LBUFLEN]; /* four times the old size */ ART_NUM oldart = art; if (!incl_body && art <= lastart) { in_answer("\n\nAre you starting an unrelated topic? [ynq] ", 'F'); setdef(buf,"y"); if (*buf == 'q') /*TODO: need to add 'h' also */ return; if (*buf != 'n') art = lastart + 1; } artopen(art); tmpfp = fopen(headname,"w"); if (tmpfp == Nullfp) { printf(cantcreate,headname) FLUSH; art = oldart; return; } interp(hbuf, (sizeof hbuf), getval("NEWSHEADER",NEWSHEADER)); fprintf(tmpfp,"%s",hbuf); if (incl_body && artfp != Nullfp) { #ifdef VERBOSE if (verbose) fputs("\n\ (Be sure to double-check the attribution against the signature, and\n\ trim the quoted article down as much as possible.)\n\ ",stdout) FLUSH; #endif interp(buf, (sizeof buf), getval("ATTRIBUTION",ATTRIBUTION)); fprintf(tmpfp,"%s\n",buf); parseheader(art); seekart((long)htype[PAST_HEADER].ht_minpos); while (readart(buf,LBUFLEN) != Nullch) { #ifdef CHARSUBST strcharsubst(buf,hbuf); fprintf(tmpfp,"%s%s",indstr,hbuf); #else fprintf(tmpfp,"%s%s",indstr,buf); #endif } fprintf(tmpfp,"\n"); } fclose(tmpfp); safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),sizeof cmd_buf); invoke(cmd_buf,origdir); UNLINK(headname); art = oldart; } void invoke(cmd,dir) char *cmd,*dir; { char oldmode = mode; if (chdir(dir)) { printf(nocd,dir) FLUSH; return; } termlib_reset(); mode = 'x'; #ifdef VERBOSE IF(verbose) printf("\n(leaving cbreak mode; cwd=%s)",dir); ELSE #endif #ifdef TERSE printf("\n(-cbreak; cwd=%s)",dir); #endif #ifdef DEBUG if (debug) printf("\nInvoking command: %s",cmd); #endif printf("\n\n") FLUSH; resetty(); /* make terminal well-behaved */ doshell(sh,cmd); /* do the command */ noecho(); /* set no echo */ crmode(); /* and cbreak mode */ #ifdef VERBOSE IF(verbose) fputs("\n(re-entering cbreak mode)\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n(+cbreak)\n",stdout) FLUSH; #endif termlib_init(); mode = oldmode; #ifdef USE_NNTP if (chdir(spool)) { #else if (chdir(spool) || chdir(ngdir)) { #endif printf(nocd,ngdir) FLUSH; sig_catcher(0); } } /* ** cut_line() determines if a line is meant as a "cut here" marker. ** Some examples that we understand: ** ** BEGIN--cut here--cut here ** ** ------------------ tear at this line ------------------ ** ** #----cut here-----cut here-----cut here-----cut here----# */ bool cut_line(str) char *str; { char *cp, got_flag; char word[80]; int dash_cnt, equal_cnt, other_cnt; /* Disallow any single-/double-quoted, parenthetical or c-commented ** string lines. Make sure it has the cut-phrase and at least six ** '-'s or '='s. If only four '-'s are present, check for a duplicate ** of the cut phrase. If over 20 unknown characters are encountered, ** assume it isn't a cut line. If we succeed, return TRUE. */ for (cp = str, dash_cnt = equal_cnt = other_cnt = 0; *cp; cp++) { switch (*cp) { case '-': dash_cnt++; break; case '=': equal_cnt++; break; case '/': if(*(cp+1) != '*') { break; } case '"': case '\'': case '(': case ')': case '[': case ']': case '{': case '}': return FALSE; default: other_cnt++; break; } } if (dash_cnt < 4 && equal_cnt < 6) return FALSE; got_flag = 0; for (*(cp = word) = '\0'; *str; str++) { if (islower(*str)) *cp++ = *str; else if (isupper(*str)) *cp++ = tolower(*str); else { if (*word) { *cp = '\0'; switch (got_flag) { case 2: if (!strcmp(word, "line") || !strcmp(word, "here")) if ((other_cnt -= 4) <= 20) return TRUE; break; case 1: if (!strcmp(word, "this")) { got_flag = 2; other_cnt -= 4; } else if (!strcmp(word, "here")) { other_cnt -= 4; if ((dash_cnt >= 6 || equal_cnt >= 6) && other_cnt <= 20) return TRUE; dash_cnt = 6; got_flag = 0; } break; case 0: if (!strcmp(word, "cut") || !strcmp(word, "snip") || !strcmp(word, "tear")) { got_flag = 1; other_cnt -= strlen(word); } break; } *(cp = word) = '\0'; } } } /* for *str */ return FALSE; } trn-3.6/mbox.saver.SH0000664000000000000000000000333705545455743011423 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting mbox.saver (with variable substitutions)" $spitshell >mbox.saver <>mbox.saver <> \$7 !GROK!THIS! ;; *) $spitshell >>mbox.saver <& /" $echo "" $echo "" ) >> \$7 !GROK!THIS! ;; esac $eunicefix mbox.saver chmod 755 mbox.saver trn-3.6/hash.h0000664000000000000000000000216405472327710010163 0ustar /* * general-purpose in-core hashing */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #define HASHDATUM struct hashdatum HASHDATUM { char *dat_ptr; unsigned dat_len; }; #ifndef HASHTABLE #define HASHTABLE struct hashtable #endif EXT HASHTABLE *hashcreate _((unsigned, int(*)())); EXT void hashdestroy _((HASHTABLE*)); EXT void hashstore _((HASHTABLE*,char*,int,HASHDATUM)); EXT void hashdelete _((HASHTABLE*,char*,int)); EXT HASHDATUM hashfetch _((HASHTABLE*,char*,int)); EXT void hashstorelast _((HASHDATUM)); EXT void hashwalk _((HASHTABLE*,void(*)(),int)); /* Internal stuff */ #ifdef DOINIT #define BADTBL(tbl) ((tbl)->ht_magic != HASHMAG) #define HASHMAG ((char)0257) #define HASHENT struct hashent HASHENT { HASHENT *he_next; /* in hash chain */ HASHDATUM he_data; int he_keylen; /* to help verify a match */ }; HASHTABLE { HASHENT **ht_addr; /* array of HASHENT pointers */ unsigned ht_size; char ht_magic; int (*ht_cmp)(); }; #endif trn-3.6/util.h0000664000000000000000000000304505654303443010213 0ustar /* $Id: util.h,v 3.0 1991/09/09 20:27:37 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT bool waiting INIT(FALSE); /* are we waiting for subprocess (in doshell)? */ EXT int len_last_line_got INIT(0); /* strlen of some_buf after */ /* some_buf = get_a_line(bufptr,bufsize,realloc,fp) */ EXT MEM_SIZE buflen_last_line_got INIT(0); /* is the string for makedir a directory name or a filename? */ #define MD_DIR 0 #define MD_FILE 1 void util_init _((void)); int doshell _((char*,char*)); char *safemalloc _((MEM_SIZE)); char *saferealloc _((char*,MEM_SIZE)); char *safecpy _((char*,char*,int)); char *safecat _((char*,char*,int)); char *cpytill _((char*,char*,int)); #ifdef SETUIDGID int eaccess _((char*,int)); #endif char *getwd _((char*)); char *get_a_line _((char*,int,bool_int,FILE*)); char *savestr _((char*)); int makedir _((char*,int)); void notincl _((char*)); char *getval _((char*,char*)); void growstr _((char**,int*,int)); void setdef _((char*,char*)); void safelink _((char*,char*)); trn-3.6/trn.10000664000000000000000000031012307041327652007750 0ustar .\" $Id: trn.1,v 3.0 1992/12/14 00:14:10 davison Trn $ .\" .\" This software is Copyright 1991 by Stan Barber. .\" .\" Permission is hereby granted to copy, reproduce, redistribute or otherwise .\" use this software as long as: there is no monetary profit gained .\" specifically from the use or reproduction of this software, it is not .\" sold, rented, traded or otherwise marketed, and this copyright notice is .\" included prominently in any copy made. .\" .\" The authors make no claims as to the fitness or correctness of this software .\" for any use whatsoever, and it is provided as is. Any use of this software .\" is at the user's own risk. .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .de Ip .br .ie \\n.$>=3 .ne \\$3 .el .ne 3 .IP "\\$1" \\$2 .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .ds qq "" .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH TRN 1 LOCAL .UC 6 .SH NAME trn - threaded read news program .SH SYNOPSIS .B trn [options] [newsgroups] .SH DESCRIPTION .I Trn is a threaded version of .I rn, which is a replacement for the readnews(1) program. Being \*(L"threaded\*(R" means that the articles are interconnected in reply order. Each discussion thread is a tree of articles where all the reply (child) articles branch off from their respective originating (parent) articles. A representation of this tree (or a portion of it) is displayed in the article header as you are reading news. This gives you a better feel for how all the articles are related, and even lets you see at a glance when an article has replies \*(-- a good thing to check before posting. In addition, .I trn has a thread selector that allows you to quickly browse through a list of subjects and choose the ones you find interesting. This thread selector sorts articles according to various criteria and can be switched into various display modes that allows you to pick all the subjects separately (threads can have multiple subjects) or even pick individual articles. Any items you don't select can be saved for reading later or marked as read with a single keystroke. .PP If you are already familiar with .I trn you may just want to read the .IR "WHAT'S NEW" section. People upgrading from .I rn will probably want to pay attention to the sections on .IR "The Selector" , .IR "The Tree Display" , and the aforementioned .IR "WHAT'S NEW" . If you're impatient, just dive in and get started. All the regular commands will be familiar to an .I rn or .I trn user, and the on-line help will give you a quick run-down of what commands are available (just type \*(L'h\*(R' from any prompt). I'd also suggest using the command: .Sp trn \-x \-X .Sp to make sure some of the best features are turned on. .Sh "Starting Trn" If no newsgroups are specified, all the newsgroups which have unread news will be presented to the user in the order in which they occur in the .I .newsrc file. At the prompt for each group you can choose to read it, skip it, move it, etc. If a list of newsgroups is provided on the command line, .I trn will start up in \*(L"add\*(R" mode, using the list as a set of patterns to add new newsgroups and restrict which newsgroups are displayed (see also the discussion of the \*(L'a\*(R' command on the newsgroup-selection level). .PP .I Trn operates on four levels: the newsgroup-selection level, the thread selector, the article-reading level, and the paging level. Each level has its own set of commands, and its own help menu. At the paging level (the bottom level) .I trn behaves much like the .IR more (1) program. At the article-reading level articles are presented to you in the order of their replies, with the subjects being ordered by the date of the oldest unread article (though there are commands for changing the default display order). In the thread selector you are presented with the subjects and (usually) authors associated with each discussion thread, and given a chance to choose which ones you wish to read now, save for later, or manipulate in some way. At the newsgroup-selection level (the top level), you may specify which newsgroup you want next, or read them in the default order, which is the order that the newsgroups occur in your .I .newsrc file. (You will therefore want to rearrange your .I .newsrc file to put the most interesting newsgroups first. This can be done with the \*(L'm\*(R' command on the Newsgroup Selection level. WARNING: invoking readnews/vnews (the old user interface) in any way (including as a news checker in your login sequence!) will cause your .I .newsrc to be disarranged again.) .PP On any level, at ANY prompt, help is available by typing an \*(L'h\*(R'. This gives you a summary of available commands and what they do. Remember this command, you'll need it. .PP Typing space to any question means to do the normal thing. You will know what that is because every prompt has a list of several plausible commands enclosed in square brackets. The first command in the list is the one which will be done if you type a space. (All input is done in cbreak mode, so carriage returns should not be typed to terminate anything except certain multi-character commands. Those commands will be obvious in the discussion below because they take an argument.) .PP Upon startup, .I trn will do several things: .Ip 1. 4 It will look for your .I .newsrc file, which is your list of subscribed-to newsgroups. If .I trn doesn't find a .IR .newsrc , it will create one. If it does find one, it will back it up under the name \*(L".\|oldnewsrc\*(R". .Ip 2. 4 It will input your .I .newsrc file, listing out the first several newsgroups with unread news. .Ip 3. 4 It will perform certain consistency checks on your .IR .newsrc . If your .I .newsrc is out of date in any of several ways, .I trn will warn you and patch it up for you, but you may have to wait a little longer for it to start up. .Ip 4. 4 .I Trn will next check to see if any new newsgroups have been created, and give you the opportunity to add them to your .IR .newsrc . .Ip 5. 4 .I Trn goes into the top prompt level \*(-- the newsgroup-selection level. .Sh "Newsgroup Selection Level" In this section the words \*(L"next\*(R" and \*(L"previous\*(R" refer to the ordering of the newsgroups in your .I .newsrc file. On the newsgroup-selection level, the prompt looks like this: .Sp ====== 17 unread articles in talk.blurfl \*(-- read now? [ynq] .Sp unless the group is set for unthreaded reading, in which case the first six characters are \*(L"******\*(R". The following commands may be given at this level: .Ip \+ 8 Enter this newsgroup through the selector. .Ip y 8 Begin reading this newsgroup now. .Ip SP 8 Enter the newsgroup by executing the default command listed in []'s. .Ip .command 8 Do this newsgroup now, but execute .I command before displaying anything. The command will be interpreted as if typed on the article selection level. .Ip = 8 Start this newsgroup, but list subjects before displaying articles. .Ip U 8 Enter this newsgroup through the \*(L"Set unread\*(R" prompt. .Ip t 8 Toggle the newsgroup between threaded and unthreaded reading. The default is threaded, and the current setting is stored in your .newsrc. .Ip n 8 Go to the next newsgroup with unread news. .Ip N 8 Go to the next newsgroup. .Ip p 8 Go to the previous newsgroup with unread news. If there is none, stay at the current newsgroup. .Ip P 8 Go to the previous newsgroup. .Ip \- 8 Go to the previously displayed newsgroup (regardless of whether it is before or after the current one in the list). .Ip 1 8 Go to the first newsgroup. .Ip ^ 8 Go to the first newsgroup with unread news. .Ip $ 8 Go to the end of the newsgroups list. .Ip "g newsgroup" 8 Go to .IR newsgroup , which can be the group's name or a zero-relative number of the groups in your .newsrc (see the \*(L'L\*(R' command to list your .newsrc). If it isn't currently subscribed to, you will be asked if you want to subscribe. .Ip "/pattern" 8 Scan forward for a newsgroup matching .IR pattern . Patterns do globbing like filenames, i.\|e., use * to match any sequence of characters, and [] to specify a list of characters to match. Use . to match a single character. Unlike normal filename globbing, newsgroup-searching is not anchored to the front and back of the filename, i.\|e. \*(L"/ski\*(R" will find rec.skiing. You may use ^ or $ to anchor the front or back of the search: \*(L"/^test$\*(R" will find newsgroup test and nothing else If you want to include newsgroups with 0 unread articles, append /r. If the newsgroup is not found between the current newsgroup and the last newsgroup, the search will wrap around to the beginning. .Ip "?pattern" 8 Same as /, but search backwards. .Ip u 8 Unsubscribe from the current newsgroup. .Ip "l string" 8 List newsgroups not subscribed to which contain the string specified. .Ip L 8 13v Lists the current state of the .IR .newsrc , along with status information. .Sp .nf \h'|0.5i'Status \h'|2i'Meaning \h'|0.5i' \h'|2i'Count of unread articles in newsgroup. \h'|0.5i'READ \h'|2i'No unread articles in newsgroup. \h'|0.5i'UNSUB \h'|2i'Unsubscribed newsgroup. \h'|0.5i'BOGUS \h'|2i'Bogus newsgroup. \h'|0.5i'JUNK \h'|2i'Ignored line in .newsrc \h'|2i'(e.\|g. readnews \*(L"options\*(R" line). .fi .Sp (A bogus newsgroup is one that is not in the list of active newsgroups in the active file, which on most systems is /usr/lib/news/active unless you use NNTP.) .Ip "m {name}" 8 Move the named newsgroup somewhere else in the .IR .newsrc . If no name is given, the current newsgroup is moved. There are a number of ways to specify where you want the newsgroup \*(-- type h for help when it asks where you want to put it. .Ip c 8 Catch up \*(-- mark all unread articles in this newsgroup as read. .Ip A 8 Abandon the changes made to the current newsgroup since .I trn was started. Useful when you accidentally mark a group as read. .Ip "o {pattern}" 8 .Ip "O {pattern}" 8 Only display those newsgroups whose name matches .IR pattern . Patterns are the same as for the \*(L'/\*(R' command. Multiple patterns may be separated by spaces, just as on the command line. The restriction will remain in effect either until there are no articles left in the restricted set of newsgroups, or another restriction command is given. Since .I pattern is optional, \*(L'o\*(R' by itself will remove the restriction. Using \*(L'O\*(R' will omit empty groups from the cycle. .Ip "a pattern" 8 Add unsubscribed newsgroups matching .IR pattern . If any matching newsgroups are found, you will be asked for each one whether you would like to add it. If you want to add all the newsgroups, you can type \*(L'Y\*(R' and they will be added the the end of the .I .newsrc file. If you don't want to subscribe, all the remaining groups can be ignored by typing \*(L'N\*(R'. After any new newsgroups have been added, the \*(L'a\*(R' command also restricts the current set of newsgroups just like the \*(L'O\*(R' command does. .Ip & 8 Print out the current status of command-line switches and any newsgroup restrictions. .Ip "&switch {switch}" 8 Set additional command-line switches. .Ip && 8 Print out the current macro definitions. .Ip "&&keys commands" 8 Define additional macros. .Ip !command 8 Escape to a subshell. One exclamation mark (!) leaves you in your own news directory. A double exclamation mark (!!) leaves you in the spool directory for news, which is usually /usr/spool/news unless you're using NNTP to read news. The environment variable SHELL will be used if defined. If .I command is null, an interactive shell is started. .Ip v 8 Print the current version number and information on where to send bug reports. .Ip q 8 Quit. .Ip x 8 Quit, restoring .newsrc to its state at startup of .IR trn . The .newsrc you would have had if you had exited with \*(L'q\*(R' will be called .newnewsrc, in case you didn't really want to type \*(L'x\*(R'. .Ip ^K 8 Edit the global list of memorized commands (in the global KILL file) that you wish to be performed in every newsgroup as it is started up (that is, when it is selected at the newsgroup-selection level). This file contains commands (one per line) such as /subject/:j or /author/f:+ to kill or select articles based on the indicated search criteria. There is also a local list of commands for each newsgroup that can contain kill/selection commands tailored for each specific group. Because of the overhead involved in searching for articles to kill, it is better if possible to use a local list rather than the global one. Local memorized commands are usually maintained by using the \*(L'A\*(R' or \*(L'T\*(R' commands from the article/pager level or in the selector. There is also a K search modifier that appends any search command you desire to add. It is also possible to manually edit the file with the \*(L'^K\*(R' command from anywhere inside a newsgroup. If either of the environment variables VISUAL or EDITOR is set, the specified editor will be invoked; otherwise a default editor is invoked on the KILL file. .Sh "The Selector" Most people who don't have all day to read news will want to enter a newsgroup by way of the selector. This is accomplished by using the \*(L'+\*(R' command at the newsgroup-selection or article/pager levels. In fact, this may be the default command for entering a newsgroup, depending on how your version of .I trn was configured and your use of the .B \-X option. .PP The selector displays a list of articles by their subjects and (usually) authors. The articles are grouped into threads by default (which may list multiple subjects per selectable item if the subject has changed during the discussion) and ordered by the date of their oldest unread article. Thread or subject groups are also shown with a count of the number of articles in each group. Each selectable item is preceded by a letter or number that can be typed to toggle its selection. Items that are selected are flagged with a \*(L'+\*(R' after their letter. Groups that have only some of their articles selected are flagged with a \*(L'*\*(R'. You can change the selector's mode (to pick each subject separately or pick individual articles), order the list by a variety of sort criteria, and switch the author display between its long, medium and short styles using the commands detailed below. .PP The following commands are available in the selector: .Ip "a-z,0-9,A-Z" 8 Select/deselect the indicated item by its letter or number. There are quite a few letters omitted from the alpha characters to be typed as commands \*(-- see below. Also, the variable SELECTCHARS is available to customize which characters you want to be used as selection letters, overriding their command function. .Ip SP 8 Perform the default command. This is usually > for most pages, and Z on the last page (although D and X are also quite popular). .Ip CR 8 Begin reading. If no articles are selected, the current item is selected (unless you've marked it as killed). .Ip "Z,TAB" 8 Begin reading. If no articles are selected, read all unread articles. .Ip "\*(L'.\*(R'" 8 Toggle the current item's selection (the one under the cursor). .Ip * 8 Same as \*(L'.\*(R' except that it affects all articles with the same subject (useful in the article selector). .Ip # 8 Make an overriding selection that reads the current item only, temporarily ignoring all other selections. .Ip "k, \*(L',\*(R'" 8 Mark the current item as killed. .Ip "m, \e" 8 Unmark the current item. .Ip \- 8 Set a range, as in a \- k. Repeats the last marking action: selection, deselection, killing, or unmarking. .Ip @ 8 Toggle all visible selections. .Ip M 8 Mark the current item's article(s) to return on newsgroup exit and kill the item. .Ip Y 8 Yank back and select the marked-to-return articles, clearing their to-return status. .Ip E 8 Exclude all unselected items from the selection list (narrow the display). Press it again to pick from all available items. .Ip "n, ]" 8 Move down to the next item (try the down-arrow keypad key also). .Ip "p, [" 8 Move up to the previous item (try the up-arrow keypad key also). .Ip < 8 Go to previous page (try the left-arrow keypad key also). .Ip > 8 Go to next page (try the right-arrow keypad key also). .Ip ^ 8 Go to the first page. .Ip $ 8 Go to the last page. .Ip S 8 Set the items the selector displays: threads, subjects or articles. If the group is unthreaded setting this to threads will thread the group. .Ip = 8 Switch between the article selector and the subject/thread selector. .Ip O 8 Pick the order for the items: date, subject, author, item count (for thread/subject groups), and a subject-date grouping of individual articles. Typing the selection in lower-case will sort the articles in the default direction, while using upper-case will reverse the sort. There is a separate default sort order for the subject/thread selector and the article selector. See the .B \-O option to set your favorite selector mode and sort order as the default. .Ip R 8 Reverse the current sort order. .Ip L 8 Switch the selector's display between the long, medium and short display styles. See the .B \-x option to set your favorite style as the default. .Ip U 8 Switch between selecting unread/read articles. .Ip X 8 Mark all unselected articles as read and start reading. .Ip D 8 Mark unselected articles on the current page as read and begin reading if articles are selected, otherwise go to the next page. .Ip J 8 Mark all selected articles as read (useful after performing some action on them with the \*(L':\*(R' command). .Ip c 8 Catch up \*(-- marks ALL articles as read without affecting their cross-posted counterparts. .Ip A 8 Add a subject-search command to the memorized list (a.\|k.\|a. a KILL file) for this group. You are prompted to choose selection (+), junking (j), selection including all replies (.) or junking including all replies (,). If the thread has more than one subject the first subject is the one chosen for the memorized command. .Ip T 8 Add a thread-oriented command to the memorized list for this group. You are prompted to choose selecting the thread (+), junking the thread (j), or clearing the auto-selection/junking for the thread (c). (Note: there are three other options (\*(L'.\*(R', \*(L',\*(R', and \*(L'C\*(R') on the article-reading level \*(-- look there for an explanation of their use.) .Ip ^K 8 Edit the local list of memorized commands (a.\|k.\|a. a KILL file) for this newsgroup. A detailed description of memorized commands is found in the Article Selection section. .Ip ":command" 8 Apply a command to all the selected threads or their selected articles. You can also use ":E" to end a binary extraction or ":p" to post a new article. Use \*(L"::command\*(R" to apply it to all non-selected threads/articles. .Sp Applicable commands include \*(L'+\*(R'/\*(L'-\*(R' (select/deselect an article), \*(L"++\*(R"/\*(L"--\*(R" (select/deselect a thread), \&\*(L"T+\*(R" (auto-select the entire thread), \*(L"Tj\*(R" (auto-junk the entire thread), \*(L't\*(R' (display article tree), \*(L"s dest\*(R" (save article to a destination), \*(L"e dir\*(R" (extract to directory), \*(L'E\*(R' (end partial uudecode), as well as: S, |, w, W, m, M, j, = and \*(L',\*(R'. .Ip ":.command" 8 Apply a command to the current thread or the selected articles in the current thread. Use \*(L"::.command\*(R" to apply a command to the unselected articles in the current thread. .Ip "/pattern" 8 Scan all articles for a subject containing .I pattern and select it. .Ip "/pattern/modifiers:command{:command}" 8 Apply the commands listed to articles matching the search command (possibly with h, a, b, r, or K modifiers). The default action, if no command is specified, is to select the article's item in the selector (e.\|g. the entire thread (\*(L"++\*(R") in the thread selector). See the section on Regular Expressions and the description of pattern searching in the Article Selection section. .Sp One example: to scan all the unread articles looking for \*(L"topic\*(R" anywhere in the article and then select its group and save the articles to the files topic.1, topic.2, etc. use \*(L"/topic/a:++:s topic.%#\*(R". .Ip N 8 Go to the next newsgroup with unread news. .Ip P 8 Go to the previous newsgroup with unread news. .Ip & 8 Display or set the current status of command-line switches. .Ip && 8 Display or set the current macro definitions. .Ip !command 8 Escape to a subshell. .Ip q 8 Quit this group. .Ip "ESC,+" 8 Quit the selector to the article level. Note: ESC won't work if trn has mapped your arrow keys with default macros and the first character that your arrow keys send is an ESC. .Ip Q 8 Quit the current newsgroup and return to the newsgroup-selection prompt for this group. .Sh "Article-Reading Level" On the article-reading level, .I trn displays unread articles in thread sequence (reading each article and its replies before going on to another topic) unless threads are disabled for a particular group, in which case the default order is the order they arrived at your site (numeric sequence). In either case if you use the subject-search command (^N) you will switch to reading the articles in date order within each matching subject. (Making selections in the subject selector or using the .B \-S switch will automatically turn subject search mode on in an unthreaded group.) .Sp On the article-reading level you are .I not asked whether you want to read an article before the article is displayed; rather, .I trn simply displays the first page (or portion of a page, at low baud rates) of an article and asks if you want to continue. The normal article-reading prompt comes at the END of an article (although article-reading commands can also be given from within the middle of an article in addition to the pager level commands). The prompt at the end of an article looks like this: .Sp End of article 248 (of 257) \*(-- what next? [npq] .Sp The following are the options at this point: .Ip n,SP 8 Scan forward for next unread article. (Note: the \*(L'n\*(R' (next) command when typed at the end of an article does not mark the article as read, since an article is automatically marked as read after the last line of it is printed. It is therefore possible to type a sequence such as \*(L'mn\*(R' and leave the article marked as unread. The fact that an article is marked as read by typing n, N, ^N, e, s, S, |, w, or W within the MIDDLE of the article is in fact a special case.) .Ip N 8 Go to the next article. .Ip ^N 8 Find the next article with the same subject in date order. This also makes subject search mode (^N) the default command at the end of an article. .Ip p 8 Scan backward for previous unread article. If there is none, stay at the current article. .Ip P 8 Go to the previous article. .Ip \- 8 Go to the previously displayed article (regardless of whether that article is before or after this article in the normal sequence). .Ip ^P 8 Find the previous article with the same subject in date order. Makes subject search mode (^N) the default. .Ip _N 8 Go to the next article in numeric sequence. .Ip _P 8 Go to the previous article in numeric sequence. .Ip "<, >" 8 Browse the previous/next selected thread/subject. If no selections have been made, all the threads that had unread news when you entered the newsgroup (or last left the selector) are treated as selected. Entering an empty newsgroup makes all the already-read threads available for browsing. .Ip "[, ]" 8 Proceed to the left/right in the article tree. Visits already-read articles as well as empty nodes. Try using the left-/right-arrow keys also. .Ip "{, }" 8 Go to the root/leaf of the article tree, even if the node is already read or empty. Proceeds to the very first/last node if you're already at a root/leaf in a multi-root thread. .Ip "(, )" 8 Go to the previous/next sibling in the thread, including \*(L"cousin\*(R" siblings. Try using the up-/down-arrow keys also. .Ip t 8 Display the entire article tree and all its associated subjects. If the group is not currently threaded, it will become threaded to process this command. .Ip ^R 8 Restart the current article. .Ip v 8 Restart the current article verbosely, displaying the entire header. .Ip ^L 8 Refresh the screen. .Ip ^X 8 Restart the current article, and decrypt as a rot13 message. .Ip X 8 Refresh the screen, and decrypt as a rot13 message. .Ip b 8 Back up one page. .Ip ^E 8 Display the last page of the article. .Ip q 8 Quit this newsgroup and go back to the newsgroup-selection level. .Ip ^ 8 Go to the first unread article. .Ip $ 8 Go to the last article (actually, one past the last article). .Ip "number" 8 Go to the numbered article. .Ip _C 8 Switch to next available charset conversion. .Ip "range{,range}:command{:command}" 8 Apply a set of commands to a set of articles. A range consists of either
or \-. A dot \*(L'.\*(R' represents the current article, and a dollar sign \*(L'$\*(R' represents the last article. .Sp Applicable commands include \*(L'm\*(R' (mark as unread), \*(L'M\*(R' (mark as read-until-exit), \*(L'j\*(R' (mark as read), \*(L"s dest\*(R" (save to a destination), \*(L"e dir\*(R" (extract to directory), \&\*(L"!command\*(R" (shell escape), \*(L"=\*(R" (print the subject), \&\*(L'+\*(R'/\*(L'-\*(R' (select/deselect the article), \&\*(L'T+\*(R' (auto-select the entire thread), \*(L'Tj\*(R' (auto-junk the entire thread), \*(L"++\*(R"/\*(L"--\*(R" (select/deselect the associated thread), \*(L'C\*(R' (cancel), as well as S, |, w, W, and t. .Ip ":command" 8 Apply a command to all the selected threads or their selected articles. Use \*(L"::command\*(R" to apply it to all non-selected threads/articles. For applicable commands, see the discussion above for the range command. .Ip ":.command" 8 Apply a command to the current thread or the selected articles in the current thread. Use \*(L"::.command\*(R" to apply a command to the unselected articles in the current thread. .Ip j 8 Junk the current article (i.\|e. mark it as read). If this command is used from within an article, you are left at the end of the article, unlike \*(L'n\*(R', which looks for the next article. .Ip m 8 Mark the current article as still unread. (If you don't want to see this article for a while you're probably better off using M instead of m, otherwise this article might get picked again as the first available article sooner than you'd like.) .Ip M 8 Mark the current article to return on newsgroup exit. Until then, the current article will be marked as read. This is useful for returning to an article in another session. .Ip Y 8 Yank back the marked-to-return articles, clearing their to-return status. If you are reading selected articles, the yanked articles come back selected. .Ip /pattern 8 Scan forward for article containing .I pattern in the subject. See the Regular Expressions section. Together with the escape substitution facility described later, it becomes easy to search for various attributes of the current article, such as subject, article ID, author name, etc. The previous pattern can be recalled with ESC. If .I pattern is omitted, the previous pattern is assumed. .Ip /pattern/f 8 Scan forward for article containing .I pattern in the from line. If you are using thread files the article data you are matching against MAY contain only the real name of the user. If you want to always match the full from line, see the following header-matching option that will read in the full from-line data if it is not already available. .Ip /pattern/Hheader 8 Scan forward for article containing .I pattern in the indicated .I header. Because we scan the entire string up to the end of the modifiers, this modifier must be the last one. For example, \*(L"/jsmoe@somesite.com/rHfrom:m+\*(R" will mark all articles from \*(L"jsmoe@somesite.com\*(R" as unread and select them. Note that if the header line isn't one that trn recognizes in its header parser, you'll have to use the following full-header matching. .Ip /pattern/h 8 Scan forward for an article containing .I pattern in the header. .Ip /pattern/a 8 Scan forward for an article containing .I pattern anywhere in the article. .Ip /pattern/b 8 Scan forward for an article containing .I pattern in the body of the article, but not the signature. (The signature must be properly delimited to be ignored, however.) .Ip /pattern/B 8 Scan forward for an article containing .I pattern anywhere in the body of the article. .Ip /pattern/r 8 Scan read articles also. .Ip /pattern/c 8 Make search case sensitive. Ordinarily upper- and lower-case are considered the same. .Ip /pattern/t 8 Force the search to start at the top of the group (useful while reading the group, since the default is to start at the current article). .Ip /pattern/I 8 Force the search to ignore the THRU line when executed as a memorized command. If the command portion is a selection command (i.\|e. it starts with a \*(L'+\*(R' or a \*(L'.\*(R') this is the default behavior. .Ip /pattern/N 8 Force the search to NOT ignore the THRU line when executed as a memorized command (useful on selection commands -- see also \-k). .Ip "/pattern/modifiers:command{:command}" 8 Apply the commands listed to articles matching the search command (possibly with h, a, b, or r modifiers). Applicable commands include \*(L'm\*(R' (mark as UNread), \*(L'M\*(R' (mark as read-until-exit), \*(L'j\*(R' (junk -- mark as read in all groups), \&\*(L'x\*(R' (mark as read in this group), \&\*(L"s dest\*(R" (save to a dest), \*(L"e dir\*(R" (extract to dir), \&\*(L"!command\*(R" (shell escape), \*(L"=\*(R" (print the subject), \&\*(L'+\*(R' (select the article), \*(L'-\*(R' deselect the article, \&\*(L'T+\*(R' (auto-select the entire thread), \*(L'Tj\*(R' (auto-junk the entire thread), \*(L"++\*(R" (select the associated thread), \&\*(L"--\*(R" deselect the associated thread), and \*(L'C\*(R' (cancel). If the first command is \*(L'm\*(R' or \*(L'M\*(R', modifier r is assumed. A K may be included in the modifiers (not the commands) to cause the entire command (sans K) to be saved to the local KILL file, where it will be applied to every article that shows up in the newsgroup. .Sp For example, to save all articles in a given newsgroup to the line printer and mark them read, use \*(L"/^/\||\|lpr:j\*(R". If you type \*(L"/^/K\||\|lpr:j\*(R", this will happen every time you enter the newsgroup. .Ip ?pattern 8 Scan backward for article containing .I pattern in the subject. May be modified as the forward search is: ?pattern?modifiers[:commands]. It is likely that you will want an r modifier when scanning backward. .Ip k 8 Mark as read all articles with the same subject as the current article. (Note: there is no single character command to temporarily mark as read (M command) articles matching the current subject. That can be done with \*(L"/s/M\*(R", however.) .Ip \, 8 Mark the current article and all its replies as read. .Ip J 8 Junk all the articles in the current thread, even if it contains multiple subjects. .Ip A 8 Add a subject-search command to the memorized list for this group (in the KILL file). You are prompted to choose selection (+), junking (j), selection including all replies (.) or junking including all replies (,). .Ip K 8 This is a synonym for the command \*(L"Aj\*(R" which adds a command to junk the current subject to the memorized commands for the group. See also the K modifier on searches above. .Ip T 8 Add a thread-oriented command to the memorized list for this group. You are prompted to choose selection of entire thread (+), junking of entire thread (j), selection of an article and its replies (.), junking of an article and its replies (,), clearing the auto-selection/junking for this thread (c), or clearing the auto-selection/junking for an article and its replies (C). .Ip ^K 8 Edit the local list of memorized commands (a.\|k.\|a. a KILL file) for this newsgroup. Each line of the KILL file is either a subject-affecting command of the form /pattern/x or a thread-affecting command of the form Tx. The first line in the KILL file has the form \*(L"THRU \*(R", which tells .I trn the maximum article number that the KILL file has been applied to. The THRU value is usually only used to keep header or article searches from happening multiple times. Subject and from-line searches are quite fast if the group has cached data around (e.\|g. a .\|thread or .\|overview file). If it doesn't, the THRU line is used to set a lower boundary on the search to keep the startup time as short as possible. If trn skipped some selections (or you're not sure), wait for the group to finish being cached (e.\|g. visiting the selector forces the caching of all unread articles), quit the group, and re-enter. .Sp To see only newgroup articles in the control newsgroup, for instance, you might include the line .Sp /newgroup/:+ .Sp which selects all subjects containing \*(L"newgroup\*(R". You can add lines automatically via the A and T commands as well as the K search modifier, but editing is the only way to remove subject commands (thread commands die automatically as the thread dies). If either of the environment variables VISUAL or EDITOR is set, the specified editor will be invoked; otherwise a default editor (normally vi) is invoked on the KILL file. .Sp The KILL file may also contain switch-setting lines beginning with \*(L'&\*(R' (see the section on \*(L"Options\*(R") and special commands beginning with \*(L'*\*(R'. There are two such commands at the moment: \*(L"*j\*(R" (junk all articles from THRU to the end of the group) and \*(L"*X\*(R" (junk all .I unselected articles from THRU to the end of the group). Additionally, any line beginning with \*(L'X\*(R' is executed on exit from the newsgroup rather than on entrance. This can be used to set switches back to a default value. One use for this capability is to set your save directory to a custom value upon entry to a newsgroup and set it back on exit using the \-ESAVEDIR option. See also the .B \-/ option for another solution to multiple save directories without using KILL files. .Ip r 8 Reply through net mail. The environment variables MAILPOSTER and MAILHEADER may be used to modify the mailing behavior of .I trn (see the environment section). If the current article does not exist (such as the \*(L"End of newsgroup\*(R" pseudo-article you can get to with a \*(L'$\*(R' command), invokes the mailer to nobody in particular. .Ip R 8 Reply, including the current article in the header file generated. (See \*(L'F\*(R' command below). The YOUSAID environment variable controls the format of the attribution line. .Ip ^F 8 Forward the current article. .Ip f 8 Submit a follow-up article. If the current article does not exist (such as the \*(L"End of newsgroup\*(R" pseudo-article you can get to with a \*(L'$\*(R' command), posts an original (root) article. .Ip F 8 Submit a follow-up article, and include the old article, with lines prefixed either by \*(L">\*(R" or by the argument to the .B \-F switch. .I Trn will attempt to provide an attribution line in front of the quoted article, generated from the From: line of the article. Unfortunately, the From: line doesn't always contain the right name; you should double check it against the signature and change it if necessary, or you may have to apologize for quoting the wrong person. The environment variables NEWSPOSTER, NEWSHEADER and ATTRIBUTION may be used to modify the posting behavior of .I trn (see environment section). .Ip C 8 Cancel the current article, but only if you are the contributor or superuser. .Ip z 8 Supersede the current article, but only if you are the contributor. .Ip Z 8 Same as the \*(L'z\*(R' command, but you start with a copy of the original article to work with. .Ip c 8 Catch up in this newsgroup; i.\|e., mark all articles as read. .Ip U 8 Mark some or all articles as unread. You can choose to mark the current thread, sub-thread (the current article and its replies), all the articles, or start up the selector to choose specific articles to set unread. .Ip u 8 Unsubscribe from this newsgroup. .Ip "s destination" 8 Save to a filename or pipe using sh. If the first character of the destination is a vertical bar, the rest of the command is considered a shell command to which the article is passed through standard input. The command is subject to filename expansion. (See also the environment variable PIPESAVER.) If the destination does not begin with a vertical bar, the rest of the command is assumed to be a filename of some sort. An initial tilde \*(L'~\*(R' will be translated to the name of the home directory, and an initial environment variable substitution is also allowed. If only a directory name is specified, the environment variable SAVENAME is used to generate the actual name. If a non-absolute filename is specified, the environment variable SAVEDIR will be used to generate the actual directory. If nothing is specified, then obviously both variables will be used. Since the current directory for .I trn while doing a save command is your private news directory, typing \*(L"s ./filename\*(R" will force the file to your news directory. Save commands are also run through % interpretation, so that you can enter \*(L"s %O/filename\*(R" to save to the directory you were in when you ran .IR trn , and \*(L"s %t\*(R" to save to a filename consisting of the Internet address of the sender. .Sp After generating the full pathname of the file to save to, .I trn determines if the file exists already, and if so, appends to it. .I trn will attempt to determine if an existing file is a mailbox or a normal file, and save the article in the same format. If the output file does not yet exist, .I trn will by default ask you which format you want, or you can make it skip the question with either the .B \-M or .B \-N switch. If the article is to be saved in mailbox format, the command to do so is generated from the environment variable MBOXSAVER. Otherwise, NORMSAVER is used. .Ip "S destination" 8 Save to a filename or pipe using a preferred shell, such as csh. Which shell is used depends first on what you have the environment variable SHELL set to, and in the absence of that, on what your news administrator set for the preferred shell when he or she installed .IR trn . .Ip "| command" 8 Shorthand for \*(L"s | command\*(R". .Ip "w destination" 8 The same as \*(L"s destination\*(R", but saves without the header. .Ip "W destination" 8 The same as \*(L"S destination\*(R", but saves without the header. .Ip "e directory" 8 Extract a shell archive or uuencoded binary to the designated directory. The article is first scanned to try discover what type of data is encapsulated. If a \*(L"cut here\*(R" line is found, the first non-blank line after it must be either the start of a shar header, or the \*(L"begin\*(R" or \*(L"table\*(R" line of a uuencoded binary. The default for extracting shars is to send the data portion of the file to /bin/sh, but that can be overridden with the UNSHAR variable (see the ENVIRONMENT section). Uudecoding is done internally by a decoder that can handle the data being split up over multiple articles, and extracted one piece at a time. To decode a multi-article file, either execute the \*(L'e\*(R' command in each article in sequence, use an article range to execute the command, or use the \*(L":e\*(R" command to repeat the command for each of the currently selected articles. When the \*(L'e\*(R' command is not followed by any arguments, it will repeat the arguments from the last extraction. All directory specifications are relative to the value of SAVEDIR, so you can use the command \*(L"e .\*(R" to force an extraction to SAVEDIR itself. If a uudecoding is in progress (i.\|e. the last piece wasn't extracted yet) and you exit the group, the partial file will be removed. This also occurs if you start to extract a new uuencoded file before the previous one was finished. See also the \*(L'E\*(R' command for ending a multi-part uudecoding manually. .Sp There is one special case that is handled differently: if the first file in a recognizable shar file is a uuencoded binary that was packed with lines starting with an \*(L'X\*(R', we will not unshar the file but instead uudecode it. If this causes problems, you can override the default extraction method by following the directory with an explicit command to execute, as described below. .Ip "e directory|command" 8 This form of the \*(L'e\*(R' command allows you to extract other data formats than shar or uuencoded files or to override the decisions made by the automatic extraction selection described above. In normal operation, all data following what we recognize as a \*(L"cut here\*(R" line will be sent to the specified command. Additionally, the distinctive beginning of a shell archive is also recognized without a preceding cut line. When the command is run, the default directory will be set to the specified directory, or the value of SAVEDIR if unspecified. Entering the \*(L'e\*(R' command without arguments will repeat your previous extract command. You can use the command \*(L"e dir|\*(R" to extract to a new directory using the previously-specified command. .Ip E 8 This command ends any multi-part uuencoded file extraction that you began, but are unable (or unwilling) to complete. The partially extracted file is removed. .Ip & 8 Print out the current status of command-line switches. .Ip "&switch {switch}" 8 Set additional command-line switches. .Ip && 8 Print out current macro definitions. .Ip "&&keys commands" 8 Define an additional macro. .Ip !command 8 Escape to a subshell. One exclamation mark (!) leaves you in your own news directory. A double exclamation mark (!!) leaves you in the spool directory of the current newsgroup. The environment variable SHELL will be used if defined. If .I command is null, an interactive shell is started. .Sp You can use escape key substitutions described later to get to many run-time values. The command is also run through % interpretation, in case it is being called from a range or search command. .Ip \+ 8 Start the selector in the last-used mode. If the newsgroup is unthreaded and the default selector mode is threads, we temporarily switch to subject selection unless manually overridden. .Ip _a 8 Start the selector in article mode. .Ip _s 8 Start the selector in subject mode. .Ip _t 8 Start the selector in thread mode. .Ip _T 8 Start the selector in thread mode unless the group isn't threaded, in which case we settle for the subject selector. .Ip = 8 List subjects of unread articles. .Ip # 8 Print last article number. .Ip _+ 8 Select the entire thread associated with the current article. .Ip _- 8 Deselect the entire thread associated with the current article. .Sh "Pager Level" At the pager level (within an article), the prompt looks like this: .Sp \*(--MORE\*(--(17%) .Sp and a number of commands may be given: .Ip SP 8 Display next page. .Ip x 8 Display next page and decrypt as a rot13 message. .Ip d 8 Display half a page more. .Ip CR 8 Display one more line. .Ip q 8 Go to the end of the current article (don't mark it either read or unread). Leaves you at the \*(L"What next?\*(R" prompt. .Ip j 8 Junk the current article. Mark it read and go to the end of the article. .Ip ^L 8 Refresh the screen. .Ip X 8 Refresh the screen and decrypt as a rot13 message. .Ip b 8 Back up one page. .Ip ^E 8 Display the last page of the article. .Ip _C 8 Switch to next available charset conversion. .Ip t 8 Display the entire article tree, including its associated subjects, and continue reading. If the group is not currently threaded, it will be threaded first. .Ip gpattern 8 Goto (search forward for) .I pattern within current article. Note that there is no space between the command and the pattern. If the pattern is found, the page containing the pattern will be displayed. Where on the page the line matching the pattern goes depends on the value of the .B \-g switch. By default the matched line goes at the top of the screen. .Ip G 8 Search for g pattern again. .Ip ^G 8 This is a special version of the \*(L'g\*(R' command that is for skipping articles in a digest. It is equivalent to setting \*(L"\-g4\*(R" and then executing the command \*(L"g^Subject:\*(R". .Ip TAB 8 This is another special version of the \*(L'g\*(R' command that is for skipping inclusions of older articles. It is equivalent to setting \*(L"\-g4\*(R" and then executing the command \*(L"g^[^c]\*(R", where \fIc\fR is the first character of the last line on the screen. It searches for the first line that doesn't begin with the same character as the last line on the screen. .Ip !command 8 Escape to a subshell. .PP The following commands skip the rest of the current article, then behave just as if typed to the \*(L"What next?\*(R" prompt at the end of the article. See the documentation at the article selection level for these commands. .Sp # $ & / = ? A c C f F k K T ^K J , m M r R ^R u U v Y ^ .br p P ^P - < > [ ] { } number .br range{,range} command{:command} .Sp The following commands also skip to the end of the article, but have the additional effect of marking the current article as read: .Sp n N ^N e s S | w W .Sp .Sh "Miscellaneous facts about commands" An \*(L'n\*(R' typed at either the \*(L"Last newsgroup\*(R" prompt or a \*(L"Last article\*(R" prompt will cycle back to the top of the newsgroup or article list, whereas a \*(L'q\*(R' will quit the level. (Note that \*(L'n\*(R' does not mean \*(L"no\*(R", but rather \*(L"next\*(R".) A space will of course do whatever is shown as the default, which will vary depending on whether .I trn thinks you have more articles or newsgroups to read. .PP The \*(L'b\*(R' (backup page) command may be repeated until the beginning of the article is reached. If .I trn is suspended (via a ^Z), then when the job is resumed, a refresh (^L) will automatically be done (Berkeley-type systems only). If you type a command such as \*(L'!\*(R' or \*(L's\*(R' which takes you from the middle of the article to the end, you can always get back into the middle by typing \*(L'^L\*(R'. .PP In multi-character commands such as \*(L'!\*(R', \*(L's\*(R', \*(L'/\*(R', etc, you can interpolate various run-time values by typing escape and a character. To find out what you can interpolate, type escape and \*(L'h\*(R', or check out the single character % substitutions for environment variables in the Interpretation and Interpolation section, which are the same. Additionally, typing a double escape will cause any % substitutions in the string already typed in to be expanded. .Sh "The Tree Display" When reading a threaded newsgroup, .I trn displays a character representation of the article tree in the upper right corner of the header. For example, consider the following display: .Sp .nf .in +4n (1)+-(1)--(\fI2\fP)--\fI[2]\fP \h'\w'(1)'u'|-(1)+-<3> \h'\w'(1)'u'|\h'\w'-(1)'u'\\-[1] \h'\w'(1)'u'\\-(1)+-[1]--[1] \h'\w'(1)+-(1)'u'\\-[1] .in -4n .fi .Sp This tree represents an initial article that has three direct replies (the second column with three (1)'s). Each reply has further replies branching off from them. In two cases the subject line was altered in the reply, as indicated by the increasing numbers. .PP The third subject is not selected for reading, as indicated by the <>'s. Note you can always forcefully visit an unselected article with \*(L'N\*(R' and \*(L'P\*(R' as well as the thread-navagation commands (which are typically macro'ed to the arrow keys on your keypad). .PP When there is only one subject associated with a thread, all the nodes are marked with the number 1. When the first subject change arrives, it is marked with the number 2, and so on. If you were to look at this thread in the thread selector, the three subjects associated with it would be listed in the same order as the ascending digits. In those rare cases where more than 9 subjects are associated with each thread, the nodes are marked with the letters A-Z, and then by a-z. .PP The articles that have already been read are enclosed in ()'s, Unread articles are displayed in []'s, and unread-but-unselected articles are displayed in <>'s. The currently displayed article has its entire node highlighted in the display. The previously displayed article has only its number highlighted. If the group has not been completely threaded yet, some articles will appear as (?) until trn can determine if the referenced article truly exists or not. If you visit such an article and wait for trn to finish threading the group, the screen will refresh as soon as the presence or absence of the article is determined. .Sh "Options" .I Trn has a nice set of options to allow you to tailor the interaction to your liking. (You might like to know that the author swears by \*(L"\-x6ms \+e \-mu \-S -XX -N -B -p\*(R".) These options may be set on the command line, via the TRNINIT environment variable, via a file pointed to by the TRNINIT variable, or from within .I trn via the & command. Options may generally be unset by typing \*(L"+switch\*(R". Options include: .TP 5 .B \-a causes trn to always thread the unread articles on entry to a group. Without this option trn may enter a group in a partially-threaded state and process the unthreaded articles in the background. The down side of this is that the tree display may not be complete when it is first displayed and you may start out at an odd position in the first thread's article tree. .TP 5 .B \-A tells trn to attempt to create some default macros that will map your arrow keys to useful trn functions (this is the default). Use .B +A to turn this behavior off. .TP 5 .B \-b will force trn to read each thread in a breadth-first order, rather than depth-first. .TP 5 .B \-B will turn on a spinner that twirls when trn is doing background article-processing. A gizmo for those interested in what's going on behind the scenes. .TP 5 .B \-c checks for news without reading news. If a list of newsgroups is given on the command line, only those newsgroups will be checked; otherwise all subscribed-to newsgroups are checked. Whenever the .B \-c switch is specified, a non-zero exit status from .I trn means that there is unread news in one of the checked newsgroups. The .B \-c switch does not disable the printing of newsgroups with unread news; this is controlled by the .B \-s switch. (The .B \-c switch is not meaningful when given via the & command.) .TP 5 .B \-C tells .I trn how often to checkpoint the .IR .newsrc , in articles read. Actually, this number says when to start thinking about doing a checkpoint if the situation is right. If a reasonable check-pointing situation doesn't arise within 10 more articles, the .I .newsrc is check-pointed willy-nilly. .TP 5 .B \-d sets your private news directory to something other than ~/News. The directory name will be globbed (via csh) if necessary (and if possible). The value of SAVEDIR (where articles are saved) is initially set to this directory, but is often manipulated via the .B \-/ option or by manipulating SAVEDIR directly (perhaps via the memorized commands (the KILL file) for a group. Any KILL files (see the K command in the Article Selection section) also reside in this directory and its subdirectories, by default. In addition, shell escapes leave you in this directory. .TP 5 .B \-D enables debugging output. See common.h for flag values. Warning: normally .I trn attempts to restore your .I .newsrc when an unexpected signal or internal error occurs. This is disabled when any debugging flags are set. .TP 5 .B \-e causes each page within an article to be started at the top of the screen, not just the first page. (It is similar to the .B \-c switch of .IR more (1).) You never have to read scrolling text with this switch. This is helpful especially at certain baud rates because you can start reading the top of the next page without waiting for the whole page to be printed. It works nicely in conjunction with the .B \-m switch, especially if you use half-intensity for your highlight mode. See also the .B \-L switch. .TP 5 .B \-E= sets the environment variable to the value specified. Within .IR trn , \*(L"&\-ESAVENAME=%t\*(R" is similar to \*(L"setenv SAVENAME '%t'\*(R" in .IR csh , or \*(L"SAVENAME='%t'; export SAVENAME\*(R" in .IR sh . Any environment variables set with .B \-E will be inherited by subprocesses of .IR trn . .TP 5 .B \-f will make trn avoid various sleep calls and the prompt after the processing of the memorized commands that are intended to allow you time to read a message before the screen clears. This allows the advanced user to cruise along a little faster at the expense of readability. The \-t (terse) option turns on -f by default, but you can override this by specifying \+f after the \-t option. .TP 5 .B \-F sets the prefix string for the \*(L'F\*(R' follow-up command to use in prefixing each line of the quoted article. For example, \*(L"\-F\*(R" inserts a tab on the front of each line (which will cause long lines to wrap around, unfortunately), \*(L"\-F>>>>\*(R" inserts \*(L">>>>\*(R" on every line, and \*(L"\-F\*(R" by itself causes nothing to be inserted, in case you want to reformat the text, for instance. The initial default prefix is \*(L">\*(R". .TP 5 .B \-g tells .I trn which line of the screen you want searched-for strings to show up on when you search with the \*(L'g\*(R' command within an article. The lines are numbered starting with 1. The initial default is \*(L"\-g1\*(R", meaning the first line of the screen. Setting the line to less than 1 or more than the number of lines on the screen will set it to the last line of the screen. .TP 5 .B \-G selects the "fuzzy" processing on the go command when you don't type in a valid group name. With this option on trn will attempt to find the group you probably meant to type, but it can be a little slow about it, so it's not on by default. .TP 5 .B \-h hides (disables the printing of) all header lines beginning with .I string that trn recognizes. For instance, \-hexp will disable the printing of the \*(L"Expires:\*(R" line. Case is insignificant. Headers unknown to trn cannot be disabled except by turning them all off (\-h without a string will disable all headers except the Subject) and then using .B +h to select those lines you want to see. You may wish to use the baud-rate switch modifier below to hide more lines at lower baud rates. .TP 5 .B \-H works just like .B \-h except that instead of setting the hiding flag for a header line, it sets the magic flag for that header line. Certain header lines have magic behavior that can be controlled this way. At present, the following actions are caused by the flag for the particular line: the Date line prints the date in local time if the group is threaded; the From line will only print the commented portion of the user name; the Newsgroups line will only print when there are multiple newsgroups; the Subject line will be underlined and (when threaded) the keyword \*(L'Subject:\*(R' is replaced by its subject number (e.\|g. [1]); and the Expires line will always be suppressed if there is nothing on it. In fact, all of these actions are the default, and you must use .B +H to undo them. .TP 5 .B \-i= specifies how long (in lines) to consider the initial page of an article \*(-- normally this is determined automatically depending on baud rate. (Note that an entire article header will always be printed regardless of the specified initial page length. If you are working at low baud rate and wish to reduce the size of the headers, you may hide certain header lines with the .B \(bsh switch.) .TP 5 .B \-I tells trn to append all new, unsubscribed groups to the end of the .newsrc. .TP 5 .B \-j forces trn to leave control characters unmolested in messages. .TP 5 .B \-J{} causes trn to join similar subjects into a common thread if they are the same up to the indicated number of characters (the default is 30). You can turn this on and off for specific groups by putting the following lines into your kill file for the group(s): .Sp &-J30 .br X&+J .TP 5 .B \-k tells trn to ignore the THRU line when processing selection searches (i.\|e. searches with a command portion that starts with a \*(L'+\*(R' or a \*(L'.\*(R') in the memorized commands (aka kill files). This is turned on by default, so use .B +k if you want to turn it off. .TP 5 .B \-K is used to keep a trn from checking for new news while you're in the group. Use this when your kill-file processing is so slow that you don't want the group to expand while you're reading. If you only want specific groups to be affected, put these lines into your kill file for the group(s): .Sp &-K .br X&+K .TP 5 .B \-l disables the clearing of the screen at the beginning of each article, in case you have a bizarre terminal. .TP 5 .B \-L tells .I trn to leave information on the screen as long as possible by not blanking the screen between pages, and by using clear to end-of-line. (The .IR more (1) program does this.) This feature works only if you have the requisite termcap capabilities. The switch has no effect unless the .B \-e switch is set. .TP 5 .B \-m= enables the marking of the last line of the previous page printed, to help the user see where to continue reading. This is most helpful when less than a full page is going to be displayed. It may also be used in conjunction with the .B \-e switch, in which case the page is erased, and the first line (which is the last line of the previous page) is highlighted. If .B \-m=s is specified, the standout mode will be used, but if .B \-m=u is specified, underlining will be used. If neither .B =s or .B =u is specified, standout is the default. Use .B +m to disable highlighting. .TP 5 .B \-M forces mailbox format in creating new save files. Ordinarily you are asked which format you want. .TP 5 .B \-N forces normal (non-mailbox) format in creating new save files. Ordinarily you are asked which format you want. .TP 5 .B \-o will act like old versions of trn and not junk cross-referenced articles when using thread commands to junk articles in the current group (such as the selector's \*(L'X\*(R' command). .TP 5 .B \-O{} specifies the selector's mode and (optionally) the sort order. The modes are \*(L'a\*(R'rticle, \*(L's\*(R'ubject, or \*(L't\*(R'hread. The orders are \*(L'd\*(R'ate, \*(L's\*(R'ubject, \*(L'a\*(R'uthor, article \*(L'c\*(R'ount per group, \*(L'n\*(R'umeric, or subject-date \*(L'g\*(R'roups. The order can be capitalized to reverse the indicated order. For example, to choose the article selector in subject order specify \*(L"-Oas\*(R". .TP 5 .B \-p{opt} tells trn to auto-select your postings and their replies as it encounters them in the various groups you read. The optional parameter is either a \*(L'.\*(R', \*(L'p\*(R', or \*(L'+\*(R' (it defaults to \*(L'.\*(R' if omitted) and affects what command trn should execute when it encounters your postings. The default is to execute the command \*(L"T.\*(R" on each of your postings which tells trn to memorize the auto-selection of this article and all its replies. Using \-pp tells trn to use the same command, but start the selection with the parent article, so that you see any other replies to the same article. Using \-p+ tells trn to select the whole thread that contains your reply. .TP 5 .B \-q bypasses the automatic check for new newsgroups when starting .IR trn . .TP 5 .B \-Q defines the set of available charset conversions. This can be useful to restrict the available conversions to those your terminal can handle and/or to specify an alternate default. The first element of this set is taken as default for each article. .TP 5 .B \-r causes .I trn to restart in the last newsgroup read during a previous session with .IR trn . It is equivalent to starting up normally and then getting to the newsgroup with a g command. .TP 5 .B \-s with no argument suppresses the initial listing of newsgroups with unread news, whether .B \-c is specified or not. Thus .B \-c and .B \-s can be used together to test \*(L"silently\*(R" the status of news from within your .I .login file. If .B \-s is followed by a number, the initial listing is suppressed after that many lines have been listed. Presuming that you have your .I .newsrc sorted into order of interest, .B \-s5 will tell you the 5 most interesting newsgroups that have unread news. This is also a nice feature to use in your .I .login file, since it not only tells you whether there is unread news, but also how important the unread news is, without having to wade through the entire list of unread newsgroups. If no .B \-s switch is given .B \-s5 is assumed, so just putting \*(L"rn \-c\*(R" into your \&.login file is fine. .TP 5 .B \-S causes .I trn to enter subject search mode (^N) automatically whenever an unthreaded newsgroup is started up with unread articles or more. Additionally, it causes any \*(L'n\*(R' typed while in subject search mode to be interpreted as \*(L'^N\*(R' instead. (To get back out of subject search mode, the best command is probably \&\*(L'^\*(R'.) If is omitted, 3 is assumed. .TP 5 .B \-t puts .I trn into terse mode. This is more cryptic but useful for low baud rates. (Note that your system administrator may have compiled .I trn with either verbose or terse messages only to save memory.) You may wish to use the baud-rate switch modifier below to enable terse mode only at lower baud rates. .TP 5 .B \-T allows you to type ahead of trn. Ordinarily trn will eat typeahead to prevent your autorepeating space bar from doing a very frustrating thing when you accidentally hold it down. If you don't have a repeating space bar, or you are working at low baud rate, you can set this switch to prevent this behavior. You may wish to use the baud-rate switch modifier below to disable typeahead only at lower baud rates. .TP 5 .B \-u sets the unbroken-subject-line mode in the selector, which simply truncates subjects that are too long instead of dumping the middle portion prior to the last two words of the subject. .TP 5 .B \-U tells trn to not write the .newsrc file out after visiting each group. While this is \*(L"unsafe\*(R" it can be faster if you have a really huge .newsrc. .TP 5 .B \-v sets verification mode for commands. When set, the command being executed is displayed to give some feedback that the key has actually been typed. Useful when the system is heavily loaded and you give a command that takes a while to start up. .TP 5 .B \-V will output trn's version number and quit. .TP 5 .B \-x{}{} Enable the extended (threaded) features of .I trn beyond the .I rn compatibility mode (this may be the default on your system, use +x if you yearn for the good ol' days). The is the maximum number of article-tree lines (from 0 to 11) you want displayed in your header. Use the to choose which thread selector styles you like (\*(L's\*(R'hort, \*(L'm\*(R'edium, or \*(L'l\*(R'ong), and in what order they are selected with the \*(L'L\*(R' command. For example, use .B \-xms to start with the medium display mode and only switch between it and the short mode. You can omit either or both of the parameters, in which case a default of .B \-x6lms is assumed. .TP 5 .B \-X{}{} If you like using the selector, you'll probably want to use this option to make the selector command (+) the default when a newsgroup is started up with at least unread articles. (Your installer may have chosen to make -X1 the default on your system.) It is also used to select which commands you want to be the defaults while using the thread selector. For example, .B \-X2XD will make the thread selector the default command for entering a newsgroup with at least 2 unread articles, and set the default command for the LAST page of the thread selector to be the .B X command and the default command for all other pages to be the .B D command. Either or both parameters can be omitted, as well as the second default command (e.\|g. .B \-XX would change the default newsgroup entry to use the selector and the default command for the last page of the selector to be \*(L'X\*(R'). The default is .B \-X1Z> if just .B \-X is specified. To set the default selector commands without having \*(L'+\*(R' be the default entry into a newsgroup, specify a high number, like 9999. .TP 5 .B \-z sets the minimum number of minutes that must elapse before the active file is refetched to look for new articles. A value of 0 or using +z turns this off. .TP 5 .B \-Z is used to select what style of database you want trn to access. Use \-Zt for thread files, \-Zo for overview files, and +Z for none. The default is whatever your newsadmin compiled into trn, and can be \-Zot to try to access either one. .TP 5 .B \-/ sets SAVEDIR to \*(L"%p/%c\*(R" and SAVENAME to \*(L"%a\*(R", which means that by default articles are saved in a subdirectory of your private news directory corresponding to the name of the the current newsgroup, with the filename being the article number. .B +/ sets SAVEDIR to \*(L"%p\*(R" and SAVENAME to \*(L"%^C\*(R", which by default saves articles directly to your private news directory, with the filename being the name of the current newsgroup, first letter capitalized. (Either .B +/ or .B \-/ may be default on your system, depending on the feelings of your news administrator when he, she or it installed .IR trn .) You may, of course, explicitly set SAVEDIR and SAVENAME to other values \*(-- see discussion in the environment section. .PP Any switch may be selectively applied according to the current baud-rate. Simply prefix the switch with +speed to apply the switch at that speed or greater, and \%\-speed to apply the switch at that speed or less. Examples: \%\-1200\-hposted suppresses the Posted line at 1200 baud or less; \%+9600\-m enables marking at 9600 baud or more. You can apply the modifier recursively to itself also: \%+300\-1200\-t sets terse mode from 300 to 1200 baud. .PP Similarly, switches may be selected based on terminal type: .Sp \-=vt100+T set +T on vt100 .br \-=tvi920\-ETERM=mytvi get a special termcap entry .br \-=tvi920\-ERNMACRO=%./.rnmac.tvi .br set up special key-mappings .br +=paper\-v set verify mode if not hardcopy .PP Some switch arguments, such as environment variable values, may require spaces in them. Such spaces should be quoted via ", ', or \e in the conventional fashion, even when passed via TRNINIT or the & command. .Sh "Regular Expressions" The patterns used in article searching are regular expressions such as those used by .IR ed (1). In addition, \ew matches an alphanumeric character and \eW a non-alphanumeric. Word boundaries may be matched by \eb, and non-boundaries by \eB. The bracketing construct \e(\ ...\ \e) may also be used, and \edigit matches the digit'th substring, where digit can range from 1 to 9. \e0 matches whatever the last bracket match matched. Up to 10 alternatives may given in a pattern, separated by \e|, with the caveat that \e(\ ...\ \e|\ ...\ \e) is illegal. .Sh "Characterset conversions" .I trn can use character set conversions when displaying articles. This helps users in non-English-speaking countries to display special characters on 7-bit displays. .I trn assumes that articles use the ISO-8859-1 character set and converts the special characters (e.g., \*(L"umlauts\*(R") to a string of ASCII characters. Currently the following conversions are supported (see the .I \-Q option): .TP 5 .B p Plain. No change. This is the default. .TP 5 .B a ISO->ASCII. Special characters are mapped to ASCII, e.g. the umlaut-o character becomes oe. .TP 5 .B m ISO->ASCII monospaced. Special characters are mapped to exactly one similar-looking ASCII character, e.g. umlaut-o becomes o. Used where correct spacing is more important than accuracy. .TP 5 .B t TeX->ISO. Assuming your display can handle the ISO-8859-1 charset, .I trn transforms umlauts in the TeX notation, which is commonly used in Germany, to real ISO characters, e.g. "a becomes umlaut-a. .PP The selected conversion, if different from .BR p , will be displayed in the article level and pager prompt. The conversion is also used when including original articles in a reply or followup. It is not used when saving articles to files. .Sh "Interpretation and Interpolation" Many of the strings that .I trn handles are subject to interpretations of several types. Under filename expansion, an initial \*(L"~/\*(R" is translated to the name of your home directory, and \*(L"~name\*(R" is translated to the login directory for the user specified. Filename expansion will also expand an initial environment variable, and also does the backslash, caret and percent expansion mentioned below. .PP All interpreted strings go through backslash, caret and percent interpretation. The backslash escapes are the normal ones (such as \en, \et, \e033, etc.). The caret escapes indicate control codes (such as ^i, ^l, etc.). If you wish to pass through a backslash or a caret it must be escaped with a backslash. The special percent escapes are similar to printf percent escapes. These cause the substitution of various run-time values into the string. The following are currently recognized: .Ip %a 8 Current article number. .Ip %A 8 Full name of current article (%P/%c/%a). .Ip %b 8 Destination of last save command, often a mailbox. .Ip %B 8 The byte offset to the beginning of the part of the article to be saved, set by the save command. The \*(L's\*(R' and \*(L'S\*(R' commands set it to 0, and the \*(L'w\*(R' and \*(L'W\*(R' commands set it to the byte offset of the body of the article. .Ip %c 8 Current newsgroup, directory form. .Ip %C 8 Current newsgroup, dot form. .Ip %d 8 Full name of newsgroup directory (%P/%c). .Ip %D 8 \*(L"Distribution:\*(R" line from the current article. .Ip %e 8 The last command executed to extract data from an article. .Ip %E 8 The last directory where an extracted file went. .Ip %f 8 \*(L"From:\*(R" line from the current article, or the \*(L"Reply-To:\*(R" line if there is one. This differs from %t in that comments (such as the full name) are not stripped out with %f. .Ip %F 8 \*(L"Newsgroups:\*(R" line for a new article, constructed from \*(L"Newsgroups:\*(R" and \*(L"Followup-To:\*(R" lines of current article. .Ip %h 8 Name of the header file to pass to the mail or news poster, containing all the information that the poster program needs in the form of a message header. It may also contain a copy of the current article. The format of the header file is controlled by the MAILHEADER and NEWSHEADER environment variables. .Ip %H 8 Host name (your machine's name). .Ip %i 8 \*(L"Message-I.D.:\*(R" line from the current article, with <> guaranteed. .Ip %I 8 The reference indication mark (see the .B \-F switch.) .Ip %l 8 The news administrator's login name, if any. .Ip %L 8 Login name (yours). .Ip %m 8 The current mode of .I trn, for use in conditional macros. .Sp .nf i Initializing. n Newsgroup-selection level. f end (Finis) of newsgroup-selection level. t the Thread/subject/article selector. a Article level (What next?). e End of the article level. p Pager level (MORE prompt). u Set-unread prompt. d selector moDe prompt. o selector Order prompt. m Memorize thread command prompt. r memoRize subject command prompt. k processing memorized (KILL file) commands. A Add this newsgroup? B aBandon confirmation. C Catchup confirmation. D Delete bogus newsgroups? F Is follow-up a new topic? M Use mailbox format? R Resubscribe to this newsgroup? .fi .Sp Note that yes/no questions are all upper-case modes. If, for example, you wanted to disallow defaults on all yes/no questions, you could define the following macro: .Sp .nf \e040 %(%m=[A-Z]?h: ) .fi .Ip %M 8 The number of articles marked to return via the \*(L'M\*(R' command. If the same article is Marked multiple times, \*(L"%M\*(R" counts it multiple times in the current implementation. .Ip %n 8 \*(L"Newsgroups:\*(R" line from the current article. .Ip %N 8 Full name (yours). .Ip %o 8 Organization (yours). .Ip %O 8 Original working directory (where you ran trn from). .Ip %p 8 Your private news directory, normally ~/News. .Ip %P 8 Public news spool directory, normally /usr/spool/news on systems that don't use NNTP. .Ip %q 8 The value of the last \*(L"quoted\*(R" input string (see the %" interp). .Ip %r 8 Last reference on references line of current article (parent article id). .Ip %R 8 References list for a new article, constructed from the references and article ID of the current article. .Ip %s 8 Subject, with all Re's and (nf)'s stripped off. .Ip %S 8 Subject, with one \*(L"Re:\*(R" stripped off. .Ip %t 8 \*(L"To:\*(R" line derived from the \*(L"From:\*(R" and \*(L"Reply-To:\*(R" lines of the current article. This always returns an Internet format address. .Ip %T 8 \*(L"To:\*(R" line derived from the \*(L"Path:\*(R" line of the current article to produce a uucp path. .Ip %u 8 The number of unread articles in the current newsgroup. .Ip %U 8 The number of unread articles in the current newsgroup, not counting the the current article. When threads are selected, this count reflects only selected articles. .Ip %v 8 The number of unselected articles, not counting the current article if it is unselected. .Ip %w 8 The directory where mthreads keeps its tmp files. .Ip %W 8 The directory where thread files are placed. .Ip %x 8 The news library directory. .Ip %X 8 The trn library directory. .Ip %z 8 The length of the current article in bytes. .Ip %Z 8 The number of selected threads. .Ip %~ 8 Your home directory. .Ip %. 8 The directory containing your dot files, which is your home directory unless the environment variable DOTDIR is defined when trn is invoked. .Ip %# 8 The current count for a multi-file save, starting with 1. This value is incremented by one for each file saved or extracted within a single command. .Ip %$ 8 Current process number. .Ip %/ 8 Last search string. .Ip %? 8 A space unless the current interp string is > 79 characters, at which point it turns into a newline. .Ip %% 8 A percent sign. .Ip "%{name} or %{name\-default}" 8 The environment variable \*(L"name\*(R". .Ip %[name] 8 The value of header line \*(L"Name:\*(R" from the current article. The \*(L"Name:\ \*(R" is not included. For example \*(L"%D\*(R" and \*(L"%[distribution]\*(R" are equivalent. The name must be spelled out in full. .Ip %`command` 8 Inserts the output of the command, with any embedded newlines translated to space. .Ip %"prompt" 8 Prints prompt on the terminal, then inputs one string, and inserts it. .Ip "%(test_text=pattern?then_text:else_text)" 8 If .I test_text matches .IR pattern , has the value .IR then_text , otherwise .IR else_text . The \*(L":else_text\*(R" is optional, and if absent, interpolates the null string. The = may be replaced with != to negate the test. To quote any of the meta-characters (\*(L'=\*(R', \*(L'?\*(R', \*(L':\*(R', or \*(L')\*(R'), precede with a backslash. .Ip %digit 8 The digits 1 through 9 interpolate the string matched by the nth bracket in the last pattern match that had brackets. If the last pattern had alternatives, you may not know the number of the bracket you want \*(-- %0 will give you the last bracket matched. .PP Modifiers: to capitalize the first letter, insert \*(L'^\*(R': \*(L"%^C\*(R" produces something like \*(L"Rec.humor\*(R". Inserting \*(L'_\*(R' causes the first letter following the last \&\*(L'/\*(R' to be capitalized: \*(L"%_c\*(R" produces \*(L"rec/Humor\*(R". .PP Inserting \*(L'\\\*(R' will insert a backslash before any characters that would be magic in a regular expression, including \*(L'%\*(R': \*(L"%\\C\*(R" produces \*(L"rec\\.humor\*(R". .PP Inserting \*(L"'\*(R" will insert a backslash before any single-quotes in the result, suitable for enclosing in single-quotes and sending to a shell: \*(L"'%'s'\*(R" might produce \*(L"'I'\\''m a subject'\*(R". .PP Inserting \*(L"''\*(R" will insert a backslash before any double-quotes in the result, suitable for enclosing in double-quotes and sending to a shell. .PP Inserting \*(L">\*(R" will strip out just the address portion of an address string such as the From line. .PP Inserting \*(L")\*(R" will strip out just the comment (real name) portion of an address string such as the From line. .PP Inserting \*(L":FMT\*(R" will format the result according to the printf-style FMT string: \*(L"%:-50.50s\*(R" left-justifies the subject into a 50 character field. .SH ENVIRONMENT The following environment variables are paid attention to by .IR trn . In general the default values assumed for these variables by .I trn are reasonable, so if you are using .I trn for the first time, you can safely ignore this section. Note that the defaults below may not correspond precisely to the defaults on your system. To find the actual defaults you would need to look in config.h and common.h in the trn source directory, and the file INIT in the trn library directory. .PP Those variables marked (%) are subject to % interpolation, and those marked (~) are subject to both % interpolation and ~ interpretation. .Ip "ATTRIBUTION (%)" 8 Gives the format of the attribution line in front of the quoted article included by an F command. .Sp Default: In article %i,%?%)f <%>f> wrote: .Ip "AUTOSUBSCRIBE" 8 When .I trn is checking for new newsgroups and finds one matching one of the patterns in AUTOSUBSCRIBE, the new group is automatically added to the end of the .newsrc, subscribed. Newsgroups not matching this or AUTOUNSUBSCRIBE, below, are offered to the user. .Sp AUTOSUBSCRIBE is a comma separated list of newsgroup patterns ala \&\*(L'o\*(R', \*(L'/\*(R', etc. It can also include \*(L"but not\*(R" entries preceded by \*(L'!\*(R'. \*(L"a,b,!c,d\*(R" is read as \*(L"matching a or b, unless it also matches c; matching d regardless\*(R". Another way to look at it is \*(L"(((a or b) and not c) or d)\*(R". To automatically subscribe to all local groups but be choosy about non-local groups, one might say \*(L"*,!*.*\*(R". .Sp Default: (none) .Ip "AUTOUNSUBSCRIBE" 8 AUTOUNSUBSCRIBE is very similar to AUTOSUBSCRIBE, above, but new newsgroups matching it are automatically added to the end of the .newsrc file, unsubscribed. If a newsgroup matches AUTOSUBSCRIBE, AUTOUNSUBSCRIBE is not consulted. .Sp Default: (none) .Ip "CANCEL (~)" 8 The shell command used to cancel an article. .Sp Default: inews \-h < %h .Ip "CANCELHEADER (%)" 8 13v The format of the file to pass to the CANCEL command in order to cancel an article. .Sp Default: .br Newsgroups: %n .br Subject: cmsg cancel %i .br References: %R .br Reply-To: %L@%H (%N) .br Distribution: %D .br Organization: %o .sp 1 %i cancelled from trn. .Ip DOTDIR 8 Where to find your dot files, if they aren't in your home directory. Can be interpolated using \*(L"%.\*(R". .Sp Default: $HOME .Ip "EDITOR (~)" 8 The name of your editor, if VISUAL is undefined. .Sp Default: whatever your news administrator compiled in, usually vi. .Ip "EXSAVER (%)" 8 The shell command to execute in order to extract data to either /bin/sh or a user-specified command. .Sp Default: tail +%Bc %A | %e .Ip "FIRSTLINE (%)" 8 Controls the format of the line displayed at the top of an article. Warning: this may go away. .Sp The default (ignoring the Marked to return display in unthreaded groups) is approximately: .Sp %C #%a%(%Z=^0$?%(%U!=^0$? (%U more\e)): (%U + %v more\e)) .Ip "FORWARDHEADER (%)" 8 The format of the header file for forwarding messages. See also FORWARDPOSTER. .Sp Default: .Sp To: %"\en\enTo: " .br Subject: %(%i=^$?:%[subject] (fwd\e) .br %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} .br )Newsgroups: %n .br In-Reply-To: %i) .br %(%[references]=^$?:References: %[references] .br )Organization: %o .br Cc: .br Bcc: \en\en .Ip "FORWARDPOSTER (~)" 8 The shell command to be used by the forward command (^F) in order to allow you to edit and deliver the file. .I trn will not itself call upon an editor for replies \*(-- this is a function of the program referenced by FORWARDPOSTER. See also FORWARDHEADER and MAILPOSTER. .Sp Default: QUOTECHARS=%I Rnmail \-h %h .Ip HIDELINE 8 If defined, contains a regular expression which matches article lines to be hidden, in order, for instance, to suppress quoted material. A recommended string for this purpose is \*(L"^>...\*(R", which \fIdoesn't\fR hide lines with only \*(L'>\*(R', to give some indication that quoted material is being skipped. If you want to hide more than one pattern, you can use \*(L"\||\|\*(R" to separate the alternatives. You can view the hidden lines by restarting the article with the \*(L'v\*(R' command. .Sp There is some overhead involved in matching each line of the article against a regular expression. You might wish to use a baud-rate modifier to enable this feature only at low baud rates. .Sp Default: undefined .Ip HOME 8 Your home directory. Affects ~ interpretation, and the location of your dot files if DOTDIR is not defined. .Sp Default: $LOGDIR .Ip "KILLGLOBAL (~)" 8 Where to find the KILL file to apply to every newsgroup. See the \*(L'^K\*(R' command at the newsgroup-selection level. .Sp Default: %p/KILL .Ip "KILLLOCAL (~)" 8 Where to find the KILL file for the current newsgroup. See the commands \*(L'K\*(R' and \*(L'^K\*(R' at the article selection level, and the search modifier \*(L'K\*(R'. .Sp Default: %p/%c/KILL .Ip LOGDIR 8 Your home directory if HOME is undefined. Affects ~ interpretation, and the location of your dot files if DOTDIR is not defined. .Sp Default: none. .Sp Explanation: you must have either $HOME or $LOGDIR. .Ip LOGNAME 8 Your login name, if USER is undefined. May be interpolated using \*(L"%L\*(R". .Sp Default: value of getlogin(). .Ip LOCALTIMEFMT 8 The format used by strftime() to print the local time. The Date line is only displayed in local time if the group is threaded (see the \-H option for more information on Date). .Sp Default: %a %b %e %X %Z %Y .Sp which is the same format as the .IR date (1) command. .Ip "MAILCALL (~)" 8 What to say when there is new mail. .Sp Default: (Mail) .Ip "MAILFILE (~)" 8 Where to check for mail. .Sp Default: /usr/spool/mail/%L .Ip "MAILHEADER (%)" 8 The format of the header file for replies. See also MAILPOSTER. .Sp Default: .Sp To: %t .br Subject: %(%i=^$?:Re: %S .br %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} .br )Newsgroups: %n .br In-Reply-To: %i) .br %(%[references]=^$?:References: %[references] .br )Organization: %o .br Cc: .br Bcc: \en\en .Ip "MAILPOSTER (~)" 8 The shell command to be used by the reply commands (r and R) in order to allow you to enter and deliver the response. .I trn will not itself call upon an editor for replies \*(-- this is a function of the program referenced by MAILPOSTER. See also MAILHEADER. .Sp Default: QUOTECHARS=%I Rnmail \-h %h .Ip "MBOXSAVER (~)" 8 The shell command to save an article in mailbox format. .Sp Default: %X/mbox.saver %A %P %c %a %B %C "%b" \e .br "From %t %`date`" .Sp Explanation: the first seven arguments are the same as for NORMSAVER. The eighth argument to the shell script is the new From line for the article, including the posting date, derived either directly from the Posted: line, or not-so-directly from the Date: line. Header munging at its finest. .Ip MODSTRING 8 The string to insert in the group summary line, which heads each article, for a moderated group. See also NOPOSTRING. .Sp Default: " (moderated)" .Ip NAME 8 Your full name. May be interpolated using \*(L"%N\*(R". .Sp Default: name from /etc/passwd, or ~/.fullname. .Ip "NEWSHEADER (%)" 8 16v The format of the header file for follow-ups. See also NEWSPOSTER. .Sp Default: .Sp %(%[followup-to]=^$?:%(%[followup-to]=^%n$?:X-ORIGINAL-NEWSGROUPS: %n .br ))Newsgroups: %(%F=^$?%C:%F) .br Subject: %(%S=^$?%"\en\enSubject: ":Re: %S) .br Summary: .br Expires: .br %(%R=^$?:References: %R .br )Sender: .br Followup-To: .br %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} .br )Distribution: %(%i=^$?%"Distribution: ":%D) .br Organization: %o .br Keywords: %[keywords] .br Cc: \en\en .Ip NEWSORG 8 Either the name of your organization, or the name of a file containing the name of your organization. (For use at sites where the ORGANIZATION environmental variable is already in use. NEWSORG will override ORGANIZATION if both are present.) May be interpolated using \*(L"%o\*(R". .Sp Default: whatever your news administrator compiled in. .Ip "NEWSPOSTER (~)" 8 The shell command to be used by the follow-up commands (f and F) in order to allow you to enter and post a follow-up news article. .I trn will not itself call upon an editor for follow-ups \*(-- this is a function of the program called by .IR trn . See also NEWSHEADER. .Sp Default: QUOTECHARS=%I Pnews \-h %h .Ip NNTPSERVER 8 The hostname of your NNTPSERVER. [This does not apply unless you are running the NNTP version of trn.] .Sp Default: the hostname listed in the server file, usually /usr/local/lib/rn/server. .Ip NOPOSTRING 8 The string to insert in the group summary line, which heads each article, for a group to which local posting is not allowed. See also MODSTRING. .Sp Default: " (no posting)" .Ip "NORMSAVER (~)" 8 The shell command to save an article in the normal (non-mailbox) format. .Sp Default: %X/norm.saver %A %P %c %a %B %C "%b" .Ip ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. (If NEWSORG is set, it will override ORGANIZATION.) May be interpolated using \*(L"%o\*(R". .Sp Default: whatever your news administrator compiled in. .Ip PAGESTOP 8 If defined, contains a regular expression which matches article lines to be treated as form-feeds. There are at least two things you might want to do with this. To cause page breaks between articles in a digest, you might define it as \*(L"^--------\*(R". To force a page break before a signature, you could define it as \*(L"^-- $\*(R". (Then, when you see \*(L"--\*(R" at the bottom of the page, you can skip the signature if you so desire by typing \*(L'n\*(R' instead of space.) To do both, you could use \*(L"^--\*(R". If you want to break on more than one pattern, you can use \*(L"\||\|\*(R" to separate the alternatives. .Sp There is some overhead involved in matching each line of the article against a regular expression. You might wish to use a baud-rate modifier to enable this feature only at low baud rates. .Sp Default: undefined .Ip "PIPESAVER (%)" 8 The shell command to execute in order to accomplish a save to a pipe (\*(L"s\ |\ command\*(R" or \*(L"w\ |\ command\*(R"). The command typed by the user is substituted in as %b. .Sp Default: %(%B=^0$?<%A:tail +%Bc %A |) %b .Sp Explanation: if %B is 0, the command is \*(L"<%A %b\*(R", otherwise the command is \*(L"tail +%Bc %A | %b\*(R". .Ip REPLYTO 8 The value of the \*(L"Reply-To:\*(R" header, if needed. .Sp Default: \*(L" \*(R". .Ip RNINIT 8 This variable is used when initializing trn in rn-compatibility mode (see the \-x switch) or when the TRNINIT variable isn't defined. See the TRNINIT variable for a description. .Ip "RNMACRO (~)" 8 The name of the file containing macros and key mappings when running trn as rn. See also the TRNMACRO variable and the CUSTOM MACROS section. .Sp Default: %./.rnmac .Ip "SAVEDIR (~)" 8 The name of the directory to save to, if the save command does not specify a directory name. .Sp Default: .br If .B \-/ is set: %p/%c .br If .B +/ is set: %p .Ip "SAVENAME (%)" 8 The name of the file to save to, if the save command contains only a directory name. .Sp Default: .br If .B \-/ is set: %a .br If .B +/ is set: %^C .Ip "SELECTCHARS" 8 The characters used by the thread selector to select the associated thread of discussion. You can specify up to 64 visible characters, including upper- and lower-case letters, numbers, and many punctuation characters. Selection characters override command characters in the selector, but are not excluded from macro expansion, so be careful. .br Default: abdefgijlorstuvwxyz1234567890BCFGHIKMVW .br (You'll notice various characters are omitted to allow them to be typed as commands in the selector.) .Ip SHELL 8 The name of your preferred shell. It will be used by the \*(L'!\*(R', \*(L'S\*(R' and \*(L'W\*(R' commands. .Sp Default: whatever your news administrator compiled in. .Ip "SUBJLINE (%)" 8 Controls the format of the lines displayed by the \*(L'=\*(R' command at the article selection level. .Sp Default: %s .Ip "SUPERSEDEHEADER (%)" 8 16v The format of the header file for a supersede article. .Sp Default: .Sp From: %L@%H (%N) .br Newsgroups: %n .br Subject: %S .br Distribution: %D .br Organization: %o .br Supersedes: %i .Ip TERM 8 Determines which termcap entry to use, unless TERMCAP contains the entry. .Ip TERMCAP 8 Holds either the name of your termcap file, or a termcap entry. .Sp Default: /etc/termcap, normally. .Ip TRNINIT 8 Default values for switches may be passed to .I trn by placing them in the TRNINIT variable (or RNINIT if you're starting trn in rn-compatibility mode). Any switch that is set in this way may be overruled on the command line, or via the \*(L'&\*(R' command from within .IR trn . Binary-valued switches that are set with \*(L"\-switch\*(R" may be unset using \*(L"+switch\*(R". .Sp If TRNINIT begins with a \*(L'/\*(R' it is assumed to be the name of a file containing switches. You can put comments in this file by preceding them with a \*(L'#\*(R' as long as this is the first character on a line or it follows some white-space (which delimits the switches in the file). If you want to set many environment variables but don't want to keep them all in your environment, or if the use of any of these variables conflicts with other programs, you can use this feature along with the .B \-E switch to set the environment variables upon startup. .Sp Default: \*(L" \*(R". .Ip "TRNMACRO (~)" 8 The name of the file containing macros and key mappings. If the file is not found, the RNMACRO variable is used to look for your rn macros. For information on what to put into this file, see the CUSTOM MACROS section. .Sp Default: %./.trnmac .Ip "UNSHAR (~)" 8 The shell command to execute in order to accomplish the unshar'ing of a shell archive. .Sp Default: /bin/sh .Ip USER 8 Your login name. May be interpolated using \*(L"%L\*(R". .Sp Default: $LOGNAME .Ip "VISUAL (~)" 8 The name of your editor. .Sp Default: $EDITOR .Ip XTERMMOUSE 8 If you set this variable to \*(L'y\*(R' (yes), trn will enable the use of the xterm mouse in the selector if you are using an xterm. If you set it to \*(L'a\*(R' (always), trn will assume you have an xterm. Once enabled left-clicking on an item selects it while middle-clicking an item will move to that item. If you click the top (header) line of the selector it moves up a page. If you click the bottom (footer) line of the selector it executes the default command for the page (left click) or goes down a page (middle click). You can also use the right mouse button to move up or down a page by clicking in the upper-half or lower-half of the screen, respectively. .Ip "YOUSAID (%)" 8 Gives the format of the attribution line in front of the quoted article included by an R command. .Sp Default: In article %i you write: .SH "AUTOMATIC MACROS" On startup .I trn attempts to build a set of macros that map your keypad arrow keys to useful functions. These default actions are mentioned in the prior description of each level's commands. If you don't like this (or trn gets it wrong), you can disable the automatic macros by using the .B \-A option. .SH "CUSTOM MACROS" When .I trn starts up it looks for a file containing macro definitions (see environment variables TRNMACRO and RNMACRO). Any sequence of commands may be bound to any sequence of keys, so you could re-map your entire keyboard if you desire. Blank lines or lines beginning with # in the macro file are considered comments; otherwise .I trn looks for two fields separated by white space. The first field gives the sequence of keystrokes that trigger the macro, and the second field gives the sequence of commands to execute. Both fields are subject to % interpolation, which will also translate backslash and caret sequences. (The keystroke field is interpreted at startup time, but the command field is interpreted at macro execution time so that you may refer to % values in a macro.) For example, if you want to reverse the roles of carriage return and space in .I trn .Sp ^J \e040 .br ^M \e040 .br \e040 ^J .Sp will do just that. By default, all characters in the command field are interpreted as the canonical .I trn characters, i.\|e. no macro expansion is done. Otherwise the above pair of macros would cause an infinite loop. To force macro expansion in the command field, enclose the macro call with ^( ... ^) thusly: .Sp @s |mysavescript .br @w w^(@s^) .Sp You can use the %() conditional construct to construct macros that work differently under different circumstances. In particular, the current mode (%m) of .I trn could be used to make a command that only works at a particular level. This is particularly vital for the selector which uses most of the lower-case letters to select the associated item in its display. For example, .Sp a %(%m=t?a:s art.hold\en) .Sp will return the original letter (a) in the selector, and the command \*(L"s art.hold\en\*(R" everywhere else. .Sp %(%{TERM}=vt100?^[[O) /^J .Sp will do the binding only if the terminal type is vt100, though if you have many of these it would be better to have separate files for each terminal. .Sp If you want to bind a macro to a function key that puts a common garbage character after the sequence (such as the carriage return on the end of Televideo 920 function sequences), DO NOT put the carriage return into all the sequences or you will waste a CONSIDERABLE amount of internal storage. Instead of \*(L"^AF^M\*(R", put \*(L"^AF+1\*(R", which indicates to .I trn that it should gobble up one character after the F. .SH "WHAT'S NEW" Here's a quick run-down of .IR trn 's features and commands aimed at the knowledgeable .I rn or .I trn user. .PP The addition of true reference-line threading is one of the biggest improvements over rn. This threading allows you to read a discussion in reply order with an article's replies being attached to the article that inspired them. Threads will encompass multiple subjects whenever a reply to an article in the thread arrives with a different subject. This is usually done to better indicate the topic in the reply when it diverges from the original subject. .PP Another big improvement is the selector, which is bound to the \*(L'+\*(R' key. The selector displays a list of threads, subjects, or individual articles to allow you to select the topics that interest you by typing their associated letter. The difference between the thread and the subject selector is that the subject selector displays all subjects with a separate selection letter, even those tied together via their references. This can be quite useful if you select some threads and desire to weed out some extraneous discussions: you could switch the selector into exclusive mode (\*(L'E\*(R' shows only selected threads) and then into subject mode (\*(L'Ss\*(R') to separate the threads into their component subjects and deselect or kill the subjects you don't care about. You don't have to go to all this trouble using the selector if you prefer to just hit the \*(L'k\*(R' key when you start reading a subject you're not interested in. The selector can also switch between showing unread articles and articles that have already been read, allowing you to selectively re-read discussions (this is the \*(L'U\*(R' command in the selector). .PP Another threaded addition is the article-tree display in the upper-right corner of the header. Looking at the tree gives you a feel for how the articles you are reading relate to each other, allowing you to see at a glance when there are lots of replies and decide if you want to junk an uninteresting set of replies or perhaps tough it out. .PP The header display has also been modified to hide a few more lines by default (e.\|g. References), but, as always, you can override these with \-h. There is also some more \*(L"magic\*(R" in the header: the From header can be trimmed to be just the comment portion (if available), and the Date header is displayed in local time (by default). Use \-H and +H to turn header magic on and off. .PP Once you begin reading articles, use the regular movement commands (n, N, p, P, etc.) as you normally would. You'll find that these commands track the reply order shown in the tree display. Then try using ^N and ^P, which follow a subject in the order the articles were posted. Finally, check out the [, ], (, ), {, and } commands to move around in the article tree a bit more directly. The first four commands should also be bound to your keypad's arrow keys, making them easier to type. For example, typing \*(L'[\*(R' (left) takes you to your parent article, even if it was already read, which is very useful for tracking down the cited portion of the article in its original context. .PP There are additional kill commands for the entire thread (J) and the current article and all its replies (,). .PP The KILL files have been extended and the commands inside them are now referred to memorized commands, since they are often used for selection rather than killing of articles. There are new, easier ways to add memorized commands using the \*(L'A\*(R'dd and \*(L'T\*(R'hread commands. The \*(L'A\*(R' command is subject-oriented, while the \*(L'T\*(R' command is article-oriented (meaning they affect a specific set of articles rather than any article that happens to have a matching subject). They both prompt you for what kind of command you want to add, making both auto-killing and auto-selecting just as easy. .PP There is also an easy way to skip around among the various threads with the < and > commands. Use them if you want to skip a set of article and read them later instead of junking them. .PP Note: your news administrator has the option of turning thread processing off for individual groups, and thus it is possible for some groups to not have any pre-processed thread information available for use. When .I trn encounters such a group, it generates the thread information on the fly while entering the group. For really large groups (or really slow systems), this can take an appreciable amount of time. If you can't talk your news administrator into pre-threading the group, you can turn off the threading on a group-by-group basis using the \*(L't\*(R' command at the newsgroup-selection level. Groups turned off in this way are read in the .I rn style \*(-- articles arranged in arrival order unless you specify the \-S option, which reads the articles in date order by subject. .PP Take note of the \*(L"e dir\*(R" command, which is used to extract a shell archive or uuencoded file into the specified directory. It is even possible to extract other data formats if you specify the appropriate filter command (e.\|g. \*(L"e dir|cmd\*(R". .PP Also, if you plan to use macro definitions, it is good to keep in mind that the selector uses most of the lower-case letters for selection, and thus it is a good idea to explicitly set the mode(s) in which a macro applies. For example, if you want to press \*(L'f\*(R' from the article pager/selector to forward the current article to the user \*(L"smith\*(R", you could define: .Sp .nf f %(%m=[pa]?|mail smith\en:f) .fi .Sp This checks the current mode (%m) and if it is \*(L'p\*(R' or \*(L'a\*(R' it expands it to the string \*(L"|mail smith\en\*(R", otherwise it returns the letter \*(L'f\*(R'. In some cases, you may simply wish to exclude the selector from a macro with the conditional \*(L"%m!=t\*(R". .PP Finally, you'll probably want to use the new options, .B \-x and .B \-X to ensure that all the newest features are available for use. These options might be on by default, depending on how your administrator decided to install .IR trn . .SH AUTHORS Rn was created by Larry Wall .br and is now under the direction of Stan Barber . .br Threaded version by Wayne Davison .br (Mail all bug reports for trn to Wayne.) .br Regular expression routines are borrowed from emacs, by James Gosling. .br Hashing routines are modified versions from Geoffrey Collyer. .SH FILES .Ip "%./.newsrc" 1.25i status of your news reading .Ip "%./.oldnewsrc" 1.25i backup copy of your .I .newsrc from start of session .Ip "%./.rnlock" 1.25i lock file so you don't screw up your .I .newsrc .Ip "%./.rnlast" 1.25i info from last run of trn .Ip "%./.rnsoft" 1.25i soft pointers into /usr/lib/news/active to speed startup, synchronous with .I .newsrc .Ip "%./.rnhead" 1.25i temporary header file to pass to a mailer or news poster .Ip "%./.[t]rnmac" 1.25i macro and keymap definitions .Ip "%p" 1.25i your news save directory, usually ~/News .Ip "%x/active" 1.25i the list of active newsgroups, usually /usr/lib/news/active on systems that don't use NNTP .Ip "%P" 1.25i the public news spool directory, usually /usr/spool/news on systems that don't use NNTP .Ip "%X/INIT" 1.25i system-wide default switches .SH SEE ALSO newsrc(5), more(1), readnews(1), Pnews(1), Rnmail(1) .SH DIAGNOSTICS Generally self-documenting, as they say. .SH BUGS The .B \-h switch can only hide header lines that .I trn knows about. .PP The \*(L'\-\*(R' command doesn't cross newsgroup boundaries, and only undoes the last article selection. .PP If you edit your .I .newsrc while .I trn is running, .I trn will happily wipe out your changes when it decides to write out the .I .newsrc file. .PP Marking of duplicate articles as read in cross-referenced newsgroups will not work unless the Xref patch is installed in inews. .PP If you get carried away with % or escape substitutions, you can overflow buffers. trn-3.6/newsgroups.SH0000664000000000000000000000507707041327652011544 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting newsgroups (with variable substitutions)" $spitshell >newsgroups < newsgroups.new ;; *) sed < newsgroups -e '/^#NNTP/d' -e '/^#NORMAL/s/^#NORMAL//' > newsgroups.new ;; esac mv newsgroups.new newsgroups $eunicefix newsgroups chmod 755 newsgroups trn-3.6/term.c0000664000000000000000000007341505664012020010175 0ustar /* $Id: term.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "util2.h" #include "final.h" #include "help.h" #include "cache.h" #include "intrp.h" #include "INTERN.h" #include "term.h" #ifdef u3b2 #undef TIOCGWINSZ #endif #undef USETITE /* use terminal init/exit seqences (not recommended) */ #undef USEKSKE /* use keypad start/end sequences */ char ERASECH; /* rubout character */ char KILLCH; /* line delete character */ char tcarea[TCSIZE]; /* area for "compiled" termcap strings */ int upcost; /* guarantee capability pointer != Nullch */ /* (I believe terminfo will ignore the &tmpaddr argument.) */ char *tgetstr(); #define Tgetstr(key) ((tmpstr = tgetstr(key,&tmpaddr)) ? tmpstr : nullstr) struct keymap { char km_type[128]; union km_union { struct keymap *km_km; char *km_str; } km_ptr[128]; }; #define KM_NOTHIN 0 #define KM_STRING 1 #define KM_KEYMAP 2 #define KM_BOGUS 3 #define KM_TMASK 3 #define KM_GSHIFT 4 #define KM_GMASK 7 typedef struct keymap KEYMAP; KEYMAP *topmap INIT(Null(KEYMAP*)); void mac_init(); KEYMAP *newkeymap(); void show_keymap(); void line_col_calcs(); /* terminal initialization */ void term_init() { savetty(); /* remember current tty state */ #ifdef I_TERMIO outspeed = _tty.c_cflag & CBAUD; /* for tputs() */ ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */ KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */ if (GT = ((_tty.c_oflag & TABDLY) != TAB3)) /* we have tabs, so that's OK */; else _tty.c_oflag &= ~TAB3; /* turn off kernel tabbing -- done in rn */ #else /* !I_TERMIO */ # ifdef I_TERMIOS outspeed = cfgetospeed(&_tty); /* for tputs() (output) */ ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */ KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */ #if 0 _tty.c_oflag &= ~OXTABS; /* turn off kernel tabbing-done in rn */ #endif # else /* !I_TERMIOS */ outspeed = _tty.sg_ospeed; /* for tputs() */ ERASECH = _tty.sg_erase; /* for finish_command() */ KILLCH = _tty.sg_kill; /* for finish_command() */ if (GT = ((_tty.sg_flags & XTABS) != XTABS)) /* we have tabs, so that's OK */; else _tty.sg_flags &= ~XTABS; # endif /* I_TERMIOS */ #endif /* I_TERMIO */ /* The following could be a table but I can't be sure that there isn't */ /* some degree of sparsity out there in the world. */ switch (outspeed) { /* 1 second of padding */ #ifdef BEXTA case BEXTA: just_a_sec = 1920; break; #else #ifdef B19200 case B19200: just_a_sec = 1920; break; #endif #endif case B9600: just_a_sec = 960; break; case B4800: just_a_sec = 480; break; case B2400: just_a_sec = 240; break; case B1800: just_a_sec = 180; break; case B1200: just_a_sec = 120; break; case B600: just_a_sec = 60; break; case B300: just_a_sec = 30; break; /* do I really have to type the rest of this??? */ case B200: just_a_sec = 20; break; case B150: just_a_sec = 15; break; case B134: just_a_sec = 13; break; case B110: just_a_sec = 11; break; case B75: just_a_sec = 8; break; case B50: just_a_sec = 5; break; default: just_a_sec = 960; break; /* if we are running detached I */ } /* don't want to know about it! */ } /* set terminal characteristics */ void term_set(tcbuf) char *tcbuf; /* temp area for "uncompiled" termcap entry */ { char *tmpaddr; /* must not be register */ register char *tmpstr; char *s; int status; #ifdef TIOCGWINSZ struct winsize winsize; #endif #ifdef PENDING #if ! defined (FIONREAD) && ! defined (HAS_RDCHK) /* do no delay reads on something that always gets closed on exit */ devtty = fileno(stdin); if (isatty(devtty)) { devtty = open("/dev/tty",0); if (devtty < 0) { printf(cantopen,"/dev/tty") FLUSH; finalize(1); } fcntl(devtty,F_SETFL,O_NDELAY); } #endif #endif /* get all that good termcap stuff */ #ifdef HAS_TERMLIB s = getenv("TERM"); status = tgetent(tcbuf,s? s : "dumb"); /* get termcap entry */ if (status < 1) { #ifdef VERBOSE printf("No termcap %s found.\n", status ? "file" : "entry") FLUSH; #else fputs("Termcap botch\n",stdout) FLUSH; #endif finalize(1); } tmpaddr = tcarea; /* set up strange tgetstr pointer */ s = Tgetstr("pc"); /* get pad character */ PC = *s; /* get it where tputs wants it */ if (!tgetflag("bs")) { /* is backspace not used? */ BC = Tgetstr("bc"); /* find out what is */ if (BC == nullstr) /* terminfo grok's 'bs' but not 'bc' */ BC = Tgetstr("le"); } else BC = "\b"; /* make a backspace handy */ UP = Tgetstr("up"); /* move up a line */ if (!*UP) /* no UP string? */ marking = 0; /* disable any marking */ if (muck_up_clear) /* this is for weird HPs */ CL = "\n\n\n\n"; else CL = Tgetstr("cl"); /* get clear string */ CE = Tgetstr("ce"); /* clear to end of line string */ TI = Tgetstr("ti"); /* initialize display */ TE = Tgetstr("te"); /* reset display */ KS = Tgetstr("ks"); /* enter `keypad transmit' mode */ KE = Tgetstr("ke"); /* exit `keypad transmit' mode */ HO = Tgetstr("ho"); /* home cursor */ IL = Tgetstr("al"); /* insert (add) line */ CM = Tgetstr("cm"); /* cursor motion */ if (*CM || *HO) can_home = TRUE; #ifdef CLEAREOL CD = Tgetstr("cd"); /* clear to end of display */ if (!*CE || !*CD || !can_home) /* can we CE, CD, and home? */ can_home_clear = FALSE; /* no, so disable use of clear eol */ if (!*CE) CE = CD; #endif /* CLEAREOL */ upcost = strlen(UP); SO = Tgetstr("so"); /* begin standout */ SE = Tgetstr("se"); /* end standout */ if ((SG = tgetnum("sg"))<0) SG = 0; /* blanks left by SG, SE */ US = Tgetstr("us"); /* start underline */ UE = Tgetstr("ue"); /* end underline */ if ((UG = tgetnum("ug"))<0) UG = 0; /* blanks left by US, UE */ if (*US) UC = nullstr; /* UC must not be NULL */ else UC = Tgetstr("uc"); /* underline a character */ if (!*US && !*UC) { /* no underline mode? */ US = SO; /* substitute standout mode */ UE = SE; UG = SG; } LINES = tgetnum("li"); /* lines per page */ COLS = tgetnum("co"); /* columns on page */ #ifdef TIOCGWINSZ { struct winsize ws; if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) { LINES = ws.ws_row; COLS = ws.ws_col; } } #endif AM = tgetflag("am"); /* terminal wraps automatically? */ XN = tgetflag("xn"); /* then eats next newline? */ VB = Tgetstr("vb"); if (!*VB) VB = "\007"; CR = Tgetstr("cr"); if (!*CR) { if (tgetflag("nc") && *UP) { CR = safemalloc((MEM_SIZE)strlen(UP)+2); sprintf(CR,"%s\r",UP); } else CR = "\r"; } #ifdef TIOCGWINSZ if (ioctl(1, TIOCGWINSZ, &winsize) >= 0) { if (winsize.ws_row > 0) LINES = winsize.ws_row; if (winsize.ws_col > 0) COLS = winsize.ws_col; } #endif #else ?????? /* Roll your own... */ #endif termlib_init(); line_col_calcs(); noecho(); /* turn off echo */ crmode(); /* enter cbreak mode */ mac_init(tcbuf); } void set_macro(seq,def) char *seq; /* input sequence of keys */ char *def; /* definition */ { mac_line(def,seq,0); /* check for common (?) brain damage: ku/kd/etc sequence may be the * cursor move sequence instead of the input sequence. * (This happens on the local xterm definitions.) * Try to recognize and adjust for this case. */ if (seq[0] == '\033' && seq[1] == '[' && seq[2]) { char lbuf[LBUFLEN]; /* copy of possibly non-writable string */ strcpy(lbuf,seq); lbuf[1] = 'O'; mac_line(def,lbuf,0); } if (seq[0] == '\033' && seq[1] == 'O' && seq[2]) { char lbuf[LBUFLEN]; /* copy of possibly non-writable string */ strcpy(lbuf,seq); lbuf[1] = '['; mac_line(def,lbuf,0); } } /* Turn the arrow keys into macros that do some basic trn functions. ** Code provided by Clifford Adams. */ void arrow_macros(tmpbuf) char *tmpbuf; { char lbuf[256]; /* should be long enough */ char *tmpaddr = tmpbuf; register char *tmpstr; /* If arrows are defined as single keys, we probably don't * want to redefine them. (The tvi912c defines kl as ^H) */ strcpy(lbuf,Tgetstr("ku")); /* up */ /* '(' at article or pager, '[' in thread sel, 'p' otherwise */ if ((int)strlen(lbuf) > 1) set_macro(lbuf,"%(%m=[ap]?\\(:%(%m=t?[:p))"); strcpy(lbuf,Tgetstr("kd")); /* down */ /* ')' at article or pager, ']' in thread sel, 'n' otherwise */ if ((int)strlen(lbuf) > 1) set_macro(lbuf,"%(%m=[ap]?\\):%(%m=t?]:n))"); strcpy(lbuf,Tgetstr("kl")); /* left */ /* '[' at article or pager, '<' otherwise */ if ((int)strlen(lbuf) > 1) set_macro(lbuf,"%(%m=[ap]?\\[:<)"); strcpy(lbuf,Tgetstr("kr")); /* right */ /* CR at newsgroups, ']' at article or pager, '>' otherwise */ if ((int)strlen(lbuf) > 1) set_macro(lbuf,"%(%m=n?^j:%(%m=[ap]?\\]:>))"); } void mac_init(tcbuf) char *tcbuf; { char tmpbuf[1024]; /* set up the Xterm mouse sequence */ sprintf(tmpbuf,"\033[M"); /* enter mouse escape sequence */ set_macro(tmpbuf,"\003"); if (auto_arrow_macros) arrow_macros(tmpbuf); if (use_threads) tmpfp = fopen(filexp(getval("TRNMACRO",TRNMACRO)),"r"); else tmpfp = Nullfp; if (!tmpfp) tmpfp = fopen(filexp(getval("RNMACRO",RNMACRO)),"r"); if (tmpfp) { while (fgets(tcbuf,1024,tmpfp) != Nullch) { mac_line(tcbuf,tmpbuf,(sizeof tmpbuf)); } fclose(tmpfp); } } void mac_line(line,tmpbuf,tbsize) char *line; char *tmpbuf; int tbsize; { register char *s, *m; register KEYMAP *curmap; register int ch; register int garbage = 0; static char override[] = "\nkeymap overrides string\n"; if (topmap == Null(KEYMAP*)) topmap = newkeymap(); if (*line == '#' || *line == '\n') return; if (line[ch = strlen(line)-1] == '\n') line[ch] = '\0'; /* A 0 length signifies we already parsed the macro into tmpbuf, ** so line is just the definition. */ if (tbsize) m = dointerp(tmpbuf,tbsize,line," \t"); else m = line; if (!*m) return; while (*m == ' ' || *m == '\t') m++; for (s=tmpbuf,curmap=topmap; *s; s++) { ch = *s & 0177; if (s[1] == '+' && isdigit(s[2])) { s += 2; garbage = (*s & KM_GMASK) << KM_GSHIFT; } else garbage = 0; if (s[1]) { if ((curmap->km_type[ch] & KM_TMASK) == KM_STRING) { if (tbsize) fputs(override,stdout) FLUSH; free(curmap->km_ptr[ch].km_str); curmap->km_ptr[ch].km_str = Nullch; } curmap->km_type[ch] = KM_KEYMAP + garbage; if (curmap->km_ptr[ch].km_km == Null(KEYMAP*)) curmap->km_ptr[ch].km_km = newkeymap(); curmap = curmap->km_ptr[ch].km_km; } else { if (tbsize && (curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP) fputs(override,stdout) FLUSH; else { curmap->km_type[ch] = KM_STRING + garbage; curmap->km_ptr[ch].km_str = savestr(m); } } } } KEYMAP* newkeymap() { register int i; register KEYMAP *map; #ifndef lint map = (KEYMAP*)safemalloc(sizeof(KEYMAP)); #else map = Null(KEYMAP*); #endif /* lint */ for (i=127; i>=0; --i) { map->km_ptr[i].km_km = Null(KEYMAP*); map->km_type[i] = KM_NOTHIN; } return map; } void show_macros() { char prebuf[64]; if (topmap != Null(KEYMAP*)) { print_lines("Macros:\n",STANDOUT); *prebuf = '\0'; show_keymap(topmap,prebuf); } else { print_lines("No macros defined.\n", NOMARKING); } } void show_keymap(curmap,prefix) register KEYMAP *curmap; char *prefix; { register int i; register char *next = prefix + strlen(prefix); register int kt; for (i=0; i<128; i++) { if (kt = curmap->km_type[i]) { if (i < ' ') sprintf(next,"^%c",i+64); else if (i == ' ') strcpy(next,"\\040"); else if (i == 127) strcpy(next,"^?"); else sprintf(next,"%c",i); if ((kt >> KM_GSHIFT) & KM_GMASK) { sprintf(cmd_buf,"+%d", (kt >> KM_GSHIFT) & KM_GMASK); strcat(next,cmd_buf); } switch (kt & KM_TMASK) { case KM_NOTHIN: sprintf(cmd_buf,"%s %c\n",prefix,i); print_lines(cmd_buf,NOMARKING); break; case KM_KEYMAP: show_keymap(curmap->km_ptr[(char)i].km_km, prefix); break; case KM_STRING: sprintf(cmd_buf,"%s %s\n",prefix,curmap->km_ptr[i].km_str); print_lines(cmd_buf,NOMARKING); break; case KM_BOGUS: sprintf(cmd_buf,"%s BOGUS\n",prefix); print_lines(cmd_buf,STANDOUT); break; } } } } /* routine to pass to tputs */ char putchr(ch) register char_int ch; { putchar(ch); #ifdef lint ch = Null(char); ch = ch; #endif return((char) 0); } int not_echoing = 0; void hide_pending() { not_echoing = 1; pushchar(0200); } bool macro_pending() { if (nextout != nextin) { if (circlebuf[nextout] == '\200') { switch (not_echoing) { case 0: break; case 1: nextout++; nextout %= PUSHSIZE; not_echoing = 0; break; default: circlebuf[nextout] = '\n'; not_echoing = 0; break; } return nextout != nextin; } return 1; } return 0; } /* input the 2nd and succeeding characters of a multi-character command */ /* returns TRUE if command finished, FALSE if they rubbed out first character */ int buflimit = LBUFLEN; bool finish_command(donewline) int donewline; { register char *s; register bool quoteone = FALSE; s = buf; if (s[1] != FINISHCMD) /* someone faking up a command? */ return TRUE; if (not_echoing) not_echoing = 2; do { top: if (not_echoing) ; else if (*(unsigned char *)s < ' ') { putchar('^'); putchar(*s | 64); } else if (*s == '\177') { putchar('^'); putchar('?'); } else putchar(*s); /* echo previous character */ s++; if (s - buf == buflimit) break; re_read: fflush(stdout); getcmd(s); if (quoteone) { quoteone = FALSE; continue; } if (errno || *s == '\f') { *s = Ctl('r'); /* force rewrite on CONT */ } if (*s == '\033') { /* substitution desired? */ #ifdef ESCSUBS char tmpbuf[4], *cpybuf; tmpbuf[0] = '%'; read_tty(&tmpbuf[1],1); #ifdef RAWONLY tmpbuf[1] &= 0177; #endif tmpbuf[2] = '\0'; if (tmpbuf[1] == 'h') { (void) help_subs(); *s = '\0'; reprint(); goto re_read; } else if (tmpbuf[1] == '\033') { *s = '\0'; cpybuf = savestr(buf); interp(buf, (sizeof buf), cpybuf); free(cpybuf); s = buf + strlen(buf); reprint(); goto re_read; } else { interp(s,(sizeof buf) - (s-buf),tmpbuf); fputs(s,stdout); s += strlen(s); } goto re_read; #else notincl("^["); *s = '\0'; reprint(); goto re_read; #endif } else if (*s == ERASECH) { /* they want to rubout a char? */ rubout(); s--; /* discount the char rubbed out */ if (*(unsigned char *)s < ' ' || *s == '\177') rubout(); if (s == buf) { /* entire string gone? */ fflush(stdout); /* return to single char command mode */ return FALSE; } else goto re_read; } else if (*s == KILLCH) { /* wipe out the whole line? */ while (s-- != buf) { /* emulate that many ERASEs */ rubout(); if (*(unsigned char *)s < ' ' || *s == '\177') rubout(); } fflush(stdout); return FALSE; /* return to single char mode */ } #ifdef WORDERASE else if (*s == Ctl('w')) { /* wipe out one word? */ *s-- = ' '; while (!isspace(*s) || isspace(s[1])) { rubout(); if (s-- == buf) { fflush(stdout); return FALSE; /* return to single char mode */ } if (*(unsigned char *)s < ' ' || *s == '\177') rubout(); } s++; goto re_read; } #endif else if (*s == Ctl('r')) { *s = '\0'; reprint(); goto re_read; } else if (*s == Ctl('v')) { putchar('^'); backspace(); fflush(stdout); getcmd(s); goto top; } else if (*s == '\\') { quoteone = TRUE; } #if defined(__bsdi__) || defined(cray) } while (*s != '\r' && *s != '\n'); /* until CR or NL (not echoed) */ #else } while (*s != '\n'); /* until a newline (not echoed) */ #endif *s = '\0'; /* terminate the string nicely */ if (donewline) putchar('\n') FLUSH; return TRUE; /* say we succeeded */ } bool finish_dblchar() { bool ret; int buflimit_save = buflimit; int not_echoing_save = not_echoing; buflimit = 2; ret = finish_command(FALSE); buflimit = buflimit_save; not_echoing = not_echoing_save; return ret; } /* discard any characters typed ahead */ void eat_typeahead() { if (!typeahead && !macro_pending()) { /* cancel only keyboard stuff */ #ifdef PENDING while (input_pending()) read_tty(buf,sizeof(buf)); #else /* this is probably v7 */ # ifdef I_SGTTY ioctl(_tty_ch,TIOCSETP,&_tty); # else # ifdef I_TERMIO ioctl(_tty_ch,TCSETAW,&_tty); # else tcsetattr(_tty_ch,TCSAFLUSH,&_tty); # endif # endif #endif } } void save_typeahead(buf, len) char *buf; int len; { int cnt; while (input_pending()) { cnt = read_tty(buf, len); buf += cnt; len -= cnt; } *buf = '\0'; } void settle_down() { dingaling(); fflush(stdout); /*sleep(1);*/ nextout = nextin; /* empty circlebuf */ eat_typeahead(); } /* read a character from the terminal, with multi-character pushback */ int read_tty(addr,size) char *addr; int size; { if (macro_pending()) { *addr = circlebuf[nextout++]; nextout %= PUSHSIZE; return 1; } else { size = read(0,addr,size); #ifdef RAWONLY *addr &= 0177; #endif return size; } } #ifdef PENDING # if !defined (FIONREAD) && !defined (HAS_RDCHK) int circfill() { register int Howmany; errno = 0; Howmany = read(devtty,circlebuf+nextin,1); if (Howmany < 0 && (errno == EAGAIN || errno == EINTR)) Howmany = 0; if (Howmany) { nextin += Howmany; nextin %= PUSHSIZE; } return Howmany; } # endif /* PENDING */ #endif /* FIONREAD */ void pushchar(c) char_int c; { nextout--; if (nextout < 0) nextout = PUSHSIZE - 1; if (nextout == nextin) { fputs("\npushback buffer overflow\n",stdout) FLUSH; sig_catcher(0); } circlebuf[nextout] = c; } /* print an underlined string, one way or another */ void underprint(s) register char *s; { assert(UC); if (*UC) { /* char by char underline? */ while (*s) { if (*(unsigned char *)s < ' ') { putchar('^'); backspace();/* back up over it */ underchar();/* and do the underline */ putchar(*s+64); backspace();/* back up over it */ underchar();/* and do the underline */ } else { putchar(*s); backspace();/* back up over it */ underchar();/* and do the underline */ } s++; } } else { /* start and stop underline */ underline(); /* start underlining */ while (*s) { if (*(unsigned char *)s < ' ') { putchar('^'); putchar(*s+64); } else putchar(*s); s++; } un_underline(); /* stop underlining */ } } /* keep screen from flashing strangely on magic cookie terminals */ #ifdef NOFIREWORKS void no_sofire() { if (*UP && *SE) { /* should we disable fireworks? */ putchar('\n'); un_standout(); up_line(); carriage_return(); } } void no_ulfire() { if (*UP && *US) { /* should we disable fireworks? */ putchar('\n'); un_underline(); up_line(); carriage_return(); } } #endif /* get a character into a buffer */ void getcmd(whatbuf) register char *whatbuf; { register KEYMAP *curmap; register int i; bool no_macros; int times = 0; /* loop detector */ char scrchar; tryagain: curmap = topmap; #ifdef OLD_RN_WAY no_macros = (whatbuf != buf && !macro_pending()); #else no_macros = (whatbuf != buf); #endif for (;;) { int_count = 0; errno = 0; if (read_tty(whatbuf,1) < 0){ if (!errno) errno = EINTR; if (errno == EINTR) return; perror(readerr); sig_catcher(0); } lastchar = *whatbuf; if (*whatbuf & 0200 || no_macros) { *whatbuf &= 0177; goto got_canonical; } if (curmap == Null(KEYMAP*)) goto got_canonical; for (i = (curmap->km_type[*whatbuf] >> KM_GSHIFT) & KM_GMASK; i; --i){ read_tty(&scrchar,1); } switch (curmap->km_type[*whatbuf] & KM_TMASK) { case KM_NOTHIN: /* no entry? */ if (curmap == topmap) /* unmapped canonical */ goto got_canonical; settle_down(); goto tryagain; case KM_KEYMAP: /* another keymap? */ curmap = curmap->km_ptr[*whatbuf].km_km; assert(curmap != Null(KEYMAP*)); break; case KM_STRING: /* a string? */ pushstring(curmap->km_ptr[*whatbuf].km_str,0200); if (++times > 20) { /* loop? */ fputs("\nmacro loop?\n",stdout); settle_down(); } no_macros = FALSE; goto tryagain; } } got_canonical: #ifdef I_SGTTY if (*whatbuf == '\r') *whatbuf = '\n'; #endif if (whatbuf == buf) whatbuf[1] = FINISHCMD; /* tell finish_command to work */ } void pushstring(str,bits) char *str; char_int bits; { register int i; char tmpbuf[PUSHSIZE]; register char *s = tmpbuf; assert(str != Nullch); interp(tmpbuf,PUSHSIZE,str); for (i = strlen(s)-1; i >= 0; --i) pushchar(s[i] ^ bits); } int get_anything() { char tmpbuf[2]; reask_anything: unflush_output(); /* disable any ^O in effect */ standout(); #ifdef VERBOSE IF(verbose) fputs("[Type space to continue] ",stdout); ELSE #endif #ifdef TERSE fputs("[MORE] ",stdout); #endif un_standout(); fflush(stdout); eat_typeahead(); if (int_count) { return -1; } cache_until_key(); getcmd(tmpbuf); if (errno || *tmpbuf == '\f') { putchar('\n') FLUSH; /* if return from stop signal */ goto reask_anything; /* give them a prompt again */ } if (*tmpbuf == 'h') { #ifdef VERBOSE IF(verbose) fputs("\nType q to quit or space to continue.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nq to quit, space to continue.\n",stdout) FLUSH; #endif goto reask_anything; } else if (*tmpbuf != ' ' && *tmpbuf != '\n') { carriage_return(); erase_eol(); /* erase the prompt */ carriage_return(); return *tmpbuf == 'q' ? -1 : *tmpbuf; } if (*tmpbuf == '\n') { page_line = LINES - 1; carriage_return(); erase_eol(); carriage_return(); } else { page_line = 1; if (erase_screen) /* -e? */ clear(); /* clear screen */ else { carriage_return(); erase_eol(); /* erase the prompt */ carriage_return(); } } return 0; } int pause_getcmd() { unflush_output(); /* disable any ^O in effect */ standout(); #ifdef VERBOSE IF(verbose) fputs("[Type space or a command] ",stdout); ELSE #endif #ifdef TERSE fputs("[CMD] ",stdout); #endif un_standout(); fflush(stdout); eat_typeahead(); if (int_count) return -1; cache_until_key(); getcmd(buf); if (errno || *buf == '\f') return 0; /* if return from stop signal */ else if (*buf != ' ') { carriage_return(); erase_eol(); /* erase the prompt */ carriage_return(); return *buf; } return 0; } void in_char(prompt, newmode, dflt) char *prompt; char_int newmode; char *dflt; { char oldmode = mode; reask_in_char: unflush_output(); /* disable any ^O in effect */ printf("%s [%s] ", prompt, dflt); fflush(stdout); eat_typeahead(); mode = newmode; getcmd(buf); if (errno || *buf == '\f') { putchar('\n') FLUSH; /* if return from stop signal */ goto reask_in_char; /* give them a prompt again */ } setdef(buf,dflt); mode = oldmode; } void in_answer(prompt, newmode) char *prompt; char_int newmode; { char oldmode = mode; reask_in_answer: unflush_output(); /* disable any ^O in effect */ fputs(prompt,stdout); fflush(stdout); eat_typeahead(); mode = newmode; reinp_in_answer: getcmd(buf); if (errno || *buf == '\f') { putchar('\n') FLUSH; /* if return from stop signal */ goto reask_in_answer; /* give them a prompt again */ } if (*buf != ' ' && *buf != '\n') if (!finish_command(TRUE)) goto reinp_in_answer; mode = oldmode; } int print_lines(what_to_print,hilite) char *what_to_print; int hilite; { register char *s; register int i; if (page_line < 0) /* they do not want to see this? */ return -1; for (s=what_to_print; *s; ) { if (page_line >= LINES || int_count) { if (i = -1, int_count || (i = get_anything())) { page_line = -1; /* disable further print_lines */ return i; } } page_line++; if (hilite == STANDOUT) { #ifdef NOFIREWORKS if (erase_screen) no_sofire(); #endif standout(); } else if (hilite == UNDERLINE) { #ifdef NOFIREWORKS if (erase_screen) no_ulfire(); #endif underline(); } for (i=0; i= ' ') putchar(*s); else if (*s == '\t') { putchar(*s); i = ((i+8) & ~7) - 1; } else if (*s == '\n') { i = 32000; } else { i++; putchar('^'); putchar(*s + 64); } s++; } if (i) { if (hilite == STANDOUT) un_standout(); else if (hilite == UNDERLINE) un_underline(); if (AM && i == COLS) fflush(stdout); else putchar('\n') FLUSH; } } return 0; } void page_init() { page_line = 1; if (erase_screen) clear(); else putchar('\n') FLUSH; } void pad(num) int num; { register int i; for (i = num; i; --i) putchar(PC); fflush(stdout); } /* echo the command just typed */ #ifdef VERIFY void printcmd() { if (verify && buf[1] == FINISHCMD) { if (*(unsigned char *)buf < ' ') { putchar('^'); putchar(*buf | 64); backspace(); backspace(); } else { putchar(*buf); backspace(); } fflush(stdout); } } #endif void rubout() { backspace(); /* do the old backspace, */ putchar(' '); /* space, */ backspace(); /* backspace trick */ } void reprint() { register char *s; fputs("^R\n",stdout) FLUSH; for (s = buf; *s; s++) { if (*(unsigned char *)s < ' ') { putchar('^'); putchar(*s | 64); } else putchar(*s); } } void home_cursor() { char *tgoto(); if (!*HO) { /* no home sequence? */ if (!*CM) { /* no cursor motion either? */ fputs("\n\n\n", stdout); return; /* forget it. */ } tputs(tgoto(CM, 0, 0), 1, putchr); /* go to home via CM */ return; } else { /* we have home sequence */ tputs(HO, 1, putchr); /* home via HO */ } } void goto_line(from,to) /* assumes caller is already at beginning of line */ int from,to; { char *tgoto(), *str; int cmcost; if (from == to) return; if (*CM && !muck_up_clear) { str = tgoto(CM,0,to); cmcost = strlen(str); } else { str = Nullch; cmcost = 9999; } if (to > from) { go_down: if (to - from <= cmcost) { while(from++ < to) putchar('\n'); return; } } else if(*UP) { if ((from - to) * upcost <= cmcost) { while(from-- > to) tputs(UP,1,putchr); return; } } else if (cmcost == 9999) { home_cursor(); from = 0; goto go_down; } tputs(str,1,putchr); } void line_col_calcs() { if (LINES > 0) { /* is this a crt? */ if (!initlines || !initlines_specified) /* no -i or unreasonable value for initlines */ if (outspeed >= B9600) /* whole page at >= 9600 baud */ initlines = LINES; else if (outspeed >= B4800)/* 16 lines at 4800 */ initlines = 16; else /* otherwise just header */ initlines = 8; } else { /* not a crt */ LINES = 30000; /* so don't page */ CL = "\n\n"; /* put a couple of lines between */ if (!initlines || !initlines_specified) /* make initlines reasonable */ initlines = 8; } if (COLS <= 0) COLS = 80; } #ifdef SIGWINCH Signal_t winch_catcher(dummy) int dummy; { /* Reset signal in case of System V dain bramage */ sigset(SIGWINCH, winch_catcher); /* Come here if window size change signal received */ #ifdef TIOCGWINSZ { struct winsize ws; char lines[10], cols[10]; if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) { if (LINES != ws.ws_row || COLS != ws.ws_col) { LINES = ws.ws_row; COLS = ws.ws_col; line_col_calcs(); sprintf(lines, "%d", LINES); sprintf(cols, "%d", COLS); export("LINES",lines); export("COLUMNS",cols); if (mode == 't' || mode == 'a' || mode == 'p') forceme("\f"); /* cause a refresh */ /* (defined only if TIOCSTI defined) */ } } } #else /* Well, if SIGWINCH is defined, but TIOCGWINSZ isn't, there's */ /* almost certainly something wrong. Figure it out for yourself, */ /* because I don't know how to deal with it :-) */ ERROR! #endif } #endif void termlib_init() { #ifdef USETITE if (TI && *TI) { tputs(TI,1,putchr); fflush(stdout); } #endif #ifdef USEKSKE if (KS && *KS) { tputs(KS,1,putchr); fflush(stdout); } #endif } void termlib_reset() { #ifdef USETITE if (TE && *TE) { tputs(TE,1,putchr); fflush(stdout); } #endif #ifdef USEKSKE if (KE && *KE) { tputs(KE,1,putchr); fflush(stdout); } #endif } void xmouse_on() { if (use_xterm_mouse && !xmouse_status) { printf("\033[?1001s"); /* save old highlight mouse tracking */ printf("\033[?1000h"); /* enable mouse tracking */ xmouse_status = TRUE; } } void xmouse_off() { if (use_xterm_mouse && xmouse_status) { printf("\033[?10001"); /* disable mouse tracking */ printf("\033[?1001r"); /* restore old highlight mouse tracking */ xmouse_status = FALSE; } } trn-3.6/bits.h0000664000000000000000000000316605660373050010201 0ustar /* $Id: bits.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT char *found_bits INIT(Nullch); EXT ART_NUM found_min; /* if subscripting is faster than shifting on your machine, define this */ #undef USESUBSCRIPT #ifdef USESUBSCRIPT EXT char powerof2[] INIT({1,2,4,8,16,32,64,128}); #define pow2(x) powerof2[x] #else #define pow2(x) (1 << (x)) #endif #define foundart(a) (found_bits[((a)-found_min) / BITSPERBYTE] \ |= pow2(((a)-found_min) % BITSPERBYTE)) #define artismissing(a) (!(found_bits[((a)-found_min) / BITSPERBYTE] \ & pow2(((a)-found_min) % BITSPERBYTE))) EXT int dmcount INIT(0); void bits_init _((void)); void rc_to_bits _((void)); void bits_to_rc _((void)); void setfoundbits _((void)); void setmissingbits _((void)); void onemore _((ARTICLE*)); void oneless _((ARTICLE*)); void onemissing _((ARTICLE*)); void unmark_as_read _((void)); void set_read _((ARTICLE*)); void set_unread _((ARTICLE*)); void delay_unmark _((ARTICLE*)); void mark_as_read _((void)); void check_first _((ART_NUM)); void yankback _((void)); int chase_xrefs _((bool_int)); trn-3.6/.config/0000775000000000000000000000000007041327652010406 5ustar trn-3.6/.config/instruct0000664000000000000000000000002607205277221012200 0ustar ian jon matthew mcv21 trn-3.6/.config/README0000664000000000000000000000020407205305776011267 0ustar This directory created by Configure to save information that should persist across sessions. You may safely delete it if you wish. trn-3.6/rt-mt.h0000664000000000000000000000315205433756744010313 0ustar /* $Id: rt-mt.h,v 3.0 1992/12/14 00:14:09 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ bool mt_init _((void)); bool mt_data _((void)); /* Stuff internal to rt-mt.c */ #ifdef DOINIT #define DB_VERSION 2 typedef char BYTE; typedef short WORD; #ifndef __alpha typedef long LONG; #else typedef int LONG; #endif #define ROOT_ARTICLE 0x0001 /* article flag definitions */ #define HAS_XREFS 0x0004 /* article has an xref line */ typedef struct { LONG root_num; WORD articles; WORD article_cnt; WORD subject_cnt; WORD pad_hack; } PACKED_ROOT; typedef struct { LONG num; LONG date; WORD subject, author; WORD flags; WORD child_cnt; WORD parent; WORD padding; WORD sibling; WORD root; } PACKED_ARTICLE; typedef struct Total { LONG first, last; LONG string1; LONG string2; WORD root; WORD article; WORD subject; WORD author; WORD domain; WORD pad_hack; } TOTAL; typedef struct { BYTE l[sizeof (LONG)]; BYTE w[sizeof (WORD)]; BYTE version; BYTE pad_hack; } BMAP; # ifndef USE_XTHREAD static char *mt_name _((char*)); # endif static int read_authors _((void)); static int read_subjects _((void)); static int read_roots _((void)); static int read_articles _((void)); static int read_ids _((void)); static void tweak_data _((void)); static int read_item(); static void safefree _((char**)); static void mybytemap _((BMAP*)); static void wp_bmap(), lp_bmap(); #endif trn-3.6/rcln.h0000664000000000000000000000170105631212540010161 0ustar /* $Id: rcln.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT ART_NUM *ngmax INIT(NULL); void rcln_init _((void)); #ifdef CATCHUP void catch_up _((NG_NUM,int)); #endif int addartnum _((ART_NUM,char*)); #ifdef MCHASE void subartnum _((ART_NUM,char*)); #endif void prange _((char*,ART_NUM,ART_NUM)); void set_toread _((NG_NUM)); void checkexpired _((NG_NUM,ART_NUM)); trn-3.6/only.h0000664000000000000000000000227005420174105010206 0ustar /* $Id: only.h,v 3.0 1991/09/09 20:23:31 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifndef NBRA #include "search.h" #endif #ifdef ONLY EXT char *ngtodo[NGMAX]; /* restrictions in effect */ # ifdef SPEED_OVER_MEM EXT COMPEX *compextodo[NGMAX]; /* restrictions in compiled form */ # endif #endif EXT int maxngtodo INIT(0); /* 0 => no restrictions */ /* >0 => # of entries in ngtodo */ EXT bool emptyOnly INIT(FALSE); void only_init _((void)); bool inlist _((char*)); /* return TRUE if ngname is in command line list */ /* or if there was no list */ void setngtodo _((char*)); #ifdef ONLY void end_only _((void)); #endif trn-3.6/autosub.h0000664000000000000000000000132505420174057010715 0ustar /* $Id: autosub.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT int auto_subscribe _((char*)); EXT bool matchlist _((char*, char*)); trn-3.6/nntplist.c0000664000000000000000000000661005654303433011104 0ustar /* $Id: nntplist.c,v 3.5 1993/04/18 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "nntpclient.h" void Usage _((void)); void finalize _((int)); char nntp_handle_timeout _((bool_int)); int debug = 0; /* make nntpclient.c happy */ #ifdef USE_GENAUTH char *loginName; #endif int main(argc, argv) int argc; char *argv[]; { char command[32]; char *action = NULL; char *wildarg = NULL; register FILE *out_fp = NULL; while (--argc) { if (**++argv == '-') { switch (*++*argv) { case 'o': if (out_fp || !--argc) Usage(); out_fp = fopen(*++argv, "w"); if (out_fp == NULL) { perror(*argv); exit(1); } break; case 'x': if (wildarg || !--argc) Usage(); wildarg = *++argv; break; default: Usage(); /* NO RETURN */ } } else if (!action) action = *argv; else Usage(); } if (!action) action = "ACTIVE"; if (!out_fp) out_fp = stdout; #ifdef USE_GENAUTH /* get login name */ loginName = getenv("USER"); if (loginName == Nullch) { loginName = getenv("LOGNAME"); #ifdef GETLOGIN if (loginName == Nullch) loginName = getlogin(); #endif } #endif if (!nntp_connect(0)) exit(1); sprintf(command,"LIST %s",action); if (wildarg) sprintf(command+strlen(command)," %s",wildarg); nntp_command(command); #ifdef HAS_SIGHOLD sighold(SIGINT); #endif if (nntp_check(FALSE) != NNTP_CLASS_OK) { fprintf(stderr,"nntplist: Can't get %s file from server.\n",action); fprintf(stderr, "Server said: %s\n", ser_line); finalize(1); } while (nntp_gets(ser_line, sizeof ser_line) >= 0) { if (NNTP_LIST_END(ser_line)) /* while there's another line */ break; /* get it and write it to */ fputs(ser_line, out_fp); putc('\n', out_fp); } #ifdef HAS_SIGHOLD sigrelse(SIGINT); #endif nntp_close(TRUE); return 0; } void Usage() { fprintf(stderr, "Usage: nntplist [-x WildSpec] [-o OutputFile] [type]\n\ \nWhere type is any of the LIST command arguments your server accepts.\n"); exit(1); } void finalize(num) int num; { nntp_close(TRUE); exit(num); } char nntp_handle_timeout(strict) bool_int strict; { nntp_error("\n503 Server timed out.\n"); if (strict) finalize(1); return NNTP_CLASS_FATAL; } static char nomem[] = "trn: out of memory!\n"; /* paranoid version of malloc */ char * safemalloc(size) MEM_SIZE size; { char *ptr; ptr = malloc(size ? size : (MEM_SIZE)1); if (ptr == Nullch) { fputs(nomem,stdout) FLUSH; finalize(1); } return ptr; } /* paranoid version of realloc. If where is NULL, call malloc */ char * saferealloc(where,size) char *where; MEM_SIZE size; { char *ptr; ptr = realloc(where, size ? size : (MEM_SIZE)1); if (!ptr) { fputs(nomem,stdout) FLUSH; finalize(1); } return ptr; } trn-3.6/NEW0000664000000000000000000005252305664226071007450 0ustar This file will aquaint you with the user-visible changes in trn 3.x. If you're upgrading from trn 2.x see the discussion of the -a option, the 't' command (newsgroup selection level), and the 'T' command (article level & in kill files) for slight incompatibilities between trn 2.5 and trn 3.0. Changes from trn 3.5 to trn 3.6: o Added support for slow network connections by: (1) making use of the LIST ACTIVE extension (if available), (2) beginning to display the received article as it is received from nntp, and (3) making the Pnews (posting) script query the group info more efficiently. o Changed nntplist's command-line syntax slightly so that it is more orthoganal and so that it can pass an argument to LIST ACTIVE or LIST NEWSGROUPS. ** If you use nntplist in your own scripts you'll need to switch them over to using the -o (output) option. ** o Added the Ctrl-E command to display the end of the current article. o Enhanced the ':' command to operate on non selected thread/articles (use ::cmd) or to operateon just the current thread (use :.cmd or ::.cmd). o Added a 't' modifier for the search command to start the search at the top of the group. E.g.: /subject string/t:+ The default for searches in the selector has always been the top of the group, so this only affects the command while reading articles. o Improved the catchup command to allow you to specify an article count to leave unread in the group. o Added a new sort order for the article selector -- by 'n'umber. o Enhanced the %( arg = regex ? : ) syntax to allow a % expansion in the regex portion of the comparison. o Enhanced xref code to not go sub-optimal on C news's single-group xref lines. o Added support for generic authentification (available in the reference NNTP 1.5.12 and INN 1.5). o Fixed an elusive crash bug that would not let some people run trn from cron. o Other misc. bug fixes. Changes from trn 3.4.1 to trn 3.5: o Enhanced the -p option to allow you to select how you'd like your postings to be selected. The default (which works the same as before) is -p (select your posting and its replies). Also available is -p+ (select all postings in the thread) and -pp (select the *parent* article and its replies). o Added the forward (^F) command to forward an article via mail. o Improved the tab command (skip cited text) to skip empty lines and choose the quotation character more intelligently. o You can now junk an article in just the current group via a search command using 'x'. E.g. /subject/:x o Included some code from Olaf Titz that allows you to tell trn to transform high-bit characters into their 7-bit ascii equivilents. See the _C command and the -Q option. o Added the back-scroll command: 'B'. o Added the -J option to allow you to join truncated subjects into a common thread. The default for -J is 30 chars, not counting the Re: portion. I have the lines "&-J27" and "X&+J" in my Babylon 5 kill file to join all the Genie posts that get truncated into the proper thread. o Added the -K option to keep a group static (no new articles) while you read it. Useful for people who have a really slow kill file for a group -- use "&-K" and "X&+K" in such a kill file to make only that group stop growing until you exit the group and re-enter. o Added optional mouse support in an xterm window. o Added a new intrp (%q) to get the value of the last quoted input (%"). Useful for using elm for forwarding articles as it needs the answer to the question "To?" on the command line: -EFORWARDPOSTER="elm -i %h -s '%'[subject]' %q" (though you may wish to redefine the FORWARDHEADER variable too). o Fixed bugs in the handling of thread kills in partially-threaded groups (Tj now works even if the -a option wasn't specified) and and in the handling of the subject-kill command (Aj). o New files, HINTS.TRN is like HINTS but in a better format. (I renamed it to avoid conflicts with the directory "hints"). o Portability enhancements to Configure and the support scripts. o Some fairly major and minor bugs fixed. Changes from trn 3.3 to trn 3.4.1: o Made the kill-file handling more consistent in how it deals with the THRU line. It now only ignores the THRU line for all selection commands. You can turn off this exception (and thus make all commands obey the THRU line) by turning off the -k option (using +k). You can also specify the 'I' modifier or the 'N' modifier to killfile searches to have them either ignore or not-ignore the THRU line, respectively. o Changed the %'s interp to not supply the leading and trailing single quotes to make it more useful. o The NNTP trn attempts to reconnect to the news server after it times out. If it is successful, trn continues. o The command /subj/M no longer assumes you meant /subj/r:M. o Automatically-generated killfile commands (e.g. the 'K' command) now escape a '/' that occurs in the subject string. o The _+ command (select whole thread) now starts reading the thread from the first unread article. o The -p self-matching code was improved to match a user's name more exactly so that people with short login names don't get improper matches. o Pnews was enhanced to deal with Followup-To: poster better when you choose to post anyway -- it now puts the user's address into the Cc: header and automatically corrects the Newsgroups: line. o Pnews returns to the prompt after an inews error instead of aborting. You can still choose to 'a'bort, if you like. o Speller now passes the ispell_options from Configure to spell, and Configure has been enhanced to prompt you for the options if either ispell or (new!) vspell can't be found. o Added the file Policy.sh.SH that puts your policy choices from the config.sh file into hints/Policy.sh. This allows you to use the Policy.sh file to prime the Configure choices on multiple machines or in the mthreads package without machine-specific selections getting in the way. o Upgraded Configure to the latest metaconfig release. o Fixed some MIME bugs and made the handling a little smarter. o Nested comment warnings removed from various include files. o Fixed several crash bugs and several nusiance bugs, including: * the problem with the NNTP trn mangling the date of new news groups (which caused either groups to get missed or groups to be re-offered). * the problem with the NNTP trn that caused it to think certain lines that begin with a '.' are the end of the list marker and quit when it got confused. * and lots more... Changes from trn 3.2 to trn 3.3: o Newsetup now looks for NEWSLIB/subscriptions for a default list of groups to subscribe the user to. If the file doesn't exist the NNTP version will attempt to grab it via the LIST SUBSCRIPTIONS command (available in INN and some nntp patches). [HINT: if you want your subscription file to default to all groups in the active file, link your subscription file to your active file -- trn will strip the info past the first space when it processes the .newsrc.] o The file newsnews will now default to a simple version update message instead of a welcome-to trn message (which is now provided by the newsetup file when creating a new .newsrc for a user). I still encourage you to install your own custom newsnews when trn is updated (and trn still doesn't install newsnews automatically -- you have to make this decision for your self). o Redirected and disabled groups (marked by '=' or 'x' in the active file) are now handled better, allowing you to read any remaining articles after a group gets redirected or disabled and warning you to either start using the new group name or that the group will not be receiving any new news. o Mime support is now prompted for in Configure and your system's display/store commands are remembered. We also handle a continued Content-Type header correctly now. o The tick (') interp modifier will generate a tick-quoted string with all ticks inside the string quoted. For example, %'s might generate (INCLUDING the "'"s) 'Ticks aren\'t a problem.' o If the environment variable FAST_PNEWS == y Pnews skips the "Are you sure?" question and the "include file" prompt. You can put -EFAST_PNEWS=y in the global INIT file, if you so desire. o Various bug fixes. Changes from trn 3.1 to trn 3.2: o Configure and the include file structure has been improved to install easier on more systems. o Fixed some non-portable test statements in Pnews. o Added the -Z option to allow you to turn on/off support for the two different database formats (-Zt, -Zo, -Zot, or +Z). o Configure now allows you set the default database support to thread files, overview files, both or none. o Trn and its support scripts handle ~/dirs in the newslib or rnlib better. o Various bug fixes. Changes from trn 3.0 to trn 3.1: o Added a new header-searching syntax: /string/Hheader:cmd. This allows you to match a string on any header that trn knows about and is faster on the NNTP side than full-header matching. Example: /: .../Hlines:j would junk all postings longer than 99 lines. o Two new commands can be put into a group's kill file: *j (kill all articles from THRU to the end of the group) and *X (kill all unselected articles from THRU to the end of the group). o Pnews (using artcheck) now checks the active file as well as the newsgroups file for each group on the Newsgroups line. This lets you know whenever you specify a group that doesn't exist. Also, the NNTP version has been fixed to provide full checking. o Pnews/Rnmail have been modified to allow you to type either upper or lower case for the response letters, and use "sp*ell" in addition to "c*heck" for the spelling checker ("s*end" is unchanged). o Pnews/Rnmail each have a pre-edit signature appending option. If you use the file DOTDIR/.news_sig INSTEAD of .signature Pnews will append your signature before you edit the file. Rnmail uses the file DOTDIR/.mail_sig. Note also that the names may be customized with the environment variables NEWSSIGNATURE and MAILSIGNATURE, if desired (allowing a per-group signature). o Added the modifiers '>' and ')' to interpreted strings. For example: %>f gives you the address only on the From line, while %)f gives you the real name portion. o Added the 'O' command (newsgroup level) that works just like 'o' except that it does not visit empty groups. o The killfile is fully saved before manual editing with Ctrl-K (message-id commands used to be saved only on exit from the group). o The screen is now refreshed when the window size changes. o Trn now makes use of stdlib.h, unistd.h (if available) and does a more intelligent inclusion of time.h & sys/time.h (as needed). o Trn doesn't loop through the current thread if unread articles still exist in it. Also fixed various problems with '>' & 'P'. o The 'D' command in the selector no longer jumps back to the top. o The NNTP version has a different new-article aquisition strategy. Your nntp server must send up-to-date information in response to the GROUP command (INN's nnrpd does) for a group to expand while you're in it. Also, you can now set the minimum time for trn to wait (see -z) before it refetches the active file at the end of the newsgroups. The default is 5 minutes. o Optional metamail support pauses between the headers and the metamail-interpreted article. Also, the 'v' command displays the raw article without metamail processing. o Numerous portability changes and bug fixes. Changes from trn 2.5 to trn 3.0: o Trn is now capable of reading more news database formats. It currently supports news overview (.overview files), mthreads (.thread files), and direct threading of the articles. The NNTP version supports the XTHREAD and XOVER NNTP extensions. If you compile trn with support for both formats it will figure out which groups (or which server) has which type of data and act accordingly. o Mthreads is now a separate package from trn since not everyone will need to use it. Look for it in the same place you found trn. o Trn attempts to build some useful default macros for your terminal's arrow keys. On the article level they move around in the thread; in the selector they change pages (left/right) and switch selections (up/down); on the newsgroup level they move by group (up/down) and enter a group (right). If you don't like this, turn it off with the +A option. o There's a new search scope -- the from line. For example: use /author/f+ to search for and select 'author's articles. o The thread selector has been extended to be a subject and article selector. Use the 'S'et selector command to change modes or use '=' to toggle between the article selector and the subject/thread selector (whichever was last in use). o The selector can now be sorted in a variety of ways: by date, subject, author, article count (in the subject/thread selector), or a combination of subject and date (in the article selector). The default is date order of the oldest unread article in a thread. Use the 'O'rder command to pick a new one or use 'R' to reverse the sort. See also the "-O" option to set your favorite mode and order. You can even put a "&-Oas" command (for example) into a group's kill file to set a per-group default. o The selector allows you to exclude all the non-selected articles from the display (i.e. narrow it) -- use 'E' to toggle this mode. o The selector has two new selection commands: '*' is used to select (or deselect) the current item and all other items with the same subject (useful in the article selector); '#' is used to make an overriding selection that immediately reads the current item ignoring all other selections. o You can now type 'M' in the selector to mark the current item's articles as read-but-returning and press 'Y' to yank back and select these articles before exiting the group. o Selections via searches are article-oriented (/subj/+) or thread-/subject-oriented (/subj/++). The article selector's default command is "+", while the thread/subject selector's is "++". In other words doing a "/subj" search with no specified command selects whatever type of object you're looking at in the selector. o If you specify the "-p" option, your postings and any replies to them are auto-selected whenever trn encounters them. o The '+' command in a non-threaded group visits the subject selector. You can also use "_a", "_s", "_t" or "_T" to force the article, subject, thread, or thread-but-I'll-settle-for-subject selector. o The selector displays subjects/threads that are partially-selected with a '*'. Fully-selected items are marked with a '+', as before. Use the article selector (possibly with 'E'xclusive set) to see which articles are selected in a partially-selected group (or just read them). o The selector remembers which subjects you selected (and didn't kill) and marks any newly-arriving articles in these subjects as selected until you exit the group. o The medium display mode of the thread selector has been improved to make it more readable. o The selector will leave out the middle portion of a subject that is too long to display the last two words of the subject. If you don't like this, use the -u option to leave them unbroken. o 'T'hread KILL commands now use message-ids to either junk or select articles. The 'T' command has been extended to be more flexible on the article level and has been added to the selector. o Another new command 'A'dds selection or kill commands to the KILL file, and works from both the article level and the selector. o The tree display has been updated to display unread-but-not- selected articles as . Other unread articles are still [x], while read articles still display as (x). o Trn can enter a group without thread information available and thread it in the background while you read. Articles that have references that may or may not exist show up as "(?)". If you visit an article like this and wait there, the screen will update when we know for sure one way or the other (after processing more of the group). o The -a option is used to tell trn to thread all of the articles on entry to the group. If you don't specify this option a group may have a few (or many) articles that get threaded in the background and won't show up on the tree display until trn processes them. o Pressing "_+" on the article level will select the entire thread associated with the current article -- useful if you've selected individual articles and wish to read the rest of the discussion. Use "_-" to deselect the current thread. o The commands _N and _P move to the next and previous article in numberical (arrival) order (article level). Thus, you can use the command "._P" on the newsgroup level to start reading a group from the very last article to arrive. o The 't' command on the newsgroup level now turns OFF reading a group with threads (this setting is stored in the .newsrc file, so it is remembered from session to session). Trn 2.x used this to force threading to be turned ON, but it wasn't usually needed for normal operation. To temporarily turn threads on once inside a non-threaded group, use the 't' or "_t" commands (article level) or the "St" command (selector). o Trn now checks for the environment variable TRNMACRO on startup (which defaults to DOTDIR/.trnmac) before checking for the usual RNMACRO (DOTDIR/.rnmac) file. If you're running trn in its rn- compatible mode, only RNMACRO is tried. o The threaded and non-threaded data in a group has been unified, resulting in more cached information on the non-threaded side (such as the from line) and more efficient handling of missing articles, just to name two benefits. o The newsgroup information is freed when we enter a new group, not when we exit the current group. This means that if you quit out of a group (even accidentally), you can go back in and everything is still there except the selections, which get cleared on group exit. o KILL file processing will now ignore the THRU line as much as possible without slowing down the handling of KILL files. If you have really slow searches (header or article searches) or you use trn without a database it will use the THRU line to only search an article one time (as it would in rn). This means that if you have subject-oriented selection commands you don't have to worry about missing articles if you don't read all of them the first time you enter a group after they arrive. This also means that you won't have to edit your local kill file to remove the THRU line to force a re-scan -- this is now unnecessary. o Header parsing is now done in-memory, making threading and caching of articles much faster. This especially helps out NNTP users because trn used to write a tmp file for every header parse. o Several new mode letters (accessed by %m in macros) were added. The most significant are 'f' for the end (Finis) of the newsgroup selection level (instead of 'n') and 'e' for the end of the article reading level (instead of 'a'). o A new % modifier has been introduced: "%:FMTx". This allows you to apply a printf-style column format to a regular %x expansion. For example, %:-50.50s would left-justify the subject into 50 characters, exactly. o The -f option will make trn go a little faster by getting rid of the delay/prompt after kill file processing, printing the "skipping article" message, and printing the "Depositing KILL command" message. This is the default if -t (terse) is specified, but can be overriden by specifying +f after the -t option. o A new option for the gadget-conscious (-B) displays a spinner when trn is processing articles in the background. o Added the -G option to make the newsgroup 'g'o command look for near matches (for those typing mistakes). o New newsgroups that are left unsubscribed are not appended to the .newsrc unless you use the -I option or you're running an NNTP version that does not use the NEWGROUPS code. o Support for metamail's mime handling is now built into the code -- see the METAMAIL define in common.h. o Pnews does more checking of your article before posting, has a spelling-check option, and allows the Cc: header to be used to send mail while posting the article. Changes from trn 1.0.3 to trn 2.5: o Added the '(' and ')' commands (article level) to move to an article's previous/next sibling, including "cousin" siblings. o The 'A'bandon command (newsgroup level) forgets all changes to the current newsgroup since you first started trn. o The thread selector now allows you type type 'c'/'y' to catchup the group without chasing cross-references. o Added 'z' and 'Z' commands (article level) supersede the current article ('Z' also including the original text). o The g command (newsgroup level) will go to a newsgroup by number where the the number corresponds to that shown in the 'L'ist command. o Added the "-o" option to use the old method of junking articles in the thread commands (they didn't chase cross-references in trn 1.x) o Added the "-b" option to read articles in a breadth-first descent of the article tree. You can specify this command at runtime to switch from breadth-first (type "&-b") to depth-first ("&+b). o Added the "-j" option to tell trn to pass control characters through the pager unharmed. trn-3.6/HINTS.TRN0000664000000000000000000001632105645405225010341 0ustar You can specify trn environment variables in an initialization file with all the options. See the first hint for details on that. Certain environment variables, like RNINIT, must be set in your shell (how else does trn know to look there for the rest?). Others, like REPLYTO may be useful in other programs, so you could consider putting that in your shell startup file too. But most of them are specific, like MAILHEADER, so you might as well set them in the init file with the -E option. That's why most of them look like -EMAILHEADER=xx rather than MAILHEADER=xx -- to remind you. Helpful hints: o You can make a file that contains all the flags and initialization that trn will use at startup. When you're done, point an environment variable TRNINIT to the file. A good choice is "TRNINIT=~/.trninit". You can put multiple lines in this init file and/or separate options by spaces. o Two new and useful flags are the -p and -G flags. -p means automatically select any thread that you have posted a message to. This makes it very easy to track replies to your insightful comments :-) (to automatically select other threads, try the 'T' command). -G tells the 'g' command to use a loose match algorithm on groups it can't find. So if you type "g news.softwre.raeders.genrl", it will assume that you meant to type news.software.readers and take you there. It is usually easier to type "/reader" than using a 'g' command, but use -G too. o The 'X' command in the selector (kinda like catchup/yes) can be made the default action on the last page of selections by using the command-line option: -XX. This lets you browse all the pages, making selections as you go, and then mark everything that was not selected as read when you press space at the last page. o If you like to select a little, read a little, and then select a little more, try using the command-line option: -XDD. This makes the 'D' command the default on all pages of the selector. Thus you can select the things you want to read on the current page, press space, and you will read the selected articles immediately (if there were any). All other articles on the current page will be marked as read. o To use mush to send all R)eplies: -EMAILPOSTER="mush -h %h" o To use elm to send all R)eplies: -EMAILHEADER="\\000" -EMAILPOSTER="elm -i %h -s '%'s' %t" o To tailor the time field: A good example is -ELOCALTIMEFMT="%A, %B %e %Y, %r %Z" which becomes: Date: Saturday, June 12 1993, 08:15:32 PM PDT. (The default is "%a %b %e %X %Z %Y", which is like the date(1) command). See the strftime(3C) man page for more details on other % characters. o To tailor the "In article , joe@user (Joe User)" line: person's name only: %)f person's address only: %>f For example: -EATTRIBUTION='According to %)f <%>f>:' o A better kill file scheme than the default (which creates obtuse directories all over your ~/News directory) is to use the following: -EKILLGLOBAL="%p/Kill/Global" -EKILLLOCAL="%p/Kill/%C" Where %p is your news directory, usually ~/News, and %C is the full name of the group, like sci.bio. This means the kill file for sci.bio is in ~/News/Kill/sci.bio instead of ~/News/sci/bio/KILL. Think about this next time you subscribe to alt.swedish.chef.bork.bork.bork :-) Note that you need a file system that allows long file names to make this work. o If you're tired of endless subscription notices to newsgroups you could care less about, look at the AUTOUNSUBSCRIBE variable. There's a companion AUTOSUBSCRIBE function too. For example, to automatically unsubscribe all NEW alt groups, use ^alt.*. More dramatic, to unsubscribe to everything BUT certain groups, use "*,!^local.*,!^comp.*", for example. If you're REALLY tired of them, use -q to tell trn to not even check for new groups at all. o If you want replies to your postings to go to a machine other than the machine you're posting from, set the REPLYTO environment variable. For example, you might set this if you were trn's author: -EREPLYTO="davison@borland.com (Wayne Davison)" o You might like to redefine the 'u' keys in the following way: 'u' visits the "Set unread?" prompt (except in the thread selector), 'U' goes directly to the already-read article selector, and Ctrl-U unsubscribes from the group (even while in the thread selector). Put the following 3 macros in your .rnmac file to accomplish this (or change TRNMACROS to .trnmac and put them there): u %(%m=[aefnp]?U:u) U %(%m=[aefnp]?U+:U) ^U %(%m=t?+u:u) o If you like to be able to move forward/backward by article number more often than you need to search by subject, redefine ^N and ^P to be _N and _P by putting these lines into your macro file: ^P %(%m=[aep]?_P:^P) ^N %(%m=[aep]?_N:^N) o If you like the way that 'q' worked in the thread selector in trn 1.x, put the following macro in your macro file: q %(%m=t?+:q) o If you would like the 'f' command to always answer yes to the "Are you starting an unreleated topic?" question, put this line into your macro file: f %(%m=[ap]?fy^m:f) o If you want to be able to save your shar headers in a file as they are extracted and you have access to "unshar" (a program that can extract shar files while saving the headers in UNSHAR.HDR), twiddle the external extract command to send the entire article: -EEXSAVER="%e <%A" and then point the UNSHAR variable at unshar instead of /bin/sh: -EUNSHAR="unshar -s" Note that this assumes that any other commands you execute with "e dir|command" can also handle the entire article as input (uuencoded and shipped files are not affected). o The following lines represent trn's default headers for posting an article. If you want to customize your header copy this to your .trninit file and modify it (and _don't_ use Pnews directly to post, use trn -- the ".f" command from newsgroup prompt works well for this). -ENEWSHEADER="%(%[followup-to]=^$?:%(%[followup-to]=^%n$?:X-ORIGINAL-NEWSGROUPS: %n ))Newsgroups: %(%F=^$?%C:%F) Subject: %(%S=^$?%\" Subject: \":Re: %S) Summary: Expires: %(%R=^$?:References: %R )Sender: Followup-To: %(%{REPLYTO}=^$?:Reply-To: %{REPLYTO} )Distribution: %(%i=^$?%\"Distribution: \":%D) Organization: %o Keywords: %[keywords] Cc: %(%F=poster?%t:%(%F!=@?:%F)) " If you're curious, trn's author has the following .trninit file: ---------------------------------------------------------------------- -XX -B -N -f -x11ms "-F> " -pp -G -u +e -mu -hunrecognized -ELOCALTIMEFMT="%A, %B %e %Y, %r %Z" -ESELECTCHARS="abdefgijlmorstuvwxyz1234567890BCFGHIKVW" -EKILLLOCAL="%p/Kill/%C" -EKILLGLOBAL="%p/Kill/Global" -EMAILPOSTER="elm -i %h -s '%'s' %t" -EFORWARDPOSTER="elm -i %h -s '%'[subject]' %q" -EATTRIBUTION="According to %)f <%>f>:" ---------------------------------------------------------------------- and the following .trnmac file: ---------------------------------------------------------------------- u %(%m=[aenp]?U:u) U %(%m=[aenp]?U+:U) ^U %(%m=t?+u:u) o %(%m=[nf]?O:o) O %(%m=[nf]?o:O) ~T s ~/Mail/trn ~M | /bin/mail davison ^[^[ ^[ ---------------------------------------------------------------------- trn-3.6/rt-ov.h0000664000000000000000000000077305420174112010300 0ustar /* $Id: rt-ov.h,v 3.0 1992/12/14 00:14:15 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ bool ov_init _((void)); bool ov_data _((ART_NUM,ART_NUM,bool_int)); void ov_close _((void)); /* Stuff internal to rt-ov.c */ #ifdef DOINIT static ARTICLE *ov_parse _((char*, ART_NUM)); #ifndef USE_XOVER static char *ov_name _((char*)); #endif #endif trn-3.6/help.c0000664000000000000000000004317105663312257010170 0ustar /* $Id: help.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "trn.h" #include "term.h" #include "INTERN.h" #include "help.h" void help_init() { ; } int help_page() { int cmd; #ifdef PAGERHELP doshell(sh,filexp(PAGERHELP)); #else page_init(); if ((cmd = print_lines("\ Paging commands:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ SP Display the next page.\n\ x Display the next page decrypted (rot13).\n\ d Display half a page more.\n\ CR Display one more line.\n\ ^R,v,^X Restart the current article (v=verbose header, ^X=rot13).\n\ ",NOMARKING)) || (cmd = print_lines("\ b Back up one page.\n\ ^E Display the last page of the article.\n\ ^L,X Refresh the screen (X=rot13).\n\ _C Switch characterset conversion.\n\ ",NOMARKING)) || (cmd = print_lines("\ t Display the entire article tree and all its subjects.\n\ ",NOMARKING)) || (cmd = print_lines("\ g pat Go to (search forward within article for) pattern.\n\ G Search again for current pattern within article.\n\ ^G Search for next line beginning with \"Subject:\".\n\ TAB Search for next line beginning with a different character.\n\ q Quit the pager, go to end of article. Leave article read or unread.\n\ j Junk this article (mark it read). Goes to end of article.\n\ \n\ ",NOMARKING)) || (cmd = print_lines("\ The following commands skip the rest of the current article, then behave\n\ just as if typed to the 'What next?' prompt at the end of the article:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ n Scan forward for next unread article.\n\ N Go to next article.\n\ ^N Scan forward for next unread article with same title.\n\ p,P,^P Same as n,N,^N, only going backwards.\n\ - Go to previously displayed article.\n\ ",NOMARKING)) || (cmd = print_lines("\ <, > Browse the previous/next selected thread. If no threads are selected,\n\ all threads that had unread news upon entry to the group are considered\n\ selected for browsing. Entering an empty group browses all threads.\n\ [, ] Go to article's parent/child (try left-/right-arrow also).\n\ (, ) Go to article's previous/next sibling (try up-/down-arrow also).\n\ {, } Go to tree's root/leaf.\n\ \n\ ",NOMARKING)) || (cmd = print_lines("\ The following commands also take you to the end of the article.\n\ Type h at end of article for a description of these commands:\n\ ",STANDOUT)) || (cmd = print_lines("\ # $ & / = ? c C f F k K ^K J , m M number e r R ^R s S u U v w W Y ^ |\n\ \n\ (To return to the middle of the article after one of these commands, type ^L.)\n\ ",NOMARKING)) ) return cmd; #endif return 0; } int help_art() { int cmd; #ifdef ARTHELP doshell(sh,filexp(ARTHELP)); #else page_init(); if ((cmd = print_lines("\ Article Selection commands:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ n,SP Find next unread article (follows discussion-tree in threaded groups).\n\ ",NOMARKING)) || (cmd = print_lines("\ N Go to next article.\n\ ^N Scan forward for next unread article with same subject in date order.\n\ p,P,^P Same as n,N,^N, only going backwards.\n\ _N,_P Go to the next/previous article numerically.\n\ - Go to previously displayed article.\n\ ",NOMARKING)) || (cmd = print_lines("\ <, > Browse the previous/next selected thread. If no threads are selected,\n\ all threads that had unread news upon entry to the group are considered\n\ selected for browsing. Entering an empty group browses all threads.\n\ [, ] Go to article's parent/child (try left-/right-arrow also).\n\ (, ) Go to article's previous/next sibling (try up-/down-arrow also).\n\ {, } Go to tree's root/leaf.\n\ t Display the entire article tree and all its subjects.\n\ ",NOMARKING)) || (cmd = print_lines("\ number Go to specified article.\n\ range{,range}:command{:command}\n\ Apply one or more commands to one or more ranges of articles.\n\ Ranges are of the form: number | number-number. You may use . for\n\ the current article, and $ for the last article.\n\ ",NOMARKING)) || (cmd = print_lines("\ Valid commands are: e, j, m, M, s, S, t, T, |, +, ++, -, and --.\n\ :cmd Perform a command on all the selected articles.\n\ ::cmd Perform a command on all non-selected articles.\n\ :.cmd Perform a command on the current thread or its selected articles.\n\ ::.cmd Perform a command on the unselected articles in the current thread.\n\ ",NOMARKING)) || (cmd = print_lines("\ /pattern/modifiers\n\ Scan forward for article containing pattern in the subject line.\n\ (Use ?pat? to scan backwards; append f to scan from lines, h to scan\n\ whole headers, a to scan entire articles, r to scan read articles, c\n\ to make case-sensitive, t to scan from the top of the group.)\n\ ",NOMARKING)) || (cmd = print_lines("\ /pattern/modifiers:command{:command}\n\ Apply one or more commands to the set of articles matching pattern.\n\ Use a K modifier to save entire command to the KILL file for this\n\ newsgroup. Commands m and M, if first, imply an r modifier.\n\ Valid commands are the same as for the range command.\n\ ",NOMARKING)) || (cmd = print_lines("\ f,F Submit a followup article (F = include this article).\n\ r,R Reply through net mail (R = include this article).\n\ ^F Forward article through net mail.\n\ e dir{|command}\n\ Extract to directory using /bin/sh, uudecode, unship, or command.\n\ s ... Save to file or pipe via sh.\n\ S ... Save via preferred shell.\n\ w,W Like s and S but save without the header.\n\ | ... Same as s|...\n\ ",NOMARKING)) || (cmd = print_lines("\ C Cancel this article, if yours.\n\ ^R,v Restart article (v=verbose).\n\ ^X Restart article, rot13 mode.\n\ _C Switch characterset conversion.\n\ c Catch up (mark all articles as read).\n\ b Back up one page.\n\ ",NOMARKING)) || (cmd = print_lines("\ ^E Display the last page of the article.\n\ ^L Refresh the screen. You can get back to the pager with this.\n\ X Refresh screen in rot13 mode.\n\ ^ Go to first unread article. Disables subject search mode.\n\ $ Go to end of newsgroup. Disables subject search mode.\n\ ",NOMARKING)) || (cmd = print_lines("# Print last article number.\n\ & Print current values of command-line switches.\n\ &switch {switch}\n\ Set or unset more switches.\n\ && Print current macro definitions.\n\ &&def Define a new macro.\n\ j Junk this article (mark it read). Stays at end of article.\n\ ",NOMARKING)) || (cmd = print_lines("\ m Mark article as still unread.\n\ M Mark article as read but to-return on group exit or Y command.\n\ Y Yank back articles marked as to-return via the M command.\n\ k Kill current subject (mark articles as read).\n\ , Mark current article and its replies as read.\n\ ",NOMARKING)) || (cmd = print_lines("\ J Junk entire thread (mark all subjects as read in this thread).\n\ A Add current subject to memorized commands (selection or killing).\n\ T Add current (sub)thread to memorized commands (selection or killing).\n\ K Mark current subject as read, and save command in KILL file.\n\ ^K Edit local KILL file (the one for this newsgroup).\n\ ",NOMARKING)) || (cmd = print_lines("\ = List subjects of unread articles.\n\ + Start the selector in whatever mode it was last in.\n\ _a Start the article selector.\n\ _s Start the subject selector.\n\ _t Start the thread selector.\n\ ",NOMARKING)) || (cmd = print_lines("\ _T Start the thread selector if threaded, else the subject selector.\n\ U Unread some news -- prompts for thread, subthread, all, or select.\n\ u Unsubscribe from this newsgroup.\n\ q Quit this newsgroup for now.\n\ Q Quit newsgroup, staying at current newsgroup.\n\ ",NOMARKING)) ) return cmd; #endif return 0; } int help_ng() { int cmd; #ifdef NGHELP doshell(sh,filexp(NGHELP)); #else page_init(); if (cmd = print_lines("\ Newsgroup Selection commands:\n\ ",STANDOUT) ) return cmd; if (ng != nextrcline) { if ((cmd = print_lines("\ \n\ y Do this newsgroup now.\n\ SP Do this newsgroup, executing the default command listed in []'s.\n\ .cmd Do this newsgroup, executing cmd as first command.\n\ + Enter this newsgroup through the selector (like typing .+).\n\ = Start this newsgroup, but list subjects before reading articles.\n\ U Enter this newsgroup by way of the \"Set unread?\" prompt.\n\ u Unsubscribe from this newsgroup.\n\ ",NOMARKING)) ) return cmd; } if ((cmd = print_lines("\ t Toggle the newsgroup between threaded and unthreaded reading.\n\ c Catch up (mark all articles as read).\n\ A Abandon read/unread changes to this newsgroup since you started trn.\n\ n Go to the next newsgroup with unread news.\n\ N Go to the next newsgroup.\n\ p Go to the previous newsgroup with unread news.\n\ P Go to the previous newsgroup.\n\ ",NOMARKING)) || (cmd = print_lines("\ - Go to the previously displayed newsgroup.\n\ 1 Go to the first newsgroup.\n\ ^ Go to the first newsgroup with unread news.\n\ $ Go to the end of newsgroups.\n\ ",NOMARKING)) || (cmd = print_lines("\ g name Go to the named newsgroup. Subscribe to new newsgroups this way too.\n\ /pat Search forward for newsgroup matching pattern.\n\ ?pat Search backward for newsgroup matching pattern.\n\ (Use * and ? style patterns. Append r to include read newsgroups.)\n\ ",NOMARKING)) || (cmd = print_lines("\ l pat List unsubscribed newsgroups containing pattern.\n\ m name Move named newsgroup elsewhere (no name moves current newsgroup).\n\ o pat Only display newsgroups matching pattern. Omit pat to unrestrict.\n\ O pat Like o, but skip empty groups.\n\ a pat Like o, but also scans for unsubscribed newsgroups matching pattern.\n\ L List current .newsrc.\n\ ",NOMARKING)) || (cmd = print_lines("\ & Print current command-line switch settings.\n\ &switch {switch}\n\ Set (or unset) more command-line switches.\n\ && Print current macro definitions.\n\ &&def Define a new macro.\n\ !cmd Shell escape.\n\ ",NOMARKING)) || (cmd = print_lines("\ q Quit trn.\n\ x Quit, restoring .newsrc to its state at startup of trn.\n\ ^K Edit the global KILL file. Use commands like /pattern/j to suppress\n\ pattern in every newsgroup.\n\ v Print version and the address for reporting bugs.\n\ ",NOMARKING)) ) return cmd; #endif if (cmd = get_anything()) return cmd; show_macros(); return 0; } #ifdef ESCSUBS int help_subs() { int cmd; #ifdef SUBSHELP doshell(sh,filexp(SUBSHELP)); #else page_init(); if ((cmd = print_lines("\ Valid substitutions are:\n\ ",STANDOUT)) || (cmd = print_lines("\ \n\ a Current article number\n\ A Full name of current article (%P/%c/%a)\n\ b Destination of last save command, often a mailbox\n\ B Bytes to ignore at beginning of last saved article\n\ ",NOMARKING)) || (cmd = print_lines("\ c Current newsgroup, directory form\n\ C Current newsgroup, dot form\n\ d Full name of newsgroup directory (%P/%c)\n\ D Distribution line from current article\n\ e The last command executed to extract data from an article\n\ E The last extraction directory\n\ ",NOMARKING)) || (cmd = print_lines("\ f Who the current article is from\n\ F Newsgroups to followup to (from Newsgroups and Followup-To)\n\ h (This help message)\n\ H Host name (yours)\n\ i Message-I.D. line from current article, with <>\n\ I Reference indicator mark (see -F switch)\n\ ",NOMARKING)) || (cmd = print_lines("\ l News administrator's login name, if any\n\ L Login name (yours)\n\ ",NOMARKING)) || (cmd = print_lines("\ m Current mode, first letter of (init,newsgroup,thread,article,pager,\n\ unread,Add,Catchup,Delete-bogus,Mailbox,Resubscribe)\n\ ",NOMARKING)) || (cmd = print_lines("\ M Number of article marked with M\n\ n Newsgroups from current article\n\ N Full name (yours)\n\ o Organization (yours)\n\ O Original working directory (where you ran trn from)\n\ ",NOMARKING)) || (cmd = print_lines("\ p Your private news directory (from -d)\n\ P Public news spool directory\n\ r Last reference (parent article id)\n\ R References list for followup article\n\ ",NOMARKING)) || (cmd = print_lines("\ s Subject, with all Re's and (nf)'s stripped off\n\ S Subject, with one Re stripped off\n\ t New To line derived from From and Reply-To (Internet format)\n\ T New To line derived from Path\n\ u Number of unread articles\n\ ",NOMARKING)) || (cmd = print_lines("\ U Number of unread articles not counting the current article (when\n\ threads are selected, the count only reflects selected articles)\n\ v The number of extra (unselected) articles, not counting the current\n\ one if it is unselected\n\ W Where thread files are saved\n\ x News library directory\n\ X Trn library directory\n\ z Length of current article in bytes\n\ Z Number of selected threads\n\ ",NOMARKING)) || (cmd = print_lines("\ ~ Your home directory\n\ . Directory containing the \"dot\" files, such as .newsrc\n\ # A counter in multi-article saves\n\ $ Current process number\n\ / Last search string\n\ ESC Run preceding command through % interpretation\n\ ",NOMARKING)) || (cmd = print_lines("\ Put ^ in the middle to capitalize the first letter: %^C = Rec.humor\n\ Put _ in the middle to capitalize the last component: %_c = rec/Humor\n\ Put \\ in the middle to quote regexp and % characters in the resulting string\n\ Put :FMT in the middle to format the result printf-style: %:-30.30t\n\ ",NOMARKING)) ) return cmd; #endif return 0; } #endif int help_select() { int cmd; page_init(); if ((cmd = print_lines("\ Selection commands:\n\ ",STANDOUT)) || (cmd = print_lines("\n\ a-z,0-9 Select/deselect the indicated item by its letter or number. Many of\n\ the alpha letters are omitted for the following commands.\n\ SP Perform the default command (usually > or Z).\n\ CR Start reading. Selects the current item if nothing is selected.\n\ Z,TAB Start reading. If nothing is selected, read all unread articles.\n\ . Toggle the current item's selection.\n\ * Same as '.' except that it affects all items with the same subject.\n\ ",NOMARKING)) || (cmd = print_lines("\ # Read the current item only, temporarily ignoring all other selections.\n\ k, ',' Mark the current item as killed.\n\ m, \\ Unmark the current item.\n\ - Set a range, as in d - f. Repeats the last marking action.\n\ @ Toggle the selection of all visible items.\n\ M Mark the current item's article(s) as to-return and kill the item.\n\ Y Yank back and select articles marked to return via M.\n\ ",NOMARKING)) || (cmd = print_lines("\ E Toggle exclusion of non-selected items from the selection list.\n\ n, ] Move down to the next item (try down-arrow also).\n\ p, [ Move up to the previous item (try up-arrow also).\n\ <, > Go to previous/next page (try left-/right-arrow also).\n\ ^, $ Go to first/last page.\n\ S Set what the selector displays: threads, subjects, or articles.\n\ If the group is unthreaded, choosing threads will thread it.\n\ ",NOMARKING)) || (cmd = print_lines("\ = Toggle between the article and thread/subject selector.\n\ O Set the selector's order. A separate default is kept for the\n\ article and subject/thread selector.\n\ R Reverse the current sort order.\n\ L Switch the display between a short style without authors and a\n\ medium or long style with authors.\n\ U Switch between selecting unread/read articles.\n\ X Mark all unselected articles as read and start reading.\n\ ",NOMARKING)) || (cmd = print_lines("\ D Mark unselected articles on the current page as read. Start\n\ reading if articles were selected, else go to next page.\n\ J Junk all selected articles (mark them as read).\n\ c Catch up -- marks ALL articles as read without chasing xrefs.\n\ A Add current subject to memorized commands (selection or killing).\n\ T Add current thread to memorized commands (selection or killing).\n\ ^K Edit local KILL file (the one for this newsgroup).\n\ N Leave this group as-is and go on to the next one.\n\ ",NOMARKING)) || (cmd = print_lines("\ P Leave this group as-is and go on to the previous one.\n\ :cmd Perform a command on all the selected articles (use :p to post).\n\ ::cmd Perform a command on all non-selected articles.\n\ :.cmd Perform a command on the current thread or its selected articles.\n\ ::.cmd Perform a command on the unselected articles in the current thread.\n\ /pattern/modifiers\n\ Scan all articles for a subject containing pattern.\n\ (Append f to scan the from line, h to scan whole headers, a to scan\n\ ",NOMARKING)) || (cmd = print_lines("\ entire articles, c to make it case-sensitive, r to scan read articles\n\ (assumed when you are selecting read articles to set unread.)\n\ /pattern/modifiers:command{:command}\n\ Apply one or more commands to the set of articles matching pattern.\n\ Use a K modifier to save entire command to the KILL file for this\n\ newsgroup. Commands m and M, if first, imply an r modifier.\n\ Valid commands are: e, E, j, m, M, s, S, t, T, !, =, ',' and the\n\ article/thread (de)selection commands: +/++ (-/--).\n\ ",NOMARKING)) || (cmd = print_lines("\ & View or set command line switches.\n\ && View or set macro definitions.\n\ !cmd Escape to a subshell.\n\ h, ? This help message.\n\ ESC, + Leave the selector but stay in the group (at last visited article).\n\ q Quit the selector and the group.\n\ Q Quit group and return to news group selection prompt for this group.\n\ ",NOMARKING)) ) return cmd; return 0; } trn-3.6/trn.h0000664000000000000000000000273605663312267010053 0ustar /* $Id: trn.h,v 4.4 1991/09/09 20:27:37 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT char *ngname INIT(Nullch); /* name of current newsgroup */ EXT int ngnlen INIT(0); /* current malloced size of ngname */ EXT char *ngdir INIT(Nullch); /* same thing in directory name form */ EXT int ngdlen INIT(0); /* current malloced size of ngdir */ EXT NG_NUM ng INIT(0); /* current newsgroup index into rcline and toread */ EXT NG_NUM current_ng INIT(0); /* stable current newsgroup so we can ditz with ng */ EXT NG_NUM starthere INIT(0); /* set to the first newsgroup with unread news on startup */ EXT char *spool INIT(Nullch); /* public news spool directory */ EXT char *threaddir INIT(Nullch); /* directory for thread data */ EXT char *overviewdir INIT(Nullch); /* directory for overview data */ void rn_init _((void)); void main _((int,char**)); void set_ngname _((char*)); char *getngdir _((char*)); void trn_version _((void)); trn-3.6/patch0000664000000000000000000000336407424607726010123 0ustar It appears that the problem is a fixed-length prompt string buffer in rcstuff.c. The following patch increases the length of the buffer and also changes all the sprintfs to snprintfs, so that in the worst case the prompt will merely be truncated instead of the stack being corrupted. This patch was developed for trn version 3.6. The same flawed approach (sprintf to a fixed length buffer) is also used in other places in trn. It seems like it could use a general cleanup, but I don't really have time for that at the moment, and with version 4 already existing I'm not sure it's worth it. --- rcstuff.c.orig Sat Aug 20 16:47:59 1994 +++ rcstuff.c Sun Jan 6 21:55:51 2002 @@ -282,7 +282,8 @@ int flags; { char *ntoforget; - char promptbuf[128]; +#define PBLEN 240 + char promptbuf[PBLEN+1]; int autosub; #ifdef VERBOSE @@ -347,11 +348,11 @@ } else { #ifdef VERBOSE IF(verbose) - sprintf(promptbuf,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname); + snprintf(promptbuf,PBLEN,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname); ELSE #endif #ifdef TERSE - sprintf(promptbuf,"\nSubscribe %s?",ngname); + snprintf(promptbuf,PBLEN,"\nSubscribe %s?",ngname); #endif reask_add: in_char(promptbuf,'A',"ynYN"); @@ -418,13 +419,13 @@ else if (rcchar[ng] == NEGCHAR) { /* unsubscribed? */ #ifdef VERBOSE IF(verbose) - sprintf(promptbuf, + snprintf(promptbuf, PBLEN, "\nNewsgroup %s is unsubscribed -- resubscribe?",ngname) FLUSH; ELSE #endif #ifdef TERSE - sprintf(promptbuf,"\nResubscribe %s?",ngname) + snprintf(promptbuf,PBLEN,"\nResubscribe %s?",ngname) FLUSH; #endif reask_unsub: trn-3.6/EXTERN.h0000664000000000000000000000132505420174050010231 0ustar /* $Id: EXTERN.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #undef EXT #define EXT extern #undef INIT #define INIT(x) #undef DOINIT trn-3.6/debian/0000775000000000000000000000000012655611206010303 5ustar trn-3.6/debian/rules0000775000000000000000000001164011373026137011364 0ustar #!/usr/bin/make -f ############################################################################## # Generic debian/rules file. Based on: # #> Sample debian.rules file - for GNU Hello (1.3). #> Copyright 1994,1995 by Ian Jackson. #> I hereby give you perpetual unlimited permission to copy, #> modify and relicense this file, provided that you do not remove #> my name from the file itself. (I assert my moral right of #> paternity under the Copyright, Designs and Patents Act 1988.) # # Heavily modified by Joey Hess # ############################################################################## # # NOTE: You shouldn't have to edit this file. Edit debian/config instead. # If you must edit this file to get your package to build properly, then # I have failed. Let me know; mail jeh22@cornell.edu. # # (Currently not handled: multiple binary packages from 1 source package, # and binary-indep rule.) # # NOTE: This file is designed so it doesn't need to be run as root. For # actions that require that the user be root, the root password will be # prompted for, if you're not already root. # ############################################################################## # Include config file. include debian/config # Generate a makefile (via configure scriopt or xmkmf). makefile-stamp: ifeq ($(strip $(use_imakefile)),y) xmkmf -a endif $(use_configure) touch makefile-stamp # Preserve some files that may get deleted/overwritten/modified otherwise. preserve-stamp: ifneq ($(strip $(preserve_files)),) $(foreach file,$(preserve_files),-cp $(file) $(file).preserved) endif touch preserve-stamp build: preserve-stamp makefile-stamp $(checkdir) $(build_command) touch build clean: preserve-stamp makefile-stamp $(checkdir) # Do actual cleaning up here. -rm -f build $(clean_command) -rm -rf *~ debian/*~ debian/files* debian/substvars $(clean_files) $(clean_tmp) # Remove Makefile that xmkmf creates. ifeq ($(strip $(use_imakefile)),y) -rm -f Makefile endif # If we preserved some files, we need to restore them now. ifneq ($(strip $(preserve_files)),) $(foreach file,$(preserve_files),-mv -f $(file).preserved $(file)) endif -rm -f preserve-stamp makefile-stamp # Build architecture-independent files here. # (not yet set up to be used) binary-indep: build $(checkdir) # Build architecture-dependent files here. binary-arch: build $(checkdir) $(clean_tmp) install -d debian/tmp debian/tmp/DEBIAN debian/tmp/usr/share/doc/$(package) $(install_command) # Compress manpages -gzip -9v -r debian/tmp/usr/share/man/ debian/tmp/usr/X11R6/man/ find debian/tmp/usr/share/man debian/tmp/usr/X11R6/man -type f | \ xargs chmod 644 # Install documentation files, compressed. ifneq ($(strip $(docs)),) cp $(docs) debian/tmp/usr/share/doc/$(package) gzip -9v debian/tmp/usr/share/doc/$(package)/* find debian/tmp/usr/share/doc/$(package)/* -type f | xargs chmod 644 endif # Install copyright file, don't compress. ifneq ($(strip $(copyright)),) cp $(copyright) debian/tmp/usr/share/doc/$(package)/copyright endif # Install examples, compressed. ifneq ($(strip $(examples)),) install -d debian/tmp/usr/share/doc/examples/$(package) cp $(examples) debian/tmp/usr/share/doc/examples/$(package) gzip -9v debian/tmp/usr/share/doc/examples/$(package)/* endif # Install other debian files if they exist. -install -m 644 debian/changelog debian/tmp/usr/share/doc/$(package)/changelog.Debian -gzip -9v debian/tmp/usr/share/doc/$(package)/changelog.Debian -install -m 644 debian/conffiles debian/tmp/DEBIAN/conffiles -install -m 755 debian/preinst debian/tmp/DEBIAN/preinst -install -m 755 debian/postinst debian/tmp/DEBIAN/postinst -install -m 755 debian/prerm debian/tmp/DEBIAN/prerm -install -m 755 debian/postrm debian/tmp/DEBIAN/postrm # Generate control file. # echo 'We got this far...' dpkg-shlibdeps $(binfiles) dpkg-gencontrol # Set permissions. @if [ "`whoami`" != root ]; then \ printf "%b\n" "\n ** Enter root password to set file permissions."; \ su -c "debian/rules setperms" ; \ else \ debian/rules setperms ; \ fi # Actually build the .deb file. dpkg --build debian/tmp .. # This must be run suid root, it sets the file permissions in debian/tmp setperms: chown -R root.root debian/tmp chmod -R g-ws debian/tmp $(ch_commands) define checkdir @test -f $(test_file) -a -f debian/rules || (printf "%b\n" "\n\ ** \"$(test_file)\" or \"debian/rules\" does not exist.\n\ ** Either \"$(package)\" is not unpacked in this directory, or\n\ ** an incorrect test_file is specified in debian/config.\n" && false) endef # This rm's the debian/tmp directory. define clean_tmp -rm -rf debian/tmp >/dev/null 2>&1 @if [ -d debian/tmp ]; then \ if [ "`whoami`" != root ]; then \ printf "%b\n" "\n ** Enter root password to remove debian/tmp."; \ su -c "rm -rf debian/tmp"; \ else \ rm -rf debian/tmp; \ fi; \ fi endef binary: binary-indep binary-arch .PHONY: clean setperms binary trn-3.6/debian/prerm0000664000000000000000000000053007205304432011344 0ustar #! /bin/sh case "$1" in remove|deconfigure) for x in Pnews Rnmail newsetup newsgroups nntplist rn trn \ trn-artchk; do update-alternatives --quiet --remove $x /usr/lib/trn/$x done ;; upgrade|failed-upgrade) ;; esac trn-3.6/debian/config0000664000000000000000000001046312041330567011474 0ustar # Edit this file to configure debian/rules to build a package. # No modification of debian/rules should be neccessary. (Famous last words!) # # File by Joey Hess # Modified for trn 3.6-7 by Jon Rabone # What is the name of this package? package=trn # Files that go in directories under /doc. docs= examples= copyright=debian/copyright # List here all executables that are produced, that we should run # dpkg-shlibdeps on. binfiles=$(package) # What file must exist in the current directory if the package is # properly unpacked here? test_file=$(package).c # Does this package build from an Imakefile? # If so, uncomment the line below. #use_imakefile=y # Does this package build from a Configure script? # If so, uncomment the line below and enter the command to run to run the # Configure script (ie: "./Configure") # # Don't reconfigure trn 3.6 unless you know what you're doing. # I've had problems getting certain things to work, e.g. strftime detection # JKR 1996 # I'll comment it out then ;) Matthew@debian.org 1999 #use_configure=./Configure -S # What commands to run to build the package? define build_command $(MAKE) endef # What commands to run to clean up after a build? define clean_command $(MAKE) -i clean endef # List here any files that must be removed during "debian/rules clean" # that clean_command doesn't take care of. clean_files= # List here any files that should be preserved during a build, and restored # to their original state during a clean. For example, if the package comes # with both an Imakefile and a Makefile, and xmkmf is run, list the original # Makefile here so it will be backed up before it is overwritten my xmkmf. preserve_files= # What command to run to install the package into debian/tmp? # You might want to edit the package's Makefile and add $(PREFIX) # to all the paths it installs files to. or, you can just write # your own install commands here instead. # # Note that debian/* and the files in /usr/doc will be installed # properly for you, you don't need to do that here. # define install_command install -d debian/tmp/usr/bin ln -s ../lib/trn/trn debian/tmp/usr/bin/trn3 install -d debian/tmp/usr/lib/trn install -s trn debian/tmp/usr/lib/trn install -s trn-artchk debian/tmp/usr/lib/trn install -s nntplist debian/tmp/usr/lib/trn install newsetup debian/tmp/usr/lib/trn install newsgroups debian/tmp/usr/lib/trn install Pnews debian/tmp/usr/lib/trn install Rnmail debian/tmp/usr/lib/trn ln -s trn debian/tmp/usr/lib/trn/rn install -d debian/tmp/usr/share/trn install -m 0644 INIT debian/tmp/usr/share/trn install -m 0644 newsnews debian/tmp/usr/share/trn install -m 0755 Pnews.header debian/tmp/usr/share/trn install -m 0755 Speller debian/tmp/usr/share/trn install -m 0755 filexp debian/tmp/usr/share/trn install -m 0755 makedir debian/tmp/usr/share/trn install -m 0755 mbox.saver debian/tmp/usr/share/trn install -m 0755 norm.saver debian/tmp/usr/share/trn install -d debian/tmp/usr/share/man/man1 cp -f Pnews.1 debian/tmp/usr/share/man/man1/trn3-Pnews.1 cp -f Rnmail.1 debian/tmp/usr/share/man/man1/trn3-Rnmail.1 cp -f newsetup.1 debian/tmp/usr/share/man/man1/trn3-newsetup.1 cp -f newsgroups.1 debian/tmp/usr/share/man/man1/trn3-newsgroups.1 cp -f nntplist.1 debian/tmp/usr/share/man/man1/trn3-nntplist.1 cp -f trn-artchk.1 debian/tmp/usr/share/man/man1/trn3-trn-artchk.1 cp -f trn.1 debian/tmp/usr/share/man/man1/trn3.1 cp -f HINTS.TRN debian/tmp/usr/share/doc/trn/trn-hints cp -f NEW debian/tmp/usr/share/doc/trn/trn-changes gzip -9vf debian/tmp/usr/share/doc/trn/trn-changes cp -f README debian/tmp/usr/share/doc/trn/trn-readme install -m 755 debian/config.debconf debian/tmp/DEBIAN/config cp -f debian/templates debian/tmp/DEBIAN/ endef # After being installed in debian/tmp, everything is chowned to root.root, # and chmod g-ws is run on everything. Enter below any chmod commands you # need to run to set files to the proper permissions. This is where you # can make programs be suid, etc. # (Note that these commands will be run as root.) define ch_commands chown -R root.news debian/tmp/usr/share/trn endef trn-3.6/debian/config.debconf0000664000000000000000000000133112041327124013060 0ustar #! /bin/sh -e # Debconf script for trn3; gets installed as DEBIAN/config # Source debconf library. . /usr/share/debconf/confmodule db_version 2.0 # Useful system information. hostname=`hostname --fqdn 2>/dev/null || true` domainname=`dnsdomainname 2>/dev/null || true` # Default news server. if [ ! -s /etc/news/server ]; then db_get shared/news/server nntpserver="$RET" if [ -z "$nntpserver" ]; then # Try to guess a good value for the NNTP server. if [ "$domainname" ]; then nntpserver="news.$domainname" else nntpserver="$hostname" fi fi db_set shared/news/server "$nntpserver" db_input medium shared/news/server || true db_go || true fi trn-3.6/debian/postinst0000664000000000000000000000222212041327415012102 0ustar #! /bin/sh . /usr/share/debconf/confmodule case "$1" in configure) ;; abort-upgrade|abort-remove|abort-deconfigure) exit 0 ;; esac umask 022 set -e update-alternatives --quiet --install \ /usr/bin/trn trn /usr/lib/trn/trn 36 \ --slave /usr/share/man/man1/trn.1.gz trn.1.gz \ /usr/share/man/man1/trn3.1.gz for x in nntplist trn-artchk; do update-alternatives --quiet --install /usr/bin/$x $x /usr/lib/trn/$x 36 \ --slave /usr/share/man/man1/$x.1.gz $x.1.gz \ /usr/share/man/man1/trn3-$x.1.gz done update-alternatives --quiet --install \ /usr/bin/rn rn /usr/lib/trn/rn 36 for x in Pnews Rnmail newsetup newsgroups; do update-alternatives --quiet --install \ /usr/bin/$x $x /usr/lib/trn/$x 36 \ --slave /usr/share/man/man1/$x.1.gz $x.1.gz \ /usr/share/man/man1/trn3-$x.1.gz done cd /etc #test for /etc/news/whoami, and create if it's absent test ! -f news/whoami && touch news/whoami test -f news/server && exit 0 if ! test -d news then mkdir news chown news:news news chmod 755 news fi db_get shared/news/server echo "$RET" > news/server trn-3.6/debian/copyright0000664000000000000000000001051011727661574012247 0ustar This is Debian/GNU Linux's prepackaged version of the `trn' threaded newsreader by Wayne Davison, Larry Wall and Stan Barber. This package was put together by Jon Rabone , from Wayne Davison's sources, obtained from unix.hensa.ac.uk:/mirrors/uunet/networking/news/readers/trn/trn-3.6.tar.gz. The changes in this package are essentially the addition of preconfigured `config.h' files and so forth and the addition of support for the Debian package maintenance scheme, by adding various debian.* files; also the small patch to makedepend.SH below. Trn 3.5 is Copyright 1993 Wayne Davison; Copyright 1991 Stan Barber; Copyright 1985 Larry Wall. Modifications for Debian GNU/Linux Copyright 1994 Ian Jackson; Copyright 1996 Jon Rabone Permission is hereby granted to copy, reproduce, redistribute or otherwise use this software as long as: there is no monetary profit gained specifically from the use or reproduction of this software, it is not sold, rented, traded or otherwise marketed, and this copyright notice is included prominently in any copy made. The authors make no claims as to the fitness or correctness of this software for any use whatsoever, and it is provided as is. Any use of this software is at the user's own risk. [ I contacted Stan Barber, Wayne Davison and Larry Wall to ask for clarification of this. Stan Barber replied: This intent of the copyright is not to INCREASE the cost of a CD-ROM distribution just because *rn was added. Put another way, if you had a CD-ROM at $100 and added *rn and changed the cost to $110 just because you added *rn, that would violate the intent of the copyright. and Wayne Davison agreed. -- Ian Jackson, former trn maintainer ] Hashing routines in hash.c written by Written by Geoffrey Collyer. Copyright 1992 Geoffrey Collyer. All rights reserved. This software is not subject to any license of the American Telephone and Telegraph Company, the Regents of the University of California, or the Free Software Foundation. Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it freely, subject to the following restrictions: 1. The author is not responsible for the consequences of use of this software, no matter how awful, even if they arise from flaws in it. 2. The origin of this software must not be misrepresented, either by explicit claim or by omission. Since few users ever read sources, credits must appear in the documentation. 3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. Since few users ever read sources, credits must appear in the documentation. 4. This notice may not be removed or altered. Date parsing code in parsedate.y originally written by Steven M. Bellovin while at the University of North Carolina at Chapel Hill. Later tweaked by a couple of people on Usenet. Completely overhauled by Rich $alz and Jim Berets in August, 1990. Further revised (removed obsolete constructs and cleaned up timezone names) in August, 1991, by Rich. Paul Eggert helped in September, 1992. This grammar has six shift/reduce conflicts. This code is in the public domain and has no copyright. Regular expression parsing in search.c Copyright 1981, 1980 James Gosling. Modified by Tom London (1981); ripped to shreds and glued back together to make a search package by Larry Wall (1984). Unpacking of ship files in unship.c based on ship.c by Mark Adler (1991). This code is not copyrighted. Modified by Wayne Davison. I (the current Debian maintainer, Matthew Vernon ) believe that automatically building trn for other architectures is fine under the set of licences above. If this ceases to be the case, please contact nonfree@releases.debian.net Here is the small patch mentioned above: --- makedepend.SH~ Sat Apr 30 02:11:49 1994 +++ makedepend.SH Wed Aug 3 01:53:17 1994 @@ -40,7 +40,7 @@ -e 's|/\*.*$||' \\ -e 's/\\\\[ ]*$//' \\ -e p \\ - -e '}' | $cppstdin -I\$objdir -I$usrinc | $sed \\ + -e '}' | $cppstdin -I\$objdir -I$usrinc $cppminus | $sed \\ -e '/^# *line/s/line//' \ -e '/^# *[0-9]/!d' \\ -e 's/^.*"\(.*\)".*\$/'\$filebase'.o: \1/' \\ trn-3.6/debian/rcln.patch0000664000000000000000000000331407041327652012265 0ustar Date: Mon, 9 Feb 1998 16:45:06 -0500 Message-Id: <199802092145.QAA01529@kanga.eecs.umich.edu> From: Subject: trn: PATCH: fix for "Warning! Somebody reset --assuming nothing read." To: submit@bugs.debian.org X-Mailer: bug 3.1.0 Package: trn Version: 3.6-9.1 Received from a colleague of mine, Mike Hucka : -- Users complain about encountering errors like this from libc6 trn 3.6 on a Debian Linux system (running hamm): Warning! Somebody reset sci.answers--assuming nothing read. Checking out your .newsrc -- hang on a second... Warning! Somebody reset comp.ai.genetic--assuming nothing read. Warning! Somebody reset comp.dcom.cell-relay--assuming nothing read. This happens when they move between newsgroups. It happens erratically, but usually in every newsreading session. An apparent side-effect of this problem for some people is that their .newsrc file then ends up with entries like comp.os.linux.misc: 1-213671,,,,,,,,,,,,,,,214204214342214196214158214153214138215158214639214043213961213885213840213719213711213755,214111,214689,214694,214699,2 Here's the patch: diff -u rcln.c.orig rcln.c --- rcln.c.orig Mon Feb 9 04:34:41 1998 +++ rcln.c Mon Feb 9 04:35:11 1998 @@ -78,7 +78,7 @@ register char *s, *t, *maxt = Nullch; ART_NUM min = 0, max = -1, lastnum = 0; char *mbuf; - bool morenum; + bool_int morenum; if (!artnum) return 0; This patch was sent to me by Wayne Ling , who in turn got it off the net. "Jan D." , who reposted the patch on the net, wrote: "I think the problem was that isdigit returned a number that didn't fit into a char." trn-3.6/debian/templates0000664000000000000000000000063212041327155012221 0ustar Template: shared/news/server Type: string Description: What news server should be used for reading and posting news? trn is configured to read news via an NNTP connection, and needs to know the fully-qualified host name of the server (such as news.example.com). If you have a local news spool, you should consider installing some NNTP server like inn2; in that case, enter "localhost" as your news server. trn-3.6/debian/control0000664000000000000000000000176612547224114011716 0ustar Source: trn Section: non-free/news Priority: optional Maintainer: Matthew Vernon Build-Depends: bison, libncurses5-dev XS-Autobuild: yes Standards-Version: 3.2.1.0 Package: trn Architecture: any Depends: ${shlibs:Depends}, inn2-inews|inews, debconf (>= 0.5) Recommends: mail-transport-agent Provides: news-reader Suggests: ispell Description: Threaded USENET news reader, based on rn trn is the most widely-used newsreader on USENET - even to the point of being a de-facto standard. It has a powerful full screen text mode user interface, where most actions are available at the press of a single key. It is highly configurable. . This version of trn reads news from a news server via NNTP (the Network News Transfer Protocol). You must have a news server available - large sites usually provide a site-wide server. . If you install a news server on your system (either containing just local newsgroups or with a partial or full USENET feed) you can configure trn to access it. trn-3.6/debian/changelog0000644000000000000000000002054712655611206012163 0ustar trn (3.6-24build1) xenial; urgency=medium * No-change rebuild for ncurses6 transition. -- Matthias Klose Sun, 07 Feb 2016 09:46:46 +0000 trn (3.6-24) unstable; urgency=low * Adjust dependency for inews to inn2-inews|inews (inewsinn doesn't exist any more) -- Matthew Vernon Wed, 08 Jul 2015 14:41:23 +0100 trn (3.6-23) unstable; urgency=high * use the shared/news/server debconf template rather than prompting in postinst (closes: #688563) -- Matthew Vernon Mon, 22 Oct 2012 21:45:25 +0100 trn (3.6-22) unstable; urgency=low * Mark package as autobuildable, per http://lists.debian.org/debian-devel-announce/2006/11/msg00012.html -- Matthew Vernon Tue, 13 Mar 2012 15:11:28 +0000 trn (3.6-21) unstable; urgency=low * Patch from Colin Watson to backport several Pnews fixes from trn4 to avoid busy-waiting if stdin goes away (part of #197677, although we still don't use set -e). -- Matthew Vernon Tue, 13 Mar 2012 14:48:05 +0000 trn (3.6-20) unstable; urgency=low * remove bashisms from debian/rules (closes: #581452) -- Matthew Vernon Thu, 13 May 2010 17:45:02 +0100 trn (3.6-19) unstable; urgency=low * copyright file fixes: standardise to license; remove parsedate.c copyright section, as this is regenerated at each build (closes: #142731) * Apply (corrected) patch from Ben Harris to make editor the default editor, in line with policy (closes: #503971) * Apply patch from Ben Harris to replace KILL files more safely (closes: #503968) * Apply patch from Jonathan Amery to make trn deal better with overlong References: lines (closes: #486630) -- Matthew Vernon Tue, 23 Dec 2008 11:20:54 +0000 trn (3.6-18) unstable; urgency=medium * Fix FTBFS bug in common.h, and add a build-depends (closes: #306318) * Continue to make this package available (closes: #336973) -- Matthew Vernon Wed, 2 Nov 2005 16:10:58 +0000 trn (3.6-17) unstable; urgency=low * patch to fix the subscribe-prompts over-run (closes: #24783) -- Matthew Vernon Sat, 26 Jan 2002 20:31:20 +0000 trn (3.6-16) unstable; urgency=low * use time.h rather than sys/time.h (closes: #90554) * include manpages for nntplist and trn-artchk (closes: #83629) * move from /usr/doc to /usr/share/doc (closes: #91687) -- Matthew Vernon Mon, 09 Apr 2001 17:04:38 +0100 trn (3.6-15) unstable; urgency=low * Many changes to make trn interoperate with trn4 (closes: #76772) -- Matthew Vernon Fri, 17 Nov 2000 19:44:26 +0000 trn (3.6-14) unstable; urgency=low * Patch config to use /etc/news/whoami -- Matthew Vernon Sun, 4 Jun 2000 21:43:20 +0100 trn (3.6-13) frozen unstable; urgency=low * Patch from Colin Watson for manpage (closes:#50193) * Patch from Peter Maydell (closes:#46301) * Previous version closed this bug oops. (closes:#21959) * Upload again into non-free :( (closes:#54519,#54095) * Changed the depends to inews|inewsinn (closes:#55484,#55470,#55160) * Can we have less duplicate bugs next time, please? -- Matthew Vernon Wed, 19 Jan 2000 12:44:42 +0000 trn (3.6-12) unstable; urgency=low * Patch from pmaydell@chiark.greenend.org.uk (closes:#42039) -- Matthew Vernon Thur, 09 Sep 1999 13:44:01 +0000 trn (3.6-11) unstable; urgency=low * Fixed the version number. Sorry folks -- Matthew Vernon Wed, 01 Sep 1999 13:13:03 +0100 trn (3.6-10.1) unstable; urgency=low * Taking over as maintainer * Shifted docs to /usr/doc/trn (closes: #42039) * Tidied up postinst to make it clear what to do about local spool (closes: #22987) * Patch from Peter Maydell to clean up handling of duff References (closes: #14591) * Patch from Brendab O'Dea to clean up temporary files (closes: #27306) * Commented out a "sanity" check that broke dbuild (closes: #24857) * Tell trn to use hostname, not /etc/news/whoami (closes: #34633) -- Matthew Vernon Mon, 23 Aug 1999 15:25:28 +0200 trn (3.6-9.4) unstable; urgency=high * Non-maintainer upload by Security Team * Fixes insecure use of files in /tmp, patch by Richard Kettlewell -- Martin Schulze Thu, 19 Aug 1999 12:05:56 +0200 trn (3.6-9.3) frozen unstable; urgency=high * Non-maintainer release. * Fixes /tmp/*$$ security problems in newsetup, newsgroups and Pnews (fixes: #19812) * Fixed FSF address. * Changed "Optional:" control field to "Suggests:" * Fixed debian/rules to work with fakeroot -- Anthony Towns Sat, 11 Apr 1998 02:36:52 +1000 trn (3.6-9.2) unstable; urgency=high * Non-maintainer release. * Fixes: #18032: trn: PATCH: fix for "Warning! Somebody reset --assuming (applied "rcln.patch") #16344: trn: Read articles being confused as "unread". #15890: trn needs /etc/news/whoami but doesn't provide it #12627: trn: unchecked prompting in postinst -- Miquel van Smoorenburg Thu, 19 Feb 1998 21:22:04 +0100 trn (3.6-9.1) unstable; urgency=low * Non-maintainer release. * Libc6 compile. -- Martin Mitchell Fri, 7 Nov 1997 19:15:21 +1100 trn (3.6-9) stable unstable; urgency=low * Changed config.sh to set pager to $PAGER-/bin/more (Bug#5957.) -- Jon Rabone Fri, 13 Dec 1996 20:00:58 +0000 trn (3.6-8) frozen unstable; urgency=low * Increased line buffer size to make trn more resilient to broken articles -- Jon Rabone Thu, 31 Oct 1996 23:06:56 +0000 trn (3.6-7) unstable; urgency=low * Made dead.article and dead.letter non world readable (Bug#1845.) * Updated to Standards-Version 2.1.1.0 * Changed maintainer email address. -- Jon Rabone Thu, 31 Oct 1996 23:06:56 +0000 trn (3.6-6); priority=LOW * Added Depends: inews to debian.control. (Bug#3089) * Added Provides: news-reader to debian.control. (Bug#2783) -- Jon Rabone <93jkr@eng.cam.ac.uk> Thu, 13 Jun 1996 13:35:12 +0000 trn (3.6-5); priority=LOW * Compiled ELF, with ncurses library. -- Michael Nonweiler Thursday, 23 May 1996 trn (3.6-4); priority=HIGH * Fixed "lots of new newsgroups" bug after 01.01.1996. (Bug#2084.) -- Ian Jackson Wed, 3 Jan 1996 15:00:12 +0000 trn (3.6-3); priority=LOW * Postinst creates /etc/news/server correctly. (Bug#1876.) * Fixed coredump caused by use of getwd. -- Ian Jackson Sun, 26 Nov 1995 15:44:14 +0000 trn (3.6-2); priority=LOW * Reads /etc/news/descriptions, if available, instead of asking server (Bug#829). * Dependencies on an MTA and an `inews' are now only Recommends and use virtual package names (Bug#1439). * Enable XAUTHINFO GENERIC support. * Compile with -O3 instead of -O2. * Extended description provided. * /usr/doc/copyright/trn now quotes the emailed clarification I got from Stan Barber. -- Ian Jackson Tue, 26 Sep 1995 15:02:23 +0100 trn (3.6-2); priority=LOW * Reads /etc/news/descriptions, if available, instead of asking server (Bug#829). * Dependencies on an MTA and an `inews' are now only Recommends and use virtual package names (Bug#1439). * Enable XAUTHINFO GENERIC support. * Compile with -O3 instead of -O2. * Extended description provided. * /usr/doc/copyright/trn now quotes the emailed clarification I got from Stan Barber. -- Ian Jackson Tue, 26 Sep 1995 15:02:23 +0100 Trn 3.6 Debian 1 - iwj * Based on version 3.6. * Now uses NEWGROUPS (which requires the server to keep active.times up to date) rather than adding all groups to .newsrc. * Dependency generation removed in favour of distributed `dependencies' file; in any case our patch to makedepend.SH is now in standard kit. Trn 3.5 Debian 2 - iwj * Put more version number information in ../*.{deb,gz} filenames. * Added Package_Revision field to `control' file. * Rationalised debian.rules somewhat, and added `build' stamp file. * Most files are not installed g+w news.news but g-w root.news. * Added this Changelog. Trn 3.5 Debian 1 - iwj Initial release, completely untested. trn-3.6/HACKERSGUIDE0000664000000000000000000001055705342472763010662 0ustar Hacking Notes from the hand of Larry Wall with respect to rn, trn's ancestor, modified by Wayne Davison to reflect the current state of trn. If you aren't interested in mucking with the innards of trn, don't read this. In the interests of both space and time optimization, things are done inside trn that don't always conform to the highest ideals of programming. To the extent I felt it was practical, I've tried to conform to good programming practice, but you must realize that my goal was to make a better mousetrap, so certain conscious tradeoffs were made in the design of trn right from the start. In particular, if you want to hack on trn (and I wouldn't blame you, it's fun), beware of the following: * buf and cmd_buf are reused all over the place. 11-squishing is a good term for it. No, I'm on a Vax now, but I've been there. * The article header is parsed on the fly, while it is being displayed. In fact, practically everything is done on the fly within the article display loop, and there are plenty of state variables. The information required to backup pages is not stored in memory, except for 1 buffer's worth. * Lots of contortions are gone through to avoid using static memory, or allocating unnecessary memory, or losing track of allocated memory, while at the same time allowing .newsrc lines and header lines to be ANY length up to the amount of memory you have. Trn spends a great deal of effort being lazy. Do not use a static buffer when you can use growstr(). * Lots of contortions are gone through to try to do things when people aren't waiting, or have only been waiting a very short time. Guessing the next article to be opened and opening it, searching ahead for the next article with the same subject, delaying the look up of the number of articles in a newsgroup, writing the rest of the page while the reader is examining the header, cacheing up subjects while the user is reading, checkpointing the .newsrc only while the reader is in the middle of an interesting article, are some of the strategies employed. * There are plenty of goto's. Most of them involve going back to reprompt, to reask for input, or to just plain do the unstructured things people want to do when they are glaring at a terminal. If they bother you too much, just think of trn as a big state machine. If they don't bother you at all, I don't want you hacking on trn. * Put all includes at the front of the file, before the first function, or makedepend will not work right. I could relax this, but makedepend would take about 5 times longer to run. In general then, feel free to hack on trn. Just don't broadcast untested patches to the net. Remember that there are people with limited address spaces and limited cpu cycles. If you add a wonderful new feature and want to publish a patch, put #ifdef's around it so that people who don't want it or can't afford it can work around it. THIS MEANS YOU. We don't need 57 varieties of mutually incompatible and incomprehensible trn floating about the net. Consider telling me about your patch so that I can consider including it in the standard version. A COMPLETE PATCH TAKES INTO ACCOUNT SYSTEM DEPENDENCIES AS DETERMINED BY THE CONFIGURE SCRIPT. * Don't use ints where trn uses typedefs, in particular, for article numbers. * Don't use %d anywhere that someone might need a %ld. (Just because YOU typedefed it as an int doesn't mean someone else won't need a long.) * Don't use %D, that's archaic. * Put FLUSHes after printf()s, fputs()es and putchar('\n')s for our poor brethern and sistern without line buffering. * Declare the type of every function. Use void, even if your C compiler doesn't. * Follow the style that trn already uses! This is my pet peeve. Well, one of them, anyway. I follow other people's strange styles when modifying their programs, so I'd be much obliged if you did likewise. * Use lint. * Use RCS. Start a new branch, like 4.4.[2-9]. (I will use 4.4.1 myself.) * Be structured wherever it doesn't interfere with practicality. * Long live paranoid programming. The rest of the program is out to get you. The world is out to destroy the program, not to mention the .newsrc. And then there's always bitrot... * Stop reading this lugubrious trash and start thinking for yourself. * Thank you and good night. trn-3.6/init.c0000664000000000000000000001573205650602710010175 0ustar /* $Id: init.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "final.h" #include "term.h" #include "last.h" #include "trn.h" #include "rcstuff.h" #include "ngdata.h" #include "only.h" #include "intrp.h" #include "addng.h" #include "sw.h" #include "art.h" #include "artsrch.h" #include "artio.h" #include "backpage.h" #include "cache.h" #include "bits.h" #include "head.h" #include "help.h" #include "kfile.h" #include "ngsrch.h" #include "ngstuff.h" #include "rcln.h" #include "respond.h" #include "nntp.h" #include "rthread.h" #include "ng.h" #include "decode.h" #include "INTERN.h" #include "init.h" bool initialize(argc,argv) int argc; char *argv[]; { char *tcbuf; register bool foundany = FALSE; char *s; #ifdef NOLINEBUF static char std_out_buf[BUFSIZ]; /* must be static or malloced */ setbuf(stdout, std_out_buf); #endif tcbuf = safemalloc(1024); /* make temp buffer for termcap and */ /* other initialization stuff */ our_pid = getpid(); /* init terminal */ term_init(); /* must precede sw_init() so that */ /* ospeed is set for baud-rate */ /* switches. Actually terminal */ /* mode setting is in term_set() */ /* init syntax etc. for searching (must also precede sw_init()) */ search_init(); /* we have to know rnlib to look up global switches in %X/INIT */ homedir = getenv("HOME"); if (homedir == Nullch) homedir = getenv("LOGDIR"); lib = savestr(filexp(NEWSLIB)); rnlib = savestr(filexp(PRIVLIB)); tmpdir = getenv("TMPDIR"); if (!tmpdir) { tmpdir = getenv("TMP"); if (!tmpdir) tmpdir = "/tmp"; } /* decode switches */ sw_init(argc,argv,&tcbuf); /* must not do % interps! */ /* (but may mung environment) */ /* init signals, status flags */ final_init(); /* start up file expansion and the % interpreter */ intrp_init(tcbuf); /* now make sure we have a current working directory */ if (!checkflag) cwd_check(); /* now that we know where to save things, cd to news directory */ if (chdir(spool)) { printf(nocd,spool) FLUSH; finalize(1); } /* if we aren't just checking, turn off echo */ if (!checkflag) term_set(tcbuf); /* get info on last rn run, if any */ if (!checkflag) last_init(tcbuf); free(tcbuf); /* recover 1024 bytes */ /* make sure we are the sole possessors of .newsrc */ if (!checkflag) lock_check(); /* check for news news */ if (!checkflag) newsnews_check(); #ifdef USE_NNTP /* open connection to server if appropriate */ if (!nntp_connect(1)) finalize(1); #endif /* open active file, etc. */ ngdata_init(); /* now read in the .newsrc file */ foundany = rcstuff_init(); /* it looks like we will actually read something, so init everything */ addng_init(); art_init(); artio_init(); artsrch_init(); backpage_init(); bits_init(); cache_init(); head_init(); help_init(); kfile_init(); ng_init(); ngsrch_init(); ngstuff_init(); only_init(); rcln_init(); respond_init(); rn_init(); decode_init(); thread_init(); util_init(); #ifdef FINDNEWNG /* * Skip this check if the -q flag was given. */ if (!quickstart) { if (find_new_groups()) { /* did we add any new groups? */ foundany = TRUE; /* let main() know */ starthere = 0; /* start ng scan from the top */ } } #endif time(&lasttime); /* remember when we inited-- */ /* ends up back in .rnlast */ writelast(); /* in fact, put it there now */ #ifdef FINDNEWNG # ifdef ONLY if (maxngtodo) /* patterns on command line? */ foundany |= scanactive(); # endif #endif /* xterm mouse initialization -- move somewhere different? */ /* values of XTERMMOUSE: y/Y/1 -- if appropriate, use it. */ /* n/N/0 -- don't ever use it. */ /* a/A -- always use it. */ s = getval("XTERMMOUSE","-"); if (*s == 'y' || *s == 'Y' || *s == '1') { if ((s = getenv("TERM")) != Nullch && strnEQ(s,"xterm",5)) use_xterm_mouse = TRUE; } else if (*s == 'a' || *s == 'A') use_xterm_mouse = TRUE; else if (*s != 'n' && *s != 'N' && *s != '0') { s = argv[0]; /* if an 'x' is at the end, enable Xterm mouse tracking */ if (s[strlen(s)-1] == 'x') use_xterm_mouse = TRUE; } return foundany; } /* make sure there is no rn out there already */ void lock_check() { lockname = savestr(filexp(LOCKNAME)); if (!checkflag) { int processnum = 0; tmpfp = fopen(lockname,"r"); if (tmpfp != Nullfp) { if (fgets(buf,LBUFLEN,tmpfp)) processnum = atoi(buf); fclose(tmpfp); } if (processnum) { #ifdef VERBOSE IF(verbose) printf("You seem to have left a trn running, process %d.\n", processnum) FLUSH; ELSE #endif #ifdef TERSE printf("Trn left running, #%d.\n", processnum) FLUSH; #endif if (kill(processnum, 0)) { /* does process not exist? */ sleep(2); #ifdef VERBOSE IF(verbose) fputs("\n\ That process does not seem to exist anymore. The count of read articles\n\ may be incorrect in the last newsgroup accessed by that other (defunct)\n\ process.\n\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nProcess crashed.\n",stdout) FLUSH; #endif if (*lastngname) { #ifdef VERBOSE IF(verbose) printf("(The last newsgroup accessed was %s.)\n\n", lastngname) FLUSH; ELSE #endif #ifdef TERSE printf("(In %s.)\n\n",lastngname) FLUSH; #endif } get_anything(); putchar('\n') FLUSH; } else { #ifdef VERBOSE IF(verbose) fputs("\n\ You may not have two copies of [t]rn running simultaneously. Goodbye.\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nCan't start another.\n",stdout) FLUSH; #endif if (bizarre) resetty(); exit(0); } } tmpfp = fopen(lockname,"w"); if (tmpfp == Nullfp) { printf(cantcreate,lockname) FLUSH; sig_catcher(0); } fprintf(tmpfp,"%ld\n",our_pid); fclose(tmpfp); } } void newsnews_check() { char *newsnewsname = filexp(NEWSNEWSNAME); if ((tmpfp = fopen(newsnewsname,"r")) != Nullfp) { fstat(fileno(tmpfp),&filestat); if (filestat.st_mtime > lasttime) { while (fgets(buf,sizeof(buf),tmpfp) != Nullch) fputs(buf,stdout) FLUSH; get_anything(); putchar('\n') FLUSH; } fclose(tmpfp); } } trn-3.6/rcstuff.c.rej0000664000000000000000000000261107424607734011471 0ustar *************** *** 347,357 **** } else { #ifdef VERBOSE IF(verbose) - sprintf(promptbuf,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname); ELSE #endif #ifdef TERSE - sprintf(promptbuf,"\nSubscribe %s?",ngname); #endif reask_add: in_char(promptbuf,'A',"ynYN"); --- 348,358 ---- } else { #ifdef VERBOSE IF(verbose) + snprintf(promptbuf,PBLEN,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname); ELSE #endif #ifdef TERSE + snprintf(promptbuf,PBLEN,"\nSubscribe %s?",ngname); #endif reask_add: in_char(promptbuf,'A',"ynYN"); *************** *** 418,430 **** else if (rcchar[ng] == NEGCHAR) { /* unsubscribed? */ #ifdef VERBOSE IF(verbose) - sprintf(promptbuf, "\nNewsgroup %s is unsubscribed -- resubscribe?",ngname) FLUSH; ELSE #endif #ifdef TERSE - sprintf(promptbuf,"\nResubscribe %s?",ngname) FLUSH; #endif reask_unsub: --- 419,431 ---- else if (rcchar[ng] == NEGCHAR) { /* unsubscribed? */ #ifdef VERBOSE IF(verbose) + snprintf(promptbuf, PBLEN, "\nNewsgroup %s is unsubscribed -- resubscribe?",ngname) FLUSH; ELSE #endif #ifdef TERSE + snprintf(promptbuf,PBLEN,"\nResubscribe %s?",ngname) FLUSH; #endif reask_unsub: trn-3.6/rt-mt.c0000664000000000000000000004352205663312264010301 0ustar /* $Id: rt-mt.c,v 3.0 1992/12/14 00:14:13 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "intrp.h" #include "trn.h" #include "cache.h" #include "bits.h" #include "ng.h" #include "ngdata.h" #include "rcln.h" #include "util.h" #include "hash.h" #include "nntp.h" #include "rthread.h" #include "rt-process.h" #include "INTERN.h" #include "rt-mt.h" extern HASHTABLE *msgid_hash; #ifndef USE_XTHREAD static FILE *fp; #endif static bool word_same, long_same; static BMAP my_bmap, mt_bmap; static char *strings = Nullch; static WORD *author_cnts = 0; static WORD *ids = 0; static ARTICLE **article_array = 0; static SUBJECT **subject_array = 0; static char **author_array = 0; static TOTAL total; static PACKED_ROOT p_root; static PACKED_ARTICLE p_article; /* Initialize our thread code by determining the byte-order of the thread ** files and our own current byte-order. If they differ, set flags to let ** the read code know what we'll need to translate. */ bool mt_init() { int i; #ifdef USE_XTHREAD long size; #endif bool success = TRUE; /* I'm an optimist */ word_same = long_same = TRUE; #ifdef USE_XTHREAD nntp_command("XTHREAD DBINIT"); size = nntp_readcheck(); if (size < 0) return FALSE; size = nntp_read((char*)&mt_bmap, (long)sizeof (BMAP)); if (size >= sizeof (BMAP) - 1) { #else /* !USE_XTHREAD */ if ((fp = fopen(filexp(DBINIT), FOPEN_RB)) != Nullfp && fread((char*)&mt_bmap, 1, sizeof (BMAP), fp) >= sizeof (BMAP) - 1) { #endif if (mt_bmap.version != DB_VERSION) { printf("\nMthreads database is the wrong version -- ignoring it.\n") FLUSH; return FALSE; } mybytemap(&my_bmap); for (i = 0; i < sizeof (LONG); i++) { if (i < sizeof (WORD)) { if (my_bmap.w[i] != mt_bmap.w[i]) { word_same = FALSE; } } if (my_bmap.l[i] != mt_bmap.l[i]) { long_same = FALSE; } } } else success = FALSE; #ifdef USE_XTHREAD while (nntp_read(ser_line, (long)sizeof ser_line)) ; /* trash any extraneous bytes */ #else if (fp != Nullfp) fclose(fp); #endif return success; } /* Open and process the data in the group's thread file. Returns TRUE unless ** we discovered a bogus thread file, destroyed the cache, and re-built it. */ bool mt_data() { bool success = TRUE; #ifdef USE_XTHREAD /* use remote thread file? */ long size; nntp_command("XTHREAD THREAD"); size = nntp_readcheck(); if (size < 0) return TRUE; #ifdef VERBOSE IF(verbose) printf("\nGetting thread file."), fflush(stdout); #endif if (nntp_read((char*)&total, (long)sizeof (TOTAL)) < sizeof (TOTAL)) goto exit; #else /* !USE_XTHREAD */ if ((fp = fopen(mt_name(ngname), FOPEN_RB)) == Nullfp) return TRUE; #ifdef VERBOSE IF(verbose) printf("\nReading thread file."), fflush(stdout); #endif if (fread((char*)&total, 1, sizeof (TOTAL), fp) < sizeof (TOTAL)) goto exit; #endif /* !USE_XTHREAD */ lp_bmap(&total.first, 4); wp_bmap(&total.root, 5); if (!total.root) { tweak_data(); goto exit; } if (total.last > lastart) #ifdef USE_NNTP total.last = lastart; #else grow_cache(total.last); #endif if (read_authors() && read_subjects() && read_roots() && read_articles() && read_ids()) { tweak_data(); first_cached = absfirst; last_cached = (total.last < absfirst ? absfirst-1: total.last); cached_all_in_range = TRUE; goto exit; } /* Something failed. Safefree takes care of checking if some items ** were already freed. Any partially-allocated structures were freed ** before we got here. All other structures are cleaned up now. */ close_cache(); safefree(&strings); safefree((char**)&article_array); safefree((char**)&subject_array); safefree((char**)&author_array); safefree((char**)&ids); try_mt = 0; build_cache(); try_mt = 1; success = FALSE; exit: #ifdef USE_XTHREAD while (nntp_read(ser_line, (long)sizeof ser_line)) ; /* trash any extraneous bytes */ #else fclose(fp); #endif return success; } #ifndef USE_XTHREAD /* Change a newsgroup name into the name of the thread data file. We ** subsitute any '.'s in the group name into '/'s (unless LONG_THREAD_NAMES ** is defined), prepend the path, and append the '/.thread' or '.th' on to ** the end. */ static char * mt_name(group) char *group; { #ifdef LONG_THREAD_NAMES sprintf(buf, "%s/%s", threaddir, group); #else register char *cp; cp = strcpy(buf, threaddir) + strlen(threaddir); *cp++ = '/'; strcpy(cp, group); while ((cp = index(cp, '.'))) *cp = '/'; if (threaddir == spool) strcat(buf, "/.thread"); else strcat(buf, ".th"); #endif return buf; } #endif static char *subject_strings, *string_end; /* The author information is an array of use-counts, followed by all the ** null-terminated strings crammed together. The subject strings are read ** in at the same time, since they are appended to the end of the author ** strings. */ static int read_authors() { register int count; register char *string_ptr, **author_ptr; if (!read_item((char**)&author_cnts, (MEM_SIZE)total.author*sizeof (WORD))) return 0; safefree((char**)&author_cnts); /* we don't need these */ if (!read_item(&strings, (MEM_SIZE)total.string1)) return 0; string_ptr = strings; string_end = string_ptr + total.string1; if (string_end[-1] != '\0') { /*error("first string table is invalid.\n");*/ return 0; } /* We'll use this array to point each article at its proper author ** (the packed values were saved as indexes). */ author_array = (char**)safemalloc(total.author * sizeof (char*)); author_ptr = author_array; for (count = total.author; count; count--) { if (string_ptr >= string_end) break; *author_ptr++ = string_ptr; string_ptr += strlen(string_ptr) + 1; } subject_strings = string_ptr; if (count) { /*error("author unpacking failed.\n");*/ return 0; } return 1; } /* The subject values consist of the crammed-together null-terminated strings ** (already read in above) and the use-count array. They were saved in the ** order that the roots require while being unpacked. */ static int read_subjects() { register int count; register char *string_ptr; register SUBJECT **subj_ptr; WORD *subject_cnts; if (!read_item((char**)&subject_cnts, (MEM_SIZE)total.subject * sizeof (WORD))) { /* (Error already logged.) */ return 0; } free((char*)subject_cnts); /* we don't need these */ /* Use this array when unpacking the article's subject offset. */ subject_array = (SUBJECT**)safemalloc(total.subject * sizeof (SUBJECT*)); subj_ptr = subject_array; string_ptr = subject_strings; /* string_end is already set */ for (count = total.subject; count; count--) { int len; ARTICLE arty; if (string_ptr >= string_end) break; len = strlen(string_ptr); arty.subj = 0; set_subj_line(&arty, string_ptr, len); if (len == 72) arty.subj->flags |= SF_SUBJTRUNCED; string_ptr += len + 1; *subj_ptr++ = arty.subj; } if (count || string_ptr != string_end) { /*error("subject data is invalid.\n");*/ return 0; } return 1; } /* Read in the packed root structures to set each subject's thread article ** offset. This gets turned into a real pointer later. */ static int read_roots() { register int count, i; register SUBJECT **subj_ptr; int ret; subj_ptr = subject_array; for (count = total.root; count--; ) { #ifdef USE_XTHREAD ret = nntp_read((char*)&p_root, (long)sizeof (PACKED_ROOT)); #else ret = fread((char*)&p_root, 1, sizeof (PACKED_ROOT), fp); #endif if (ret != sizeof (PACKED_ROOT)) { /*error("failed root read -- %d bytes instead of %d.\n", ret, sizeof (PACKED_ROOT));*/ return 0; } wp_bmap(&p_root.articles, 3); /* converts subject_cnt too */ if (p_root.articles < 0 || p_root.articles >= total.article) { /*error("root has invalid values.\n");*/ return 0; } i = p_root.subject_cnt; if (i <= 0 || (subj_ptr - subject_array) + i > total.subject) { /*error("root has invalid values.\n");*/ return 0; } subj_ptr[i-1]->thread_link = subj_ptr[0]; while (i--) { union { ARTICLE *ap; int num; } uni; if (i) subj_ptr[0]->thread_link = subj_ptr[1]; uni.num = p_root.articles; (*subj_ptr++)->thread = uni.ap; } } return 1; } static bool invalid_data; /* A simple routine that checks the validity of the article's subject value. ** A -1 means that it is NULL, otherwise it should be an offset into the ** subject array we just unpacked. */ static SUBJECT * the_subject(num) WORD num; { if (num == -1) return Nullsubj; if (num < 0 || num >= total.subject) { /*printf("Invalid subject in thread file: %d [%ld]\n", num, art_num);*/ invalid_data = TRUE; return Nullsubj; } return subject_array[num]; } /* Ditto for author checking. */ static char * the_author(num) WORD num; { if (num == -1) return Nullch; if (num < 0 || num >= total.author) { /*error("invalid author in thread file: %d [%ld]\n", num, art_num);*/ invalid_data = TRUE; return Nullch; } return savestr(author_array[num]); } /* Our parent/sibling information is a relative offset in the article array. ** zero for none. Child values are always found in the very next array ** element if child_cnt is non-zero. */ static ARTICLE * the_article(relative_offset, num) WORD relative_offset; int num; { union { ARTICLE *ap; int num; } uni; if (!relative_offset) return Nullart; num += relative_offset; if (num < 0 || num >= total.article) { /*error("invalid article offset in thread file.\n");*/ invalid_data = TRUE; return Nullart; } uni.num = num+1; return uni.ap; /* slip them an offset in disguise */ } /* Read the articles into their trees. Point everything everywhere. */ static int read_articles() { register int count; register ARTICLE *article, **art_ptr; int ret; /* Build an array to interpret interlinkages of articles. */ article_array = (ARTICLE**)safemalloc(total.article * sizeof (ARTICLE*)); art_ptr = article_array; invalid_data = FALSE; for (count = 0; count < total.article; count++) { #ifdef USE_XTHREAD ret = nntp_read((char*)&p_article, (long)sizeof (PACKED_ARTICLE)); #else ret = fread((char*)&p_article, 1, sizeof (PACKED_ARTICLE), fp); #endif if (ret != sizeof (PACKED_ARTICLE)) { /*error("failed article read -- %d bytes instead of %d.\n", ret, sizeof (PACKED_ARTICLE));*/ return 0; } lp_bmap(&p_article.num, 2); wp_bmap(&p_article.subject, 8); #ifdef USE_NNTP article = *art_ptr++ = allocate_article(p_article.num > lastart? 0 : p_article.num); #else article = *art_ptr++ = allocate_article(p_article.num); #endif article->date = p_article.date; #ifndef DBM_XREFS if (olden_days < 2 && !(p_article.flags & HAS_XREFS)) article->xrefs = nullstr; #endif article->from = the_author(p_article.author); article->parent = the_article(p_article.parent, count); article->child1 = the_article(p_article.child_cnt ? 1 : 0, count); article->sibling = the_article(p_article.sibling, count); article->subj = the_subject(p_article.subject); if (invalid_data) { /* (Error already logged.) */ return 0; } /* This is ok because parent articles precede their children */ if (article->parent) { union { ARTICLE *ap; int num; } uni; uni.ap = article->parent; article->parent = article_array[uni.num-1]; } if (article->subj) { if (!(article->flags & AF_MISSING)) { article->flags |= AF_FROMTRUNCED | AF_THREADED | ((p_article.flags & ROOT_ARTICLE)? 0 : AF_HAS_RE); } /* Give this subject to any faked parent articles */ while (article->parent && !article->parent->subj) { article->parent->subj = article->subj; article = article->parent; } } else article->flags |= AF_FAKE|AF_MISSING; } /* We're done with most of the pointer arrays at this point. */ safefree((char**)&subject_array); safefree((char**)&author_array); safefree(&strings); return 1; } /* Read the message-id strings and attach them to each article. The data ** format consists of the mushed-together null-terminated strings (a domain ** name followed by all its unique-id prefixes) and then the article offsets ** to which they belong. The first domain name was omitted, as it is a null ** domain for those truly weird message-id's without '@'s. */ static int read_ids() { register ARTICLE *article; register char *string_ptr; register int i, count, len, len2; if (!read_item(&strings, (MEM_SIZE)total.string2) || !read_item((char**)&ids, (MEM_SIZE)(total.article+total.domain+1) * sizeof (WORD))) { return 0; } wp_bmap(ids, total.article + total.domain + 1); string_ptr = strings; string_end = string_ptr + total.string2; if (string_end[-1] != '\0') { /*error("second string table is invalid.\n");*/ return 0; } for (i = 0, count = total.domain + 1; count--; i++) { if (i) { if (string_ptr >= string_end) { /*error("error unpacking domain strings.\n");*/ return 0; } sprintf(buf, "@%s", string_ptr); len = strlen(string_ptr) + 1; string_ptr += len; } else { *buf = '\0'; len = 0; } if (ids[i] != -1) { if (ids[i] < 0 || ids[i] >= total.article) { /*error("error in id array.\n");*/ return 0; } article = article_array[ids[i]]; for (;;) { if (string_ptr >= string_end) { /*error("error unpacking domain strings.\n");*/ return 0; } len2 = strlen(string_ptr); article->msgid = safemalloc(len2 + len + 2 + 1); sprintf(article->msgid, "<%s%s>", string_ptr, buf); string_ptr += len2 + 1; if (msgid_hash) { HASHDATUM data; if ((article->flags & AF_TMPMEM) == AF_TMPMEM) { data.dat_ptr = (char*)article; data.dat_len = 0; } else { data.dat_ptr = Nullch; data.dat_len = article_num(article); } hashstore(msgid_hash, article->msgid, len2+len+2, data); } if (++i >= total.article + total.domain + !count) { /*error("overran id array unpacking domains.\n");*/ return 0; } if (ids[i] != -1) { if (ids[i] < 0 || ids[i] >= total.article) return 0; article = article_array[ids[i]]; } else break; } } } safefree((char**)&ids); safefree(&strings); return 1; } /* And finally, turn all the links into real pointers and mark missing ** articles as read. */ static void tweak_data() { register int count; register ARTICLE *ap, **art_ptr; register SUBJECT *sp; register ART_NUM i; union { ARTICLE *ap; int num; } uni; for (sp = first_subject; sp; sp = sp->next) { uni.ap = sp->thread; sp->thread = article_array[uni.num]; } art_ptr = article_array; for (count = total.article; count--; ) { ap = *art_ptr++; if (ap->child1) { uni.ap = ap->child1; ap->child1 = article_array[uni.num-1]; } if (ap->sibling) { uni.ap = ap->sibling; ap->sibling = article_array[uni.num-1]; } } art_ptr = article_array; for (count = total.article; count--; ) { ap = *art_ptr++; if (!(ap->flags & AF_MISSING)) cache_article(ap); } /* Mark any missing articles as read */ for (i = absfirst, ap = article_ptr(i); i <= total.last; i++, ap++) { if ((ap->flags & (AF_CACHED|AF_MISSING)) == AF_CACHED) check_poster(ap); else onemissing(ap); } safefree((char**)&article_array); } /* A shorthand for reading a chunk of the file into a malloc'ed array. */ static int read_item(dest, len) char **dest; MEM_SIZE len; { long ret; *dest = safemalloc(len); #ifdef USE_XTHREAD ret = nntp_read(*dest, (long)len); #else ret = fread(*dest, 1, (int)len, fp); #endif if (ret != len) { free(*dest); *dest = Nullch; return 0; } putchar('.'), fflush(stdout); return 1; } /* Free some memory if it hasn't already been freed. */ static void safefree(pp) char **pp; { if (*pp) { free(*pp); *pp = Nullch; } } /* Determine this machine's byte map for WORDs and LONGs. A byte map is an ** array of BYTEs (sizeof (WORD) or sizeof (LONG) of them) with the 0th BYTE ** being the byte number of the high-order byte in my , and so forth. */ static void mybytemap(map) BMAP *map; { union { BYTE b[sizeof (LONG)]; WORD w; LONG l; } u; register BYTE *mp; register int i, j; mp = &map->w[sizeof (WORD)]; u.w = 1; for (i = sizeof (WORD); i > 0; i--) { for (j = 0; j < sizeof (WORD); j++) { if (u.b[j] != 0) break; } if (j == sizeof (WORD)) goto bad_news; *--mp = j; while (u.b[j] != 0 && u.w) u.w <<= 1; } mp = &map->l[sizeof (LONG)]; u.l = 1; for (i = sizeof (LONG); i > 0; i--) { for (j = 0; j < sizeof (LONG); j++) { if (u.b[j] != 0) break; } if (j == sizeof (LONG)) { bad_news: /* trouble -- set both to *something* consistent */ for (j = 0; j < sizeof (WORD); j++) map->w[j] = j; for (j = 0; j < sizeof (LONG); j++) map->l[j] = j; return; } *--mp = j; while (u.b[j] != 0 && u.l) u.l <<= 1; } } /* Transform each WORD's byte-ordering in a buffer of the designated length. */ static void wp_bmap(buf, len) WORD *buf; int len; { union { BYTE b[sizeof (WORD)]; WORD w; } in, out; register int i; if (word_same) return; while (len--) { in.w = *buf; for (i = 0; i < sizeof (WORD); i++) out.b[my_bmap.w[i]] = in.b[mt_bmap.w[i]]; *buf++ = out.w; } } /* Transform each LONG's byte-ordering in a buffer of the designated length. */ static void lp_bmap(buf, len) LONG *buf; int len; { union { BYTE b[sizeof (LONG)]; LONG l; } in, out; register int i; if (long_same) return; while (len--) { in.l = *buf; for (i = 0; i < sizeof (LONG); i++) out.b[my_bmap.l[i]] = in.b[mt_bmap.l[i]]; *buf++ = out.l; } } trn-3.6/nntpinit.c0000664000000000000000000002347505663312264011106 0ustar /* $Id: nntpinit.c,v 3.0 1991/11/22 04:12:21 davison Trn $ */ /* This software is Copyright 1992 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction or this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #undef DECNET /* If you want decnet support */ #undef EXCELAN /* Excelan EXOS 205 support */ #undef NONETDB /* Define if you're missing netdb.h */ #include "EXTERN.h" #include "common.h" #include "nntpclient.h" #ifdef USE_NNTP #include #include #ifdef NONETDB # define IPPORT_NNTP ((unsigned short) 119) #else # include #endif /* !EXCELAN */ #ifdef EXCELAN int connect _((int, struct sockaddr *)); unsigned short htons _((unsigned short)); unsigned long rhost _((char **)); int rresvport p((int)); int socket _((int, struct sockproto *, struct sockaddr_in *, int)); #endif /* EXCELAN */ #ifdef DECNET #include #include #endif /* DECNET */ unsigned long inet_addr _((char *x)); int get_tcp_socket _((char *machine)); int server_init(server) char *server; { int sockt_rd, sockt_wr; #ifdef DECNET char *cp; cp = index(server, ':'); if (cp && cp[1] == ':') { *cp = '\0'; sockt_rd = get_dnet_socket(server); } else sockt_rd = get_tcp_socket(server); #else /* !DECNET */ sockt_rd = get_tcp_socket(server); #endif if (sockt_rd < 0) return -1; sockt_wr = dup(sockt_rd); /* Now we'll make file pointers (i.e., buffered I/O) out of ** the socket file descriptor. Note that we can't just ** open a fp for reading and writing -- we have to open ** up two separate fp's, one for reading, one for writing. */ if ((ser_rd_fp = fdopen(sockt_rd, "r")) == NULL) { perror("server_init: fdopen #1"); return -1; } if ((ser_wr_fp = fdopen(sockt_wr, "w")) == NULL) { perror("server_init: fdopen #2"); ser_rd_fp = NULL; return -1; } /* Now get the server's signon message */ nntp_check(FALSE); if (*ser_line == NNTP_CLASS_OK) { char line2[NNTP_STRLEN]; /* Send an XMODE READER command to see if this is a compliant ** nntpd we're talking to. If not, try MODE READER just in ** case we're talking to innd. If OK, use the new reply. */ nntp_command("XMODE READER"); nntp_gets(line2, sizeof line2); if (atoi(line2) != NNTP_BAD_COMMAND_VAL) CompliantServer = 1; else { nntp_command("MODE READER"); nntp_gets(line2, sizeof line2); } #if defined(DEBUG) && defined(FLUSH) if (debug & DEB_NNTP) printf("<%s\n", line2) FLUSH; #endif if (atoi(line2) != NNTP_BAD_COMMAND_VAL) strcpy(ser_line, line2); } return atoi(ser_line); } int get_tcp_socket(server) char *server; { int portno; int s; struct sockaddr_in sin; #ifdef __hpux int socksize = 0; int socksizelen = sizeof socksize; #endif #ifdef NONETDB bzero((char *) &sin, sizeof(sin)); sin.sin_family = AF_INET; #else struct servent *getservbyname(), *sp; struct hostent *gethostbyname(), *hp; #ifdef h_addr int x = 0; register char **cp; static char *alist[1]; #endif /* h_addr */ static struct hostent def; static struct in_addr defaddr; static char namebuf[ 256 ]; if ((sp = getservbyname("nntp", "tcp")) == NULL) { fprintf(stderr, "nntp/tcp: Unknown service.\n"); return -1; } portno = sp->s_port; /* If not a raw ip address, try nameserver */ if (!isdigit(*server) || (defaddr.s_addr = inet_addr(server)) == -1) hp = gethostbyname(server); else { /* Raw ip address, fake */ (void) strcpy(namebuf, server); def.h_name = namebuf; #ifdef h_addr def.h_addr_list = alist; #endif def.h_addr = (char *)&defaddr; def.h_length = sizeof(struct in_addr); def.h_addrtype = AF_INET; def.h_aliases = 0; hp = &def; } if (hp == NULL) { fprintf(stderr, "%s: Unknown host.\n", server); return -1; } bzero((char *) &sin, sizeof(sin)); sin.sin_family = hp->h_addrtype; sin.sin_port = portno; #endif /* !NONETDB */ /* The following is kinda gross. The name server under 4.3 ** returns a list of addresses, each of which should be tried ** in turn if the previous one fails. However, 4.2 hostent ** structure doesn't have this list of addresses. ** Under 4.3, h_addr is a #define to h_addr_list[0]. ** We use this to figure out whether to include the NS specific ** code... */ #ifdef h_addr /* get a socket and initiate connection -- use multiple addresses */ for (cp = hp->h_addr_list; cp && *cp; cp++) { s = socket(hp->h_addrtype, SOCK_STREAM, 0); if (s < 0) { perror("socket"); return -1; } bcopy(*cp, (char *)&sin.sin_addr, hp->h_length); if (x < 0) fprintf(stderr, "trying %s\n", inet_ntoa(sin.sin_addr)); x = connect(s, (struct sockaddr *)&sin, sizeof (sin)); if (x == 0) break; fprintf(stderr, "connection to %s: ", inet_ntoa(sin.sin_addr)); perror(""); (void) close(s); } if (x < 0) { fprintf(stderr, "giving up...\n"); return -1; } #else /* no name server */ #ifdef EXCELAN s = socket(SOCK_STREAM, (struct sockproto *)NULL, &sin, SO_KEEPALIVE); if (s < 0) { /* Get the socket */ perror("socket"); return -1; } bzero((char *) &sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(IPPORT_NNTP); /* set up addr for the connect */ if ((sin.sin_addr.s_addr = rhost(&server)) == -1) { fprintf(stderr, "%s: Unknown host.\n", server); return -1; } /* And then connect */ if (connect(s, (struct sockaddr *)&sin) < 0) { perror("connect"); (void) close(s); return -1; } #else /* not EXCELAN */ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } /* And then connect */ bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length); if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("connect"); (void) close(s); return -1; } #endif /* !EXCELAN */ #endif /* !h_addr */ #ifdef __hpux /* recommended by raj@cup.hp.com */ #define HPSOCKSIZE 0x8000 getsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, (caddr_t)&socksizelen); if (socksize < HPSOCKSIZE) { socksize = HPSOCKSIZE; setsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, sizeof(socksize)); } socksize = 0; socksizelen = sizeof(socksize); getsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, (caddr_t)&socksizelen); if (socksize < HPSOCKSIZE) { socksize = HPSOCKSIZE; setsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, sizeof(socksize)); } #endif return s; } #ifdef DECNET int get_dnet_socket(server) char *server; { int s, area, node; struct sockaddr_dn sdn; struct nodeent *getnodebyname(), *np; bzero((char *) &sdn, sizeof(sdn)); switch (s = sscanf(server, "%d%*[.]%d", &area, &node)) { case 1: node = area; area = 0; case 2: node += area*1024; sdn.sdn_add.a_len = 2; sdn.sdn_family = AF_DECnet; sdn.sdn_add.a_addr[0] = node % 256; sdn.sdn_add.a_addr[1] = node / 256; break; default: if ((np = getnodebyname(server)) == NULL) { fprintf(stderr, "%s: Unknown host.\n", server); return -1; } else { bcopy(np->n_addr, (char *) sdn.sdn_add.a_addr, np->n_length); sdn.sdn_add.a_len = np->n_length; sdn.sdn_family = np->n_addrtype; } break; } sdn.sdn_objnum = 0; sdn.sdn_flags = 0; sdn.sdn_objnamel = strlen("NNTP"); bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel); if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) { nerror("socket"); return -1; } /* And then connect */ if (connect(s, (struct sockaddr *) &sdn, sizeof(sdn)) < 0) { nerror("connect"); close(s); return -1; } return s; } #endif /* DECNET */ #ifdef EXCELAN /* * inet_addr for EXCELAN (which does not have it!) * */ unsigned long inet_addr(cp) register char *cp; { unsigned long val, base, n; register char c; unsigned long octet[4], *octetptr = octet; #ifndef htonl extern unsigned long htonl(); #endif /* htonl */ again: /* * Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, other=decimal. */ val = 0; base = 10; if (*cp == '0') base = 8, cp++; if (*cp == 'x' || *cp == 'X') base = 16, cp++; while (c = *cp) { if (isdigit(c)) { val = (val * base) + (c - '0'); cp++; continue; } if (base == 16 && isxdigit(c)) { val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A')); cp++; continue; } break; } if (*cp == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16-bits) * a.b (with b treated as 24 bits) */ if (octetptr >= octet + 4) return (-1); *octetptr++ = val, cp++; goto again; } /* * Check for trailing characters. */ if (*cp && !isspace(*cp)) return (-1); *octetptr++ = val; /* * Concoct the address according to * the number of octet specified. */ n = octetptr - octet; switch (n) { case 1: /* a -- 32 bits */ val = octet[0]; break; case 2: /* a.b -- 8.24 bits */ val = (octet[0] << 24) | (octet[1] & 0xffffff); break; case 3: /* a.b.c -- 8.8.16 bits */ val = (octet[0] << 24) | ((octet[1] & 0xff) << 16) | (octet[2] & 0xffff); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ val = (octet[0] << 24) | ((octet[1] & 0xff) << 16) | ((octet[2] & 0xff) << 8) | (octet[3] & 0xff); break; default: return (-1); } val = htonl(val); return (val); } #endif /* EXCELAN */ #endif /* USE_NNTP */ trn-3.6/Makefile.SH0000664000000000000000000001467505663312252011047 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting Makefile (with variable substitutions)" case "$srcdir" in '') srcdir='.';; esac $cat >Makefile <>Makefile <<'!NO!SUBS!' public = trn newsetup newsgroups Pnews Rnmail trn-artchk $(nntpbin) private = norm.saver mbox.saver makedir Pnews.header Speller filexp util = makedepend newsnews manpages = trn.1 Pnews.1 Rnmail.1 newsetup.1 newsgroups.1 #NNTPnntpbin=nntplist #NNTPnntpsrc=nntpinit.c nntpclient.c nntpauth.c nntp.c #NNTPnntpobj1=nntpinit.o nntpclient.o nntpauth.o #NNTPnntpobj2=nntp.o h1 = addng.h art.h artio.h artsrch.h autosub.h backpage.h bits.h cache.h h2 = charsubst.h common.h decode.h final.h hash.h head.h help.h init.h intrp.h h3 = kfile.h last.h ng.h ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h h4 = respond.h rthread.h rt-mt.h rt-ov.h rt-page.h rt-process.h rt-select.h h5 = rt-util.h rt-wumpus.h search.h sw.h term.h trn.h util.h util2.h h = $(h1) $(h2) $(h3) $(h4) $(h5) c1 = addng.c art.c artio.c artsrch.c autosub.c backpage.c bits.c cache.c c2 = charsubst.c decode.c edit_dist.c final.c hash.c head.c help.c init.c c3 = intrp.c kfile.c last.c $(ndirc) ng.c ngdata.c nghash.c ngsrch.c ngstuff.c c4 = only.c rcln.c rcstuff.c respond.c rthread.c rt-mt.c rt-ov.c c5 = rt-process.c rt-page.c rt-select.c rt-util.c rt-wumpus.c search.c c6 = $(strftimec) sw.c term.c trn.c util.c util2.c unship.c uudecode.c c7 = $(nntpsrc) charsubst.c c = $(c1) $(c2) $(c3) $(c4) $(c5) $(c6) $(c7) obj1 = addng.o art.o artio.o artsrch.o autosub.o backpage.o bits.o cache.o obj2 = charsubst.o decode.o edit_dist.o final.o hash.o head.o help.o init.o obj3 = intrp.o kfile.o last.o $(ndiro) ng.o ngdata.o nghash.o ngsrch.o obj4 = ngstuff.o only.o rcln.o rcstuff.o respond.o rthread.o rt-mt.o rt-ov.o obj5 = rt-process.o rt-page.o rt-select.o rt-util.o rt-wumpus.o search.o obj6 = $(strftimeo) sw.o term.o trn.o util.o util2.o unship.o uudecode.o obj7 = parsedate.o $(nntpobj1) $(nntpobj2) obj = $(obj1) $(obj2) $(obj3) $(obj4) $(obj5) $(obj6) $(obj7) addedbymake = $(public) $(private) $(util) Makefile.old config.h\ parsedate.c cppstdin all # grrr SHELL = /bin/sh .c.o: $(CC) -c $(CFLAGS) $< all: $(public) $(private) $(util) Makefile $(TOUCH) all trn: $(obj) $(CC) $(LDFLAGS) $(obj) $(libs) -o trn #NNTP #NNTPnntpinit.o: nntpinit.c #NNTP $(CC) -c $(CFLAGS) $(NNTPFLAGS) $< #NNTP #NNTPnntplist: nntplist.o util2.o $(nntpobj1) #NNTP $(CC) $(LDFLAGS) nntplist.o util2.o $(nntpobj1) -o nntplist $(libs) trn-artchk: trn-artchk.o util2.o $(nntpobj1) $(CC) $(LDFLAGS) trn-artchk.o util2.o $(nntpobj1) -o trn-artchk $(libs) parsedate.c: parsedate.y @echo 'Expect 6 shift/reduce conflicts' $(YACC) $(srcdir)/parsedate.y mv -f y.tab.c parsedate.c #STRFTIME #STRFTIME$(strftimeo): $(strftimec) #STRFTIME $(CC) -c $(CFLAGS) $(strftimec) unipatch: unipatch.o $(CC) $(LDFLAGS) unipatch.o -o unipatch Pnews.header: Pnews # if a .h file depends on another .h file... $(h): -$(TOUCH) $@ install: $(public) $(private) $(manpages) # won't work with csh export PATH || exit 1 - (cd `./filexp $(rnbin)`; mv trn trn.old) - if test `pwd` != `./filexp $(rnbin)`; then cp $(public) `./filexp $(rnbin)`;\ (cd `./filexp $(rnbin)`; chmod o+x $(public); strip trn trn-artchk $(nntpbin)); fi - if test $(installfilexp) = Rnfilexp; then cp ./filexp `./filexp $(rnbin)/Rnfilexp`; fi - ./makedir `./filexp $(rnlib)` - chmod o+r `./filexp $(rnlib)` - if test `pwd` != `./filexp $(rnlib)`; then cp $(srcdir)/INIT $(private) `./filexp $(rnlib)`; fi - if test ! -f `./filexp $(rnlib)/newsnews`; then cp newsnews `./filexp $(rnlib)`; fi - (cd `./filexp $(rnlib)`; chmod o+x $(private); chmod o+r INIT newsnews) - if test "X$(mansrc)" != "X" -a "X`pwd`" != X`./filexp $(mansrc)`; then \ for page in $(manpages); do \ dest=$(mansrc)/`basename $$page .1`.$(manext); \ rm -f $$dest; cp $(srcdir)/$$page $$dest; chmod 444 $$dest; \ done; \ fi clean: @echo 'Use "make realclean" to also remove the Makefile.' @echo 'Use "make spotless" to even remove config.sh.' rm -rf UU rm -f *.o core $(addedbymake) realclean: @echo 'You can use "Configure -S ; make depend ; make" to reverse this.' rm -rf UU rm -f *.o core $(addedbymake) Makefile spotless: rm -rf UU .config rm -f *.o core $(addedbymake) Makefile config.sh # The following lint has practically everything turned on. Unfortunately, # you have to wade through a lot of mumbo jumbo that can't be suppressed. # If the source file has a /*NOSTRICT*/ somewhere, ignore the lint message # for that spot. lint: $(c) parsedate.c $(LINT) $(lintflags) $(defs) $? > trn.fuzz 2>&1 sabertrn: $(c) parsedate.c #load $(c) parsedate.c $(libs) depend: config.h Makefile makedepend ./makedepend # AUTOMATICALLY GENERATED MAKE DEPENDENCIES--PUT NOTHING BELOW THIS LINE !NO!SUBS! if test -f dependencies; then $cat dependencies >>Makefile else $cat $srcdir/dependencies >>Makefile fi case "$d_nntp" in define) sed < Makefile -e '/^#NNTP/s/^#NNTP//' > Makefile.new ;; *) sed < Makefile -e '/^#NNTP/d' > Makefile.new ;; esac case "$strftimec" in ''|' ') sed < Makefile.new -e '/^#STRFTIME/d' > Makefile ;; *) sed < Makefile.new -e '/^#STRFTIME/s/^#STRFTIME//' > Makefile ;; esac rm Makefile.new $eunicefix Makefile trn-3.6/head.h0000664000000000000000000001464405547415011010142 0ustar /* $Id: head.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #define HEAD_FIRST 1 /* types of header lines (if only C really believed in enums) * (These must stay in alphabetic order at least in the first letter. * Within each letter it helps to arrange in increasing likelihood.) */ #define PAST_HEADER 0 /* body */ #define SOME_LINE (PAST_HEADER+1) /* unrecognized */ #define ARTID_LINE (SOME_LINE+1) /* article-i.d. */ #define APPR_LINE (ARTID_LINE+1) /* approved */ #define ACAT_LINE (APPR_LINE+1) /* ACategory (ClariNet) */ #define ANPA_LINE (ACAT_LINE+1) /* ANPA (ClariNet) */ #define CODES_LINE (ANPA_LINE+1) /* Codes (ClariNet) */ #define CONTENT_LINE (CODES_LINE+1) /* Content-Type (MIME) */ #define CONTXFER_LINE (CONTENT_LINE+1) /* Content-Transfer-Encoding */ #define CANCEL_LINE (CONTXFER_LINE+1) /* cancel */ #define DIST_LINE (CANCEL_LINE+1) /* distribution */ #define DATE_LINE (DIST_LINE+1) /* date */ #define RECEIVED_LINE (DATE_LINE+1) /* date-received */ #define EXPIR_LINE (RECEIVED_LINE+1) /* expires */ #define FOLLOW_LINE (EXPIR_LINE+1) /* followup-to */ #define FROM_LINE (FOLLOW_LINE+1) /* from */ #define FORM_LINE (FROM_LINE+1) /* Format (ClariNet) */ #define KEYW_LINE (FORM_LINE+1) /* keywords */ #define LINES_LINE (KEYW_LINE+1) /* lines */ #define MESSID_LINE (LINES_LINE+1) /* message-id */ #define NFFR_LINE (MESSID_LINE+1) /* nf-from */ #define NFID_LINE (NFFR_LINE+1) /* nf-id */ #define NGS_LINE (NFID_LINE+1) /* newsgroups */ #define NNTPHOST_LINE (NGS_LINE+1) /* nntp-posting-host */ #define NOTE_LINE (NNTPHOST_LINE+1) /* Note (ClariNet) */ #define ORG_LINE (NOTE_LINE+1) /* organization */ #define PATH_LINE (ORG_LINE+1) /* path */ #define POSTED_LINE (PATH_LINE+1) /* posted */ #define PVER_LINE (POSTED_LINE+1) /* posting-version */ #define PRI_LINE (PVER_LINE+1) /* Priority (ClariNet) */ #define REPLY_LINE (PRI_LINE+1) /* reply-to */ #define REFS_LINE (REPLY_LINE+1) /* references */ #define RVER_LINE (REFS_LINE+1) /* relay-version */ #define SENDER_LINE (RVER_LINE+1) /* sender */ #define SUMRY_LINE (SENDER_LINE+1) /* summary */ #define SUBJ_LINE (SUMRY_LINE+1) /* subject */ #define SUPR_LINE (SUBJ_LINE+1) /* supersedes */ #define SLUG_LINE (SUPR_LINE+1) /* Slugword (ClariNet) */ #define XREF_LINE (SLUG_LINE+1) /* xref */ #define XSUP_LINE (XREF_LINE+1) /* X-Supersedes (ClariNet) */ #define HEAD_LAST (XSUP_LINE+1) /* total # of headers */ struct headtype { char *ht_name; /* header line identifier */ ART_POS ht_minpos; /* pointer to beginning of line in article */ ART_POS ht_maxpos; /* pointer to end of line in article */ char ht_length; /* could make these into nybbles but */ char ht_flags; /* it wouldn't save space normally */ }; /* due to alignment considerations */ #define HT_HIDE 1 /* -h on this line */ #define HT_MAGIC 2 /* do any special processing on this line */ #define HT_CACHED 4 /* this information is cached in the article data */ /* This array must stay in the same order as the list above */ #ifndef DOINIT EXT struct headtype htype[HEAD_LAST]; #else struct headtype htype[HEAD_LAST] = { /* name minpos maxpos length flag */ {"BODY", 0, 0, 4, 0 }, {"unrecognized", 0, 0, 12, 0 }, {"article-i.d.", 0, 0, 12, HT_HIDE }, {"approved", 0, 0, 8, HT_HIDE }, {"acategory", 0, 0, 9, HT_HIDE }, {"anpa", 0, 0, 4, HT_HIDE }, {"codes", 0, 0, 5, HT_HIDE }, #if defined(MIMESHOW) || defined(MIMESTORE) {"content-type", 0, 0, 12, HT_MAGIC }, {"content-transfer-encoding", 0, 0, 25, HT_MAGIC }, #else {"content-type", 0, 0, 12, 0 }, {"content-transfer-encoding", 0, 0, 25, 0 }, #endif {"control", 0, 0, 7, 0 }, {"distribution", 0, 0, 12, 0 }, {"date", 0, 0, 4, HT_MAGIC }, {"date-received", 0, 0, 13, 0 }, {"expires", 0, 0, 7, HT_HIDE|HT_MAGIC}, {"followup-to", 0, 0, 11, 0 }, {"from", 0, 0, 4, HT_CACHED }, {"format", 0, 0, 6, HT_HIDE }, {"keywords", 0, 0, 8, 0 }, {"lines", 0, 0, 5, 0 }, {"message-id", 0, 0, 10, HT_HIDE|HT_CACHED}, {"nf-from", 0, 0, 7, HT_HIDE }, {"nf-id", 0, 0, 5, HT_HIDE }, #ifdef DBM_XREFS {"newsgroups", 0, 0, 10, HT_MAGIC|HT_HIDE|HT_CACHED}, #else {"newsgroups", 0, 0, 10, HT_MAGIC|HT_HIDE}, #endif {"nntp-posting-host",0, 0, 17, HT_HIDE }, {"note", 0, 0, 4, 0, }, {"organization", 0, 0, 12, 0 }, {"path", 0, 0, 4, HT_HIDE }, {"posted", 0, 0, 6, HT_HIDE }, {"posting-version", 0, 0, 15, HT_HIDE }, {"priority", 0, 0, 8, HT_HIDE }, {"reply-to", 0, 0, 8, HT_HIDE }, {"references", 0, 0, 10, HT_HIDE }, {"relay-version", 0, 0, 13, HT_HIDE }, {"sender", 0, 0, 6, HT_HIDE }, {"summary", 0, 0, 7, 0 }, {"subject", 0, 0, 7, HT_MAGIC|HT_CACHED}, {"supersedes", 0, 0, 10, 0 }, {"slugword", 0, 0, 8, HT_HIDE }, #ifdef DBM_XREFS {"xref", 0, 0, 4, HT_HIDE}, #else {"xref", 0, 0, 4, HT_HIDE|HT_CACHED}, #endif {"x-supersedes", 0, 0, 12, HT_HIDE } }; #endif EXT ART_NUM parsed_art INIT(0); /* the article number we've parsed */ EXT char in_header INIT(0); /* are we decoding the header? */ EXT char *headbuf; EXT long headbuf_size; void head_init _((void)); int set_line_type _((char*,char*)); void start_header _((ART_NUM)); void end_header_line _((void)); void end_header _((void)); bool parseline _((char*,int,int)); bool parseheader _((ART_NUM)); char *fetchlines _((ART_NUM,int)); /* returns a malloc'ed string */ char *prefetchlines _((ART_NUM,int,bool_int)); #ifdef DEBUG void dumpheader _((char*)); #endif #ifdef USE_NNTP #define PREFETCH_SIZE 5 #endif #define fetchsubj(artnum,copy) prefetchlines(artnum,SUBJ_LINE,copy) #define fetchfrom(artnum,copy) prefetchlines(artnum,FROM_LINE,copy) #ifdef DBM_XREFS #define fetchxref(artnum,copy) prefetchlines(artnum,NGS_LINE,copy) #else #define fetchxref(artnum,copy) prefetchlines(artnum,XREF_LINE,copy) #endif trn-3.6/trn-artchk.10000664000000000000000000000325207264355630011230 0ustar '\" t .\" @(#)trn-artchk.1 .\" .\" Original version of this manpage: .\" Peter Maydell (pmaydell@chiark.greenend.org.uk), 09/1999 .\" .TH TRN-ARTCHK 1 "September 1999" .SH NAME trn\-artchk \- check a news article's validity .SH SYNOPSIS .B trn-artchk .I article maxLineLen newsgroupsFile activeFile .br .SH DESCRIPTION .LP .B trn-artchk will check an article intended for posting to USENET for validity, printing warnings if problems are found. As the name implies, it's mostly intended to be used internally by the .B trn(1) newsreader. It will check the article for a variety of problems, including overlong lines, incorrect header syntax, missing trailing newline and attempt to post to invalid newsgroups. It will print a summary of groups the article is intended for, with descriptions. .SH OPTIONS .TP .I article This should be the file containing the article to check. .TP .I maxLineLen should be the maximum line length to accept. A warning will be issued if the article contains any lines longer than this. .TP .I newsgroupsFile should be a newsgroups file in the standard format used by NNTP servers. If the file is not found, trn-artchk will attempt to make an NNTP connection to obtain the information. Note that even if it is desired to obtain the information from the server this article must still be present, as a placeholder. .TP .I activeFile should be an NNTP server active file. The same notes apply to this as to the .IN newsgroupsFile argument. .SH AUTHOR Wayne Davison, Stan Barber and Larry Wall are all candidates for authorship of trn-artchk; its provenance is uncertain. Peter Maydell wrote this manual page. .SH SEE ALSO .BR trn (1) trn-3.6/rt-wumpus.h0000664000000000000000000000115105420174120011202 0ustar /* $Id: rt-wumpus.h,v 3.0 1992/12/14 00:14:15 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ void init_tree _((void)); void entire_tree _((ARTICLE*)); int tree_puts _((char*,ART_LINE,int)); int finish_tree _((ART_LINE)); #ifdef DOINIT static void find_depth _((ARTICLE*,int)); static void cache_tree _((ARTICLE*,int,char*)); static void display_tree _((ARTICLE*,char*)); static char letter _((ARTICLE*)); static int check_page_line _((void)); #endif trn-3.6/art.h0000664000000000000000000000240605645405227010027 0ustar /* $Id: art.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ /* do_article() return values */ #define DA_NORM 0 #define DA_RAISE 1 #define DA_CLEAN 2 #define DA_TOEND 3 EXT ART_LINE highlight INIT(-1);/* next line to be highlighted */ EXT ART_POS artsize; /* size in bytes of article */ #ifdef INNERSEARCH EXT ART_POS innersearch INIT(0); /* artpos of end of line we want to visit */ EXT ART_LINE innerlight INIT(0); /* highlight position for innersearch or 0 */ EXT char hide_everything INIT(0);/* if set, do not write page now, */ #endif /* ...but execute char when done with page */ void art_init _((void)); int do_article _((void)); int page_switch _((void)); bool innermore _((void)); trn-3.6/artio.h0000664000000000000000000000254605645405230010356 0ustar /* $Id: artio.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT ART_POS artpos INIT(0); /* byte position in article file */ EXT ART_LINE artline INIT(0); /* current line number in article file */ EXT FILE *artfp INIT(Nullfp); /* current article file pointer */ EXT ART_NUM openart INIT(0); /* the article number we have open */ #ifdef LINKART EXT char *linkartname INIT(nullstr);/* real name of article for Eunice */ #endif void artio_init _((void)); FILE *artopen _((ART_NUM)); /* open an article unless already opened */ #ifdef USE_NNTP # define seekart(pos) nntp_seekart(pos) # define readart(s,len) nntp_readart(s,len) # define tellart() nntp_tellart() #else # define seekart(pos) fseek(artfp, pos, 0) # define readart(s,len) fgets(s,len,artfp) # define tellart() ftell(artfp) #endif trn-3.6/intrp.c0000664000000000000000000007524511124144463010372 0ustar /* $Id: intrp.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "util2.h" #include "search.h" #include "cache.h" #include "bits.h" #include "head.h" #include "trn.h" #include "artsrch.h" #include "ng.h" #include "ngdata.h" #include "respond.h" #include "rcstuff.h" #include "artio.h" #include "init.h" #include "term.h" #include "final.h" #include "rthread.h" #include "rt-select.h" #include "rt-util.h" #include "nntp.h" #include "INTERN.h" #include "intrp.h" static char * regexp_specials = "^$.*[\\/?%"; char orgname[] = ORGNAME; #ifdef HAS_UNAME #include struct utsname utsn; #endif #ifdef TILDENAME static char *tildename = Nullch; static char *tildedir = Nullch; #endif #ifdef CONDSUB COMPEX cond_compex; char *skipinterp _((char *,char *)); #endif static void abort_interp _((void)); void intrp_init(tcbuf) char *tcbuf; { char *getlogin(); #ifdef CONDSUB init_compex(&cond_compex); #endif /* get environmental stuff */ #ifdef NEWS_ADMIN { #ifdef HAS_GETPWENT struct passwd *getpwnam(); struct passwd *pwd = getpwnam(NEWS_ADMIN); if (pwd != NULL) newsuid = pwd->pw_uid; #else #ifdef TILDENAME char tildenews[2+sizeof NEWS_ADMIN]; strcpy(tildenews, "~"); strcat(tildenews, NEWS_ADMIN); (void) filexp(tildenews); #else ??? "Define either HAS_GETPWENT or TILDENAME to get NEWS_ADMIN" #endif /* TILDENAME */ #endif /* HAS_GETPWENT */ } #endif /* NEWS_ADMIN */ /* get home directory */ homedir = getenv("HOME"); if (homedir == Nullch) homedir = getenv("LOGDIR"); dotdir = getval("DOTDIR",homedir); /* get login name */ loginName = getenv("USER"); if (loginName == Nullch) { loginName = getenv("LOGNAME"); #ifdef GETLOGIN if (loginName == Nullch) loginName = savestr(getlogin()); #endif } spool = savestr(filexp(NEWSSPOOL)); /* usually /usr/spool/news */ threaddir = filexp(THREAD_DIR); if (strEQ(threaddir,spool)) threaddir = spool; else threaddir = savestr(threaddir); overviewdir = filexp(OVERVIEW_DIR); if (strEQ(overviewdir,spool)) overviewdir = spool; else overviewdir = savestr(overviewdir); #ifdef NEWS_ADMIN /* if this is the news admin then load his UID into newsuid */ if (strEQ(loginName,NEWS_ADMIN)) newsuid = getuid(); #endif if (checkflag) /* that getwd below takes ~1/3 sec. */ return; /* and we do not need it for -c */ getwd(tcbuf); /* find working directory name */ origdir = savestr(tcbuf); /* and remember it */ /* get the real name of the person (%N) */ /* Must be done after loginName is read in because BERKNAMES uses that */ strcpy(tcbuf,getrealname((long)getuid())); realname = savestr(tcbuf); /* name of header file (%h) */ headname = savestr(filexp(HEADNAME)); /* host name that goes in postings (%H) */ phostname = PHOSTNAME; if (*phostname == '/' || *phostname == '~') { phostname = filexp(phostname); if ((tmpfp = fopen(phostname,"r")) == NULL) { printf("Warning: Couldn't open %s to determine hostname!\n", phostname); sig_catcher(0); } fgets(buf, sizeof(buf), tmpfp); if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0; fclose(tmpfp); phostname = savestr(buf); } else { #ifdef HAS_GETHOSTNAME gethostname(buf,sizeof buf); #else # ifdef HAS_UNAME /* get sysname */ uname(&utsn); strcpy(buf,utsn.nodename); # else # ifdef PHOSTCMD { FILE *popen(); FILE *pipefp = popen(PHOSTCMD,"r"); if (pipefp == Nullfp) { printf("Can't find hostname\n"); sig_catcher(0); } fgets(buf,sizeof buf,pipefp); buf[strlen(buf)-1] = '\0'; /* wipe out newline */ pclose(pipefp); } # else *buf = '\0'; # endif /* PHOSTCMD */ # endif /* HAS_UNAME */ #endif /* HAS_GETHOSTNAME */ if (*buf) { if (*phostname == '.' && phostname[1]) { strcat(buf,phostname); } phostname = savestr(buf); } } } /* expand filename via %, ~, and $ interpretation */ /* returns pointer to static area */ /* Note that there is a 1-deep cache of ~name interpretation */ char * filexp(s) register char *s; { static char filename[CBUFLEN]; char scrbuf[CBUFLEN]; register char *d; #ifdef DEBUG if (debug & DEB_FILEXP) printf("< %s\n",s) FLUSH; #endif interp(filename, (sizeof filename), s); /* interpret any % escapes */ #ifdef DEBUG if (debug & DEB_FILEXP) printf("%% %s\n",filename) FLUSH; #endif s = filename; if (*s == '~') { /* does destination start with ~? */ if (!*(++s) || *s == '/') { sprintf(scrbuf,"%s%s",homedir,s); /* swap $HOME for it */ #ifdef DEBUG if (debug & DEB_FILEXP) printf("~ %s\n",scrbuf) FLUSH; #endif strcpy(filename,scrbuf); } else { #ifdef TILDENAME for (d=scrbuf; isalnum(*s); s++,d++) *d = *s; *d = '\0'; if (tildedir && strEQ(tildename,scrbuf)) { strcpy(scrbuf,tildedir); strcat(scrbuf, s); strcpy(filename, scrbuf); #ifdef DEBUG if (debug & DEB_FILEXP) printf("r %s %s\n",tildename,tildedir) FLUSH; #endif } else { if (tildename) { free(tildename); free(tildedir); } tildedir = Nullch; tildename = savestr(scrbuf); #ifdef HAS_GETPWENT /* getpwnam() is not the paragon of efficiency */ { #ifdef notdef struct passwd *getpwnam _((char*)); #endif struct passwd *pwd = getpwnam(tildename); if (pwd == NULL) { printf("%s is an unknown user. Using default.\n",tildename) FLUSH; return Nullch; } sprintf(scrbuf,"%s%s",pwd->pw_dir,s); tildedir = savestr(pwd->pw_dir); strcpy(filename,scrbuf); endpwent(); } #else /* this will run faster, and is less D space */ { /* just be sure LOGDIRFIELD is correct */ FILE *pfp = fopen("/etc/passwd","r"); char tmpbuf[512]; int i; if (pfp == Nullfp) { printf(cantopen,"passwd") FLUSH; sig_catcher(0); } while (fgets(tmpbuf,512,pfp) != Nullch) { d = cpytill(scrbuf,tmpbuf,':'); #ifdef DEBUG if (debug & DEB_FILEXP) printf("p %s\n",tmpbuf) FLUSH; #endif if (strEQ(scrbuf,tildename)) { for (i=LOGDIRFIELD-2; i; i--) { if (d) d = index(d+1,':'); } if (d) { cpytill(scrbuf,d+1,':'); tildedir = savestr(scrbuf); strcat(scrbuf,s); strcpy(filename,scrbuf); } break; } } fclose(pfp); } #endif } #else /* !TILDENAME */ #ifdef VERBOSE IF(verbose) fputs("~loginname not implemented.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("~login not impl.\n",stdout) FLUSH; #endif #endif } } else if (*s == '$') { /* starts with some env variable? */ d = scrbuf; *d++ = '%'; if (s[1] == '{') strcpy(d,s+2); else { *d++ = '{'; for (s++; isalnum(*s); s++) *d++ = *s; /* skip over token */ *d++ = '}'; strcpy(d,s); } #ifdef DEBUG if (debug & DEB_FILEXP) printf("$ %s\n",scrbuf) FLUSH; #endif interp(filename, (sizeof filename), scrbuf); /* this might do some extra '%'s but */ /* that is how the Mercedes Benz */ } #ifdef DEBUG if (debug & DEB_FILEXP) printf("> %s\n",filename) FLUSH; #endif return filename; } #ifdef CONDSUB /* skip interpolations */ char * skipinterp(pattern,stoppers) register char *pattern; char *stoppers; { while (*pattern && (!stoppers || !index(stoppers,*pattern))) { #ifdef DEBUG if (debug & DEB_INTRP) printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern); #endif if (*pattern == '%' && pattern[1]) { switch_again: switch (*++pattern) { case '^': case '_': case '\\': case '\'': case '>': case ')': goto switch_again; case ':': pattern++; while (*pattern && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) { pattern++; } pattern--; goto switch_again; case '{': for (pattern++; *pattern && *pattern != '}'; pattern++) if (*pattern == '\\') pattern++; break; case '[': for (pattern++; *pattern && *pattern != ']'; pattern++) if (*pattern == '\\') pattern++; break; #ifdef CONDSUB case '(': { pattern = skipinterp(pattern+1,"!="); if (!*pattern) goto getout; for (pattern++; *pattern && *pattern != '?'; pattern++) if (*pattern == '\\') pattern++; if (!*pattern) goto getout; pattern = skipinterp(pattern+1,":)"); if (*pattern == ':') pattern = skipinterp(pattern+1,")"); break; } #endif #ifdef BACKTICK case '`': { pattern = skipinterp(pattern+1,"`"); break; } #endif #ifdef PROMPTTTY case '"': pattern = skipinterp(pattern+1,"\""); break; #endif default: break; } pattern++; } else { if (*pattern == '^' && pattern[1]) pattern += 2; else if (*pattern == '\\' && pattern[1]) pattern += 2; else pattern++; } } getout: return pattern; /* where we left off */ } #endif /* interpret interpolations */ char * dointerp(dest,destsize,pattern,stoppers) register char *dest; register int destsize; register char *pattern; char *stoppers; { char *subj_buf = Nullch; char *ngs_buf = Nullch; char *refs_buf = Nullch; char *artid_buf = Nullch; char *reply_buf = Nullch; char *from_buf = Nullch; char *path_buf = Nullch; char *follow_buf = Nullch; char *dist_buf = Nullch; char *line_buf = Nullch; char *line_split = Nullch; char *orig_dest = dest; register char *s, *h; register int i; char scrbuf[512]; char spfbuf[512]; static char *input_str = Nullch; static int input_siz = 0; bool upper = FALSE; bool lastcomp = FALSE; bool re_quote = FALSE; int tick_quote = 0; bool address_parse = FALSE; bool comment_parse = FALSE; bool proc_sprintf = FALSE; int metabit = 0; while (*pattern && (!stoppers || !index(stoppers,*pattern))) { #ifdef DEBUG if (debug & DEB_INTRP) printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern); #endif if (*pattern == '%' && pattern[1]) { upper = FALSE; lastcomp = FALSE; re_quote = FALSE; tick_quote = 0; address_parse = FALSE; comment_parse = FALSE; proc_sprintf = FALSE; for (s=Nullch; !s; ) { switch (*++pattern) { case '^': upper = TRUE; break; case '_': lastcomp = TRUE; break; case '\\': re_quote = TRUE; break; case '\'': tick_quote++; break; case '>': address_parse = TRUE; break; case ')': comment_parse = TRUE; break; case ':': proc_sprintf = TRUE; h = spfbuf; *h++ = '%'; pattern++; /* Skip over ':' */ while (*pattern && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) { *h++ = *pattern++; } *h++ = 's'; *h++ = '\0'; pattern--; break; case '/': #ifdef ARTSEARCH s = scrbuf; if (!index("/?g",pattern[-2])) *s++ = '/'; strcpy(s,lastpat); s += strlen(s); if (pattern[-2] != 'g') { if (index("/?",pattern[-2])) *s++ = pattern[-2]; else *s++ = '/'; if (art_doread) *s++ = 'r'; if (art_howmuch != ARTSCOPE_SUBJECT) { *s++ = scopestr[art_howmuch]; if (art_howmuch == ARTSCOPE_ONEHDR) { safecpy(s,art_srchhdr, (sizeof scrbuf) - (s-scrbuf)); s = index(s,':') + 1; if (!s) s = scrbuf+(sizeof scrbuf)-1; } } } *s = '\0'; s = scrbuf; #else s = nullstr; #endif break; case '{': pattern = cpytill(scrbuf,pattern+1,'}'); if (s = index(scrbuf,'-')) *s++ = '\0'; else s = nullstr; s = getval(scrbuf,s); break; case '[': if (in_ng) { pattern = cpytill(scrbuf,pattern+1,']'); i = set_line_type(scrbuf,scrbuf+strlen(scrbuf)); if (line_buf) free(line_buf); s = line_buf = fetchlines(art,i); } else s = nullstr; break; #ifdef CONDSUB case '(': { COMPEX *oldbra_compex = bra_compex; char rch; bool matched; pattern = dointerp(dest,destsize,pattern+1,"!="); rch = *pattern; if (rch == '!') pattern++; if (*pattern != '=') goto getout; pattern = cpytill(scrbuf,pattern+1,'?'); if (!*pattern) goto getout; s = scrbuf; h = spfbuf; proc_sprintf = FALSE; do { switch (*s) { case '^': *h++ = '\\'; break; case '\\': *h++ = '\\'; *h++ = '\\'; break; case '%': proc_sprintf = TRUE; break; } *h++ = *s; } while (*s++); if (proc_sprintf) { dointerp(scrbuf,sizeof scrbuf,spfbuf,Nullch); proc_sprintf = FALSE; } if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) { printf("%s: %s\n",scrbuf,s) FLUSH; pattern += strlen(pattern); free_compex(&cond_compex); goto getout; } matched = (execute(&cond_compex,dest) != Nullch); if (getbracket(&cond_compex, 0)) /* were there brackets? */ bra_compex = &cond_compex; if (matched==(rch == '=')) { pattern = dointerp(dest,destsize,pattern+1,":)"); if (*pattern == ':') pattern = skipinterp(pattern+1,")"); } else { pattern = skipinterp(pattern+1,":)"); if (*pattern == ':') pattern++; pattern = dointerp(dest,destsize,pattern,")"); } s = dest; bra_compex = oldbra_compex; free_compex(&cond_compex); break; } #endif #ifdef BACKTICK case '`': { FILE *pipefp, *popen(); pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`"); pipefp = popen(scrbuf,"r"); if (pipefp != Nullfp) { int len; len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1, pipefp); scrbuf[len] = '\0'; pclose(pipefp); } else { printf("\nCan't run %s\n",scrbuf); *scrbuf = '\0'; } for (s=scrbuf; *s; s++) { if (*s == '\n') { if (s[1]) *s = ' '; else *s = '\0'; } } s = scrbuf; break; } #endif #ifdef PROMPTTTY case '"': pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\""); fputs(scrbuf,stdout) FLUSH; resetty(); fgets(scrbuf, sizeof scrbuf, stdin); noecho(); crmode(); i = strlen(scrbuf); if (scrbuf[i-1] == '\n') { scrbuf[--i] = '\0'; } growstr(&input_str, &input_siz, i+1); safecpy(input_str, scrbuf, i+1); s = input_str; break; #endif case '~': s = homedir; break; case '.': s = dotdir; break; case '$': s = scrbuf; sprintf(s,"%ld",our_pid); break; case '#': s = scrbuf; sprintf(s,"%d",perform_cnt); break; case '?': s = " "; line_split = dest; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': #ifdef CONDSUB s = getbracket(bra_compex,*pattern - '0'); #else s = nullstr; #endif break; case 'a': if (in_ng) { s = scrbuf; sprintf(s,"%ld",(long)art); } else s = nullstr; break; case 'A': if (in_ng) { #ifdef LINKART s = linkartname; /* so Eunice people get right file */ #else s = scrbuf; #ifdef USE_NNTP if (artopen(art)) nntp_finishbody(FB_SILENT); sprintf(s,"%s/%s",spool,nntp_artname()); #else sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art); #endif #endif } else s = nullstr; break; case 'b': s = savedest; break; case 'B': s = scrbuf; sprintf(s,"%ld",(long)savefrom); break; case 'c': s = ngdir; break; case 'C': s = ngname; break; case 'd': s = scrbuf; sprintf(s,"%s/%s",spool,ngdir); break; case 'D': if (in_ng) s = dist_buf = fetchlines(art,DIST_LINE); else s = nullstr; break; case 'e': s = (extractprog ? extractprog : "-"); break; case 'E': s = extractdest; break; case 'f': /* from line */ if (in_ng) { parseheader(art); if (htype[REPLY_LINE].ht_minpos >= 0 && !comment_parse) { /* was there a reply line? */ if (!(s=reply_buf)) s = reply_buf = fetchlines(art,REPLY_LINE); } else if (!(s = from_buf)) s = from_buf = fetchlines(art,FROM_LINE); } else s = nullstr; break; case 'F': if (in_ng) { parseheader(art); if (htype[FOLLOW_LINE].ht_minpos >= 0) /* is there a Followup-To line? */ s = follow_buf = fetchlines(art,FOLLOW_LINE); else s = ngs_buf = fetchlines(art,NGS_LINE); } else s = nullstr; break; case 'h': /* header file name */ s = headname; break; case 'H': /* host name in postings */ s = phostname; break; case 'i': if (in_ng) { if (!(s=artid_buf)) s = artid_buf = fetchlines(art,MESSID_LINE); if (*s && *s != '<') { sprintf(scrbuf,"<%s>",artid_buf); s = scrbuf; } } else s = nullstr; break; case 'I': /* ref article indicator */ s = scrbuf; sprintf(scrbuf,"'%s'",indstr); break; case 'l': /* rn library */ #ifdef NEWS_ADMIN s = newsadmin; #else s = "???"; #endif break; case 'L': /* login id */ s = loginName; break; case 'm': /* current mode */ s = scrbuf; *s = mode; s[1] = '\0'; break; case 'M': sprintf(scrbuf,"%ld",(long)dmcount); s = scrbuf; break; case 'n': /* newsgroups */ if (in_ng) s = ngs_buf = fetchlines(art,NGS_LINE); else s = nullstr; break; case 'N': /* full name */ s = getval("NAME",realname); break; case 'o': /* organization */ #ifdef IGNOREORG s = getval("NEWSORG",orgname); #else s = getenv("NEWSORG"); if (s == Nullch) s = getval("ORGANIZATION",orgname); #endif s = filexp(s); #ifdef ORGFILE if (*s == '/') { FILE *ofp = fopen(s,"r"); if (ofp) { if (fgets(scrbuf,sizeof scrbuf,ofp) == NULL) *scrbuf = '\0'; fclose(ofp); s = scrbuf+strlen(scrbuf)-1; if (*scrbuf && *s == '\n') *s = '\0'; s = scrbuf; } else s = nullstr; } #endif break; case 'O': s = origdir; break; case 'p': s = cwd; break; case 'P': s = spool; break; case 'q': s = input_str; break; case 'r': if (in_ng) { parseheader(art); if (htype[REFS_LINE].ht_minpos >= 0) { int trial_size=sizeof(scrbuf); char * trial_buf=0; refs_buf = fetchlines(art,REFS_LINE); do { if (trial_buf != 0) trial_size = trial_size*2; free(trial_buf); trial_buf = malloc(trial_size); } while (refscpy(trial_buf,trial_size,refs_buf) < 1); if (strlen(trial_buf)+1 < (sizeof scrbuf)) { strcpy(scrbuf, trial_buf); } else { reducerefs(scrbuf,(sizeof scrbuf),trial_buf); } free(trial_buf); } else *scrbuf = '\0'; s = rindex(scrbuf,'<'); } else s = nullstr; break; case 'R': if (!in_ng) { s = nullstr; break; } parseheader(art); if (htype[REFS_LINE].ht_minpos >= 0) { int trial_size=sizeof(scrbuf); char * trial_buf=0; refs_buf = fetchlines(art,REFS_LINE); do { if (trial_buf != 0) trial_size = trial_size*2; free(trial_buf); trial_buf = malloc(trial_size); } while (refscpy(trial_buf,trial_size,refs_buf) < 1); reducerefs(scrbuf,(sizeof scrbuf),trial_buf); free(trial_buf); } else *scrbuf = '\0'; if (!artid_buf) artid_buf = fetchlines(art,MESSID_LINE); if (artid_buf[0] == '<') safecat(scrbuf,artid_buf,sizeof(scrbuf)); else if (artid_buf[0]) { char tmpbuf[64]; sprintf(tmpbuf,"<%s>",artid_buf); safecat(scrbuf,tmpbuf,sizeof(scrbuf)); } s = scrbuf; break; case 's': if (!in_ng) { s = nullstr; break; } if (!(s=subj_buf)) s = subj_buf = fetchsubj(art,TRUE); /* get subject handy */ while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') { /* skip extra Re: */ s += 3; if (*s == ' ') s++; } if (h = instr(s,"- (nf", TRUE)) *h = '\0'; break; case 'S': if (!in_ng) { s = nullstr; break; } if (!(s=subj_buf)) s = subj_buf = fetchsubj(art,TRUE); /* get subject handy */ if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') { /* skip extra Re: */ s += 3; if (*s == ' ') s++; } break; case 't': case 'T': if (!in_ng) { s = nullstr; break; } parseheader(art); if (htype[REPLY_LINE].ht_minpos >= 0) { /* was there a reply line? */ if (!(s=reply_buf)) s = reply_buf = fetchlines(art,REPLY_LINE); } else if (!(s = from_buf)) s = from_buf = fetchlines(art,FROM_LINE); else s = "noname"; if (*pattern == 'T') { if (htype[PATH_LINE].ht_minpos >= 0) { /* should we substitute path? */ s = path_buf = fetchlines(art,PATH_LINE); } i = strlen(phostname); if (strnEQ(phostname,s,i) && s[i] == '!') s += i + 1; } address_parse = TRUE; /* just the good part */ break; case 'u': sprintf(scrbuf,"%ld",(long)toread[ng]); s = scrbuf; break; case 'U': { int unseen; if (!in_ng) { s = nullstr; break; } unseen = (art <= lastart) && !was_read(art); if (selected_only) { int selected; selected = (curr_artp->flags & AF_SEL); sprintf(scrbuf,"%ld", (long)selected_count - (selected && unseen)); } else sprintf(scrbuf,"%ld",(long)toread[ng] - unseen); s = scrbuf; break; } case 'v': { int selected, unseen; if (in_ng) { selected = (curr_artp->flags & AF_SEL); unseen = (art <= lastart) && !was_read(art); sprintf(scrbuf,"%ld",(long)toread[ng] - selected_count - (!selected && unseen)); s = scrbuf; } else s = nullstr; break; } case 'W': s = threaddir; break; case 'x': /* news library */ s = lib; break; case 'X': /* rn library */ s = rnlib; break; case 'y': s = tmpdir; break; case 'z': if (!in_ng) { s = nullstr; break; } #ifdef LINKART s = linkartname; /* so Eunice people get right file */ #else s = scrbuf; sprintf(s,"%ld",(long)art); #endif if (stat(s,&filestat) < 0) filestat.st_size = 0L; sprintf(scrbuf,"%5ld",(long)filestat.st_size); s = scrbuf; break; case 'Z': sprintf(scrbuf,"%ld",(long)selected_count); s = scrbuf; break; default: if (--destsize <= 0) abort_interp(); *dest++ = *pattern | metabit; s = nullstr; break; } } if (!s) s = nullstr; if (proc_sprintf) { sprintf(scrbuf,spfbuf,s); s = scrbuf; } pattern++; if (upper || lastcomp) { char *t; if (s != scrbuf) { safecpy(scrbuf,s,(sizeof scrbuf)); s = scrbuf; } if (upper || !(t=rindex(s,'/'))) t = s; while (*t && !isalpha(*t)) t++; if (islower(*t)) *t = toupper(*t); } /* Do we have room left? */ i = strlen(s); if (destsize <= i) abort_interp(); destsize -= i; /* adjust the size now. */ /* A maze of twisty little conditions, all alike... */ if (address_parse || comment_parse) { if (s != scrbuf) { safecpy(scrbuf,s,(sizeof scrbuf)); s = scrbuf; } if (address_parse) { if ((h=index(s,'<')) != Nullch) { /* grab the good part */ s = h+1; if ((h=index(s,'>')) != Nullch) *h = '\0'; } else if ((h=index(s,'(')) != Nullch) { while (h-- != s && *h == ' ') ; h[1] = '\0'; /* or strip the comment */ } } else { if (!(s = extract_name(s))) s = nullstr; } } if (metabit) { /* set meta bit while copying. */ i = metabit; /* maybe get into register */ if (s == dest) { while (*dest) *dest++ |= i; } else { while (*s) *dest++ = *s++ | i; } } else if (re_quote || tick_quote) { /* put a backslash before regexp specials while copying. */ if (s == dest) { /* copy out so we can copy in. */ safecpy(scrbuf, s, sizeof scrbuf); s = scrbuf; if (i > sizeof scrbuf) /* we truncated, ack! */ abort_interp(); } while (*s) { if ((re_quote && index(regexp_specials, *s)) || (tick_quote == 2 && *s == '"')) { if (--destsize <= 0) abort_interp(); *dest++ = '\\'; } else if (re_quote && *s == ' ' && s[1] == ' ') { *dest++ = ' '; *dest++ = '*'; while (*++s == ' ') ; continue; } else if (tick_quote && *s == '\'') { if ((destsize -= 3) <= 0) abort_interp(); *dest++ = '\''; *dest++ = '\\'; *dest++ = '\''; } *dest++ = *s++; } } else { /* straight copy. */ if (s == dest) { dest += i; } else { while (*s) *dest++ = *s++; } } } else { if (--destsize <= 0) abort_interp(); if (*pattern == '^' && pattern[1]) { ++pattern; /* skip uparrow */ i = *pattern; /* get char into a register */ if (i == '?') *dest++ = '\177' | metabit; else if (i == '(') { metabit = 0200; destsize++; } else if (i == ')') { metabit = 0; destsize++; } else *dest++ = (i & 037) | metabit; pattern++; } else if (*pattern == '\\' && pattern[1]) { ++pattern; /* skip backslash */ i = *pattern; /* get char into a register */ /* this used to be a switch but the if may save space */ if (i >= '0' && i <= '7') { i = 1; while (i < 01000 && *pattern >= '0' && *pattern <= '7') { i <<= 3; i += *pattern++ - '0'; } *dest++ = (i & 0377) | metabit; --pattern; } else if (i == 'b') *dest++ = '\b' | metabit; else if (i == 'f') *dest++ = '\f' | metabit; else if (i == 'n') *dest++ = '\n' | metabit; else if (i == 'r') *dest++ = '\r' | metabit; else if (i == 't') *dest++ = '\t' | metabit; else *dest++ = i | metabit; pattern++; } else *dest++ = *pattern++ | metabit; } } *dest = '\0'; if (line_split != Nullch) if ((int)strlen(orig_dest) > 79) *line_split = '\n'; getout: if (subj_buf != Nullch) /* return any checked out storage */ free(subj_buf); if (ngs_buf != Nullch) free(ngs_buf); if (refs_buf != Nullch) free(refs_buf); if (artid_buf != Nullch) free(artid_buf); if (reply_buf != Nullch) free(reply_buf); if (from_buf != Nullch) free(from_buf); if (path_buf != Nullch) free(path_buf); if (follow_buf != Nullch) free(follow_buf); if (dist_buf != Nullch) free(dist_buf); if (line_buf != Nullch) free(line_buf); return pattern; /* where we left off */ } void interp(dest,destsize,pattern) char *dest; int destsize; char *pattern; { dointerp(dest,destsize,pattern,Nullch); #ifdef DEBUG if (debug & DEB_FILEXP) fputs(dest,stdout); #endif } /* copy a references line, normalizing as we go */ int refscpy(dest,destsize,src) register char *dest, *src; register int destsize; { register char *dot, *at, *beg; char tmpbuf[64]; while (*src) { if (*src != '<') { if (--destsize <= 0) break; *dest++ = '<'; at = dot = Nullch; beg = src; while (*src && *src != ' ' && *src != ',') { if (*src == '.') dot = src; else if (*src == '@') at = src; if (--destsize <= 0) break; *dest++ = *src++; } if (destsize <= 0) break; if (dot && !at) { int len; *dest = *dot++ = '\0'; sprintf(tmpbuf,"%s@%s.UUCP",dot,beg); len = strlen(tmpbuf); if (destsize > len) { strcpy(dest,tmpbuf); dest = dest + len; destsize -= len; } } if (--destsize <= 0) break; *dest++ = '>'; } else { while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ; if (destsize <= 0) break; } while (*src == ' ' || *src == '\t' || *src == '\n' || *src == ',') src++; if (*src && --destsize > 0) *dest++ = ' '; } *dest = '\0'; return(destsize); } /* reduce a references line */ /* note, modifies src */ void reducerefs(dest,destsize,src) register char *dest, *src; register int destsize; { char * s; char * h; /* no more than 3 prior references PLUS the ** root article allowed, including the one ** concatenated below */ if ((s = rindex(src,'<')) > src) { *s = '\0'; h = rindex(src,'<'); *s = '<'; if (h > src) { s = index(src+1,'<'); if (s < h) strcpy(s,h); } } if (strlen(src) < destsize) { strcpy(dest,src); } else { if ((s = rindex(src,'<')) > src) { if (strlen(s) < destsize) { strcpy(dest,s); } else { *dest = '\0'; } } else { *dest = '\0'; } } } /* get the person's real name from /etc/passwd */ /* (string is overwritten, so it must be copied) */ char * getrealname(uid) long uid; { char *s, *c; #ifdef PASSNAMES #ifdef HAS_GETPWENT #ifdef notdef struct passwd *getpwuid _((uid_t)); #endif struct passwd *pwd = getpwuid(uid); if (!pwd) return nullstr; s = pwd->pw_gecos; #else char tmpbuf[512]; int i; getpw(uid, tmpbuf); for (s=tmpbuf, i=GCOSFIELD-1; i; i--) { if (s) s = index(s,':')+1; } if (!s) return nullstr; cpytill(tmpbuf,s,':'); s = tmpbuf; #endif #ifdef BERKNAMES #ifdef BERKJUNK while (*s && !isalnum(*s) && *s != '&') s++; #endif if ((c = index(s, ',')) != Nullch) *c = '\0'; if ((c = index(s, ';')) != Nullch) *c = '\0'; s = cpytill(buf,s,'&'); if (*s == '&') { /* whoever thought this one up was */ c = buf + strlen(buf); /* in the middle of the night */ strcat(c,loginName); /* before the morning after */ strcat(c,s+1); if (islower(*c)) *c = toupper(*c); /* gack and double gack */ } #else if ((c = index(s, '(')) != Nullch) *c = '\0'; if ((c = index(s, '-')) != Nullch) s = c; strcpy(buf,s); #endif #ifdef HAS_GETPWENT endpwent(); #endif return buf; /* return something static */ #else if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) { fgets(buf,sizeof buf,tmpfp); fclose(tmpfp); buf[strlen(buf)-1] = '\0'; return buf; } return "PUT YOUR NAME HERE"; #endif } static void abort_interp() { fputs("\n% interp buffer overflow!\n",stdout) FLUSH; sig_catcher(0); } trn-3.6/addng.c0000664000000000000000000002000405644354543010306 0ustar /* $Id: addng.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "trn.h" #include "ngdata.h" #include "last.h" #include "util.h" #include "intrp.h" #include "only.h" #include "rcstuff.h" #include "nntp.h" #include "final.h" #include "INTERN.h" #include "addng.h" void addng_init() { ; } #ifdef FINDNEWNG /* generate a list of new newsgroups from active file */ bool newlist(munged,checkinlist) bool_int munged; /* are we scanning the whole file? */ bool_int checkinlist; { char *tmpname; register char *s, *status; register NG_NUM ngnum; tmpname = filexp(RNEWNAME); tmpfp = fopen(tmpname,"w+"); if (tmpfp == Nullfp) { printf(cantcreate,tmpname) FLUSH; return FALSE; } UNLINK(tmpname); /* be nice to the world */ for (;;) { #ifdef USE_NNTP if (!actfp) { if (NNTP_LIST_END(ser_line)) break; strcpy(buf, ser_line); nntp_gets(ser_line, sizeof ser_line); } else #endif if (fgets(buf,LBUFLEN,actfp) == Nullch) break; /* Check if they want to break out of the new newsgroups search */ if (int_count) { int_count = 0; fclose(tmpfp); return FALSE; } if (s = index(buf,' ')) { status=s; while (isdigit(*status) || isspace(*status)) status++; *s++ = '\0'; if (strnEQ(buf,"to.",3) || *status == 'x' || *status == '=') /* since = groups are refiling to another group, just ignore their existence */ continue; #ifdef ACTIVE_TIMES if (inlist(buf) && ((ngnum = find_ng(buf)) == nextrcline || toread[ngnum] == TR_UNSUB) #else if (checkinlist ? (inlist(buf) && ((ngnum = find_ng(buf)) == nextrcline || toread[ngnum] == TR_UNSUB)) : (find_ng(buf) == nextrcline && birthof(buf,(ART_NUM)atol(s)) > lasttime) #endif ) { /* if not in .newsrc and younger */ /* than the last time we checked */ fprintf(tmpfp,"%s\n",buf); /* then remember said newsgroup */ } #ifdef FASTNEW else { /* not really a new group */ if (!munged) { /* did we assume not munged? */ fclose(tmpfp); /* then go back, knowing that */ return TRUE; /* active file was indeed munged */ } } #endif } #ifdef DEBUG else printf("Bad active record: %s\n",buf) FLUSH; #endif } /* we have successfully generated the list */ fseek(tmpfp,0L,0); /* rewind back to the beginning */ while (fgets(buf,LBUFLEN,tmpfp) != Nullch) { buf[strlen(buf)-1] = '\0'; get_ng(buf,GNG_RELOC); /* add newsgroup, maybe */ } fclose(tmpfp); /* be nice to ourselves */ return FALSE; /* do not call us again */ } #ifdef ACTIVE_TIMES #ifdef USE_NNTP bool find_new_groups() { char *tmpname; register char *s; time_t server_time; NG_NUM oldnext = nextrcline; /* remember # lines in newsrc */ tmpname = filexp(RNEWNAME); tmpfp = fopen(tmpname,"w+"); if (tmpfp == Nullfp) { printf(cantcreate,tmpname) FLUSH; return FALSE; } UNLINK(tmpname); /* be nice to the world */ server_time = nntp_time(); if (!nntp_newgroups(lastnewtime)) { fclose(tmpfp); printf("Can't get new groups from server:\n%s\n", ser_line); return FALSE; } while (1) { nntp_gets(ser_line, sizeof ser_line); #ifdef DEBUG if (debug & DEB_NNTP) printf("<%s\n", ser_line) FLUSH; #endif if (NNTP_LIST_END(ser_line)) break; if ((s = index(ser_line, ' ')) != Nullch) *s++ = '\0'; else if (findact(buf, ser_line, strlen(ser_line), 0L) >= 0) s = buf + strlen(ser_line) + 1; if (s) { while (isdigit(*s) || isspace(*s)) s++; if (*s == 'x' || *s == '=') continue; } fprintf(tmpfp,"%s\n",ser_line); } /* we have successfully generated the list */ if (ftell(tmpfp)) { fputs("\nFinding new newsgroups:\n",stdout) FLUSH; fseek(tmpfp,0L,0); /* rewind back to the beginning */ while (fgets(buf,LBUFLEN,tmpfp) != Nullch) { buf[strlen(buf)-1] = '\0'; get_ng(buf,0); /* add newsgroup, maybe */ } lastnewtime = server_time; /* remember when we found new groups */ } /* (ends up back in .rnlast) */ fclose(tmpfp); /* be nice to ourselves */ return oldnext != nextrcline; } #else /* !USE_NNTP */ bool find_new_groups() { register char *s; time_t lastone; NG_NUM oldnext = nextrcline; /* remember # lines in newsrc */ fstat(fileno(actfp),&filestat); /* find active file size */ lastactsiz = filestat.st_size; /* just to save it in .rnlast */ stat(ACTIVE_TIMES,&filestat); /* did active.times file grow? */ if (filestat.st_size == lastnewsize) return FALSE; lastnewsize = filestat.st_size; fputs("\nChecking for new newsgroups...\n",stdout) FLUSH; s = filexp(ACTIVE_TIMES); tmpfp = fopen(s,"r"); if (tmpfp == Nullfp) { printf(cantopen,s) FLUSH; return FALSE; } lastone = time(Null(time_t*)) - 24L * 60 * 60 - 1; while (fgets(buf,LBUFLEN,tmpfp) != Nullch) { if ((s = index(buf, ' ')) != Nullch) if ((lastone = atol(s+1)) >= lastnewtime) { char tmpbuf[LBUFLEN]; *s = '\0'; if (findact(tmpbuf, buf, s - buf, 0L) >= 0) { s = tmpbuf + (s-buf) + 1; while (isdigit(*s) || isspace(*s)) s++; if (*s != 'x' && *s != '=') { get_ng(buf,0); /* add newsgroup, maybe */ } } } } fclose(tmpfp); lastnewtime = lastone+1; /* remember time of last new group */ /* (ends up back in .rnlast) */ return oldnext != nextrcline; } #endif /* !USE_NNTP */ #else /* not ACTIVE_TIMES */ bool find_new_groups() { long oldactsiz = lastactsiz; NG_NUM oldnext = nextrcline; /* remember # lines in newsrc */ #ifdef USE_NNTP if (!actfp) { return 0; } #endif fstat(fileno(actfp),&filestat); /* did active file grow? */ if (filestat.st_size == lastactsiz) return FALSE; lastactsiz = filestat.st_size; /* remember new size */ #ifdef VERBOSE IF(verbose) fputs("\nChecking active file for new newsgroups...\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNew newsgroups:\n",stdout) FLUSH; #endif #ifdef FASTNEW /* bad soft ptrs -> edited active */ if (!writesoft && oldactsiz) { /* maybe just do tail of file? */ fseek(actfp,oldactsiz-NL_SIZE,0); fgets(buf,LBUFLEN,actfp); if (*buf == '\n' && !newlist(FALSE,FALSE)) goto bugout; } #endif fseek(actfp,0L,0); /* rewind active file */ newlist(TRUE,FALSE); /* sure hope they use hashing... */ bugout: return oldnext != nextrcline; } /* return creation time of newsgroup */ time_t birthof(ngnam,ngsize) char *ngnam; ART_NUM ngsize; { #ifdef USE_NNTP /* ngsize not used */ long tot; if (!nntp_group(ngnam,-1)) return 0; /* not a real group */ (void) sscanf(ser_line,"%*d%ld",&tot); if (tot > 0) return time(Null(time_t*)); return 0; #else /* !USE_NNTP */ char tst[512]; sprintf(tst, ngsize ? "%s/%s/1" : "%s/%s" ,spool,getngdir(ngnam)); if (stat(tst,&filestat) < 0) return (ngsize ? 0L : time(Null(time_t*))); /* not there, assume something good */ return filestat.st_mtime; #endif /* !USE_NNTP */ } #endif /* ACTIVE_TIMES */ bool scanactive() { NG_NUM oldnext = nextrcline; /* remember # lines in newsrc */ if (actfp) { fseek(actfp,0L,0); newlist(TRUE,TRUE); } #ifdef USE_NNTP else { if (maxngtodo != 1) { strcpy(buf, "*"); } else { sprintf(buf,"*%s*", ngtodo[0]); } if (nntp_list("active", buf, strlen(buf)) == 1) { newlist(TRUE,TRUE); } } #endif if (nextrcline != oldnext) { /* did we add any new groups? */ return TRUE; } return FALSE; } #endif /* FINDNEWNG */ trn-3.6/only.c0000664000000000000000000000540105420174104010177 0ustar /* $Id: only.c,v 3.0 1991/09/09 20:23:31 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "util.h" #include "final.h" #include "ngsrch.h" #include "INTERN.h" #include "only.h" void only_init() { ; } void setngtodo(pat) char *pat; { #ifdef ONLY #ifdef SPEED_OVER_MEM char *s; #endif if (!*pat) return; if (maxngtodo < NGMAX) { ngtodo[maxngtodo] = savestr(pat); #ifdef SPEED_OVER_MEM #ifndef lint compextodo[maxngtodo] = (COMPEX*)safemalloc(sizeof(COMPEX)); #endif /* lint */ init_compex(compextodo[maxngtodo]); compile(compextodo[maxngtodo],pat,TRUE,TRUE); if ((s = ng_comp(compextodo[maxngtodo],pat,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; finalize(1); } #endif maxngtodo++; } #else notincl("o"); #endif } /* if command line list is non-null, is this newsgroup wanted? */ bool inlist(ngnam) char *ngnam; { #ifdef ONLY register int i; #ifdef SPEED_OVER_MEM if (maxngtodo == 0) return TRUE; for (i=0; i 1 ? ", etc." : nullstr) FLUSH; ELSE #endif #ifdef TERSE fputs("\nExiting \"only\".\n",stdout) FLUSH; #endif for (whicharg = 0; whicharg < maxngtodo; whicharg++) { free(ngtodo[whicharg]); #ifdef SPEED_OVER_MEM free_compex(compextodo[whicharg]); #ifndef lint free((char*)compextodo[whicharg]); #endif /* lint */ #endif } maxngtodo = 0; emptyOnly = FALSE; } } #endif trn-3.6/addng.h0000664000000000000000000000154405420174052010306 0ustar /* $Id: addng.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ void addng_init _((void)); #ifdef FINDNEWNG bool newlist _((bool_int,bool_int)); # ifdef ACTIVE_TIMES bool find_new_groups _((void)); # else time_t birthof _((char*,ART_NUM)); # endif bool scanactive _((void)); #endif trn-3.6/filexp.SH0000664000000000000000000000161205555112757010614 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting filexp (with variable substitutions)" $spitshell >filexp <>filexp <<'!NO!SUBS!' : expand filename case "$1" in ~/*|~) echo $1 | $sed "s|~|${HOME-$LOGDIR}|" ;; ~*) if $test -f /bin/csh; then /bin/csh -f -c "glob $1" failed=$? echo "" exit $failed else name=`$expr x$1 : '..\([^/]*\)'` dir=`$sed -n -e "/^${name}:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\).*"'$'"/\1/" -e p -e q -e '}' &2 exit 1 fi case "$1" in */*) echo $dir/`$expr x$1 : '..[^/]*/\(.*\)'` ;; *) echo $dir ;; esac fi ;; *) echo $1 ;; esac !NO!SUBS! chmod +x filexp $eunicefix filexp trn-3.6/term.h0000664000000000000000000002125505660373072010212 0ustar /* $Id: term.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT char circlebuf[PUSHSIZE]; EXT int nextin INIT(0); EXT int nextout INIT(0); EXT char lastchar; #ifdef PENDING # ifdef FIONREAD EXT long iocount INIT(0); # ifndef lint #define input_pending() (macro_pending() || (ioctl(0, FIONREAD, &iocount),(int)iocount)) # else #define input_pending() bizarre # endif /* lint */ # else /* FIONREAD */ # ifdef HAS_RDCHK #define input_pending() (macro_pending() || rdchk(0)) # else /* HAS_RDCHK */ int circfill(); EXT int devtty INIT(0); # ifndef lint #define input_pending() (macro_pending() || circfill()) # else #define input_pending() bizarre # endif /* lint */ # endif /* HAS_RDCHK */ # endif /* FIONREAD */ #else /* PENDING */ # ifndef lint #define input_pending() macro_pending() # else #define input_pending() bizarre # endif /* lint */ #endif /* PENDING */ /* stuff wanted by terminal mode diddling routines */ #ifdef I_TERMIO EXT struct termio _tty, _oldtty; #else # ifdef I_TERMIOS EXT struct termios _tty, _oldtty; # else EXT struct sgttyb _tty; EXT int _res_flg INIT(0); # endif #endif EXT int _tty_ch INIT(2); EXT bool bizarre INIT(FALSE); /* do we need to restore terminal? */ /* terminal mode diddling routines */ #ifdef I_TERMIO #define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty)) #define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define savetty() (ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty)) #define resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty)) #define unflush_output() #else /* !I_TERMIO */ # ifdef I_TERMIOS #define crmode() ((bizarre=1), _tty.c_lflag &= ~ICANON,_tty.c_cc[VMIN]=1,tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,tcsetattr(_tty_ch, TCSAFLUSH,&_tty)) #define echo() ((bizarre=1),_tty.c_lflag |= ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty)) #define savetty() (tcgetattr(_tty_ch, &_oldtty),tcgetattr(_tty_ch, &_tty)) #define resetty() ((bizarre=0),tcsetattr(_tty_ch, TCSAFLUSH, &_oldtty)) #define unflush_output() # else /* !I_TERMIOS */ #define raw() ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty)) #define noraw() ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty)) #define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty)) #define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty)) #define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty)) #define nl() ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty)) #define nonl() ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty)) #define savetty() (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags) #define resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty)) # ifdef LFLUSHO # ifndef lint EXT int lflusho INIT(LFLUSHO); # else EXT long lflusho INIT(LFLUSHO); # endif /* lint */ #define unflush_output() (ioctl(_tty_ch,TIOCLBIC,&lflusho)) # else #define unflush_output() # endif /* LFLUSHO */ # endif /* I_TERMIOS */ #endif /* I_TERMIO */ #ifdef TIOCSTI #ifdef lint #define forceme(c) ioctl(_tty_ch,TIOCSTI,Null(long*)) /* ghad! */ #else #define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */ #endif /* lint */ #else #define forceme(c) #endif /* termcap stuff */ /* * NOTE: if you don't have termlib you'll either have to define these strings * and the tputs routine, or you'll have to redefine the macros below */ #ifdef HAS_TERMLIB EXT int GT; /* hardware tabs */ EXT char *BC INIT(Nullch); /* backspace character */ EXT char *UP INIT(Nullch); /* move cursor up one line */ EXT char *CR INIT(Nullch); /* get to left margin, somehow */ EXT char *VB INIT(Nullch); /* visible bell */ EXT char *CL INIT(Nullch); /* home and clear screen */ EXT char *CE INIT(Nullch); /* clear to end of line */ EXT char *TI INIT(Nullch); /* initialize terminal */ EXT char *TE INIT(Nullch); /* reset terminal */ EXT char *KS INIT(Nullch); /* enter `keypad transmit' mode */ EXT char *KE INIT(Nullch); /* exit `keypad transmit' mode */ EXT char *CM INIT(Nullch); /* cursor motion */ EXT char *HO INIT(Nullch); /* home cursor */ EXT char *IL INIT(Nullch); /* insert line */ #ifdef CLEAREOL EXT char *CD INIT(Nullch); /* clear to end of display */ #endif /* CLEAREOL */ EXT char *SO INIT(Nullch); /* begin standout mode */ EXT char *SE INIT(Nullch); /* end standout mode */ EXT int SG INIT(0); /* blanks left by SO and SE */ EXT char *US INIT(Nullch); /* start underline mode */ EXT char *UE INIT(Nullch); /* end underline mode */ EXT char *UC INIT(Nullch); /* underline a character, if that's how it's done */ EXT int UG INIT(0); /* blanks left by US and UE */ EXT bool AM INIT(FALSE); /* does terminal have automatic margins? */ EXT bool XN INIT(FALSE); /* does it eat 1st newline after automatic wrap? */ EXT char PC INIT(0); /* pad character for use by tputs() */ #ifdef _POSIX_SOURCE EXT speed_t outspeed INIT(0); /* terminal output speed, */ #else EXT long outspeed INIT(0); /* for use by tputs() */ #endif EXT int LINES INIT(0), COLS INIT(0); /* size of screen */ EXT int just_a_sec INIT(960); /* 1 sec at current baud rate */ /* (number of nulls) */ /* define a few handy macros */ #define backspace() tputs(BC,0,putchr) FLUSH #define clear() tputs(CL,LINES,putchr) FLUSH #define erase_eol() tputs(CE,1,putchr) FLUSH #ifdef CLEAREOL #define clear_rest() tputs(CD,LINES,putchr) FLUSH #define maybe_eol() if(erase_screen&&can_home_clear)tputs(CE,1,putchr) FLUSH #endif /* CLEAREOL */ #define underline() tputs(US,1,putchr) FLUSH #define un_underline() tputs(UE,1,putchr) FLUSH #define underchar() tputs(UC,0,putchr) FLUSH #define standout() tputs(SO,1,putchr) FLUSH #define un_standout() tputs(SE,1,putchr) FLUSH #define up_line() tputs(UP,1,putchr) FLUSH #define insert_line() tputs(IL,1,putchr) FLUSH #define carriage_return() tputs(CR,1,putchr) FLUSH #define dingaling() tputs(VB,1,putchr) FLUSH #else ???????? /* up to you */ #endif EXT int page_line INIT(1); /* line number for paging in print_line (origin 1) */ EXT bool use_xterm_mouse INIT(FALSE); EXT bool xmouse_status INIT(FALSE); /* TRUE: mouse is on */ void term_init _((void)); void term_set _((char*)); void pushchar _((char_int)); void pushstring _((char*,char_int)); void mac_init _((char*)); void mac_line _((char*,char*,int)); void show_macros _((void)); char putchr _((char_int)); /* routine for tputs to call */ void hide_pending _((void)); bool macro_pending _((void)); bool finish_command _((int)); bool finish_dblchar _((void)); void eat_typeahead _((void)); void save_typeahead _((char*, int)); void settle_down _((void)); #ifdef HAS_TERMLIB void termlib_init _((void)); void termlib_reset _((void)); #endif int read_tty _((char*,int)); void underprint _((char*)); #ifdef NOFIREWORKS void no_sofire _((void)); void no_ulfire _((void)); #endif void getcmd _((char*)); int get_anything _((void)); int pause_getcmd _((void)); void in_char _((char*,char_int,char*)); void in_answer _((char*,char_int)); int print_lines _((char*,int)); void page_init _((void)); void pad _((int)); void printcmd _((void)); void rubout _((void)); void reprint _((void)); void home_cursor _((void)); void goto_line _((int,int)); #ifdef SIGWINCH Signal_t winch_catcher _((int)); #endif /* SIGWINCH */ void arrow_macros _((char*)); void xmouse_on _((void)); void xmouse_off _((void)); trn-3.6/INTERN.h0000664000000000000000000000140005420174051010216 0ustar /* $Id: INTERN.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #undef EXT #define EXT #undef INIT #ifdef xenix #define INIT(x) =x #else #define INIT(x) = x #endif #define DOINIT trn-3.6/artstate.h0000664000000000000000000000260705420174056011065 0ustar /* $Id: artstate.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT bool reread INIT(FALSE); /* consider current art temporarily */ /* unread? */ EXT bool do_fseek INIT(FALSE); /* should we back up in article file? */ EXT bool oldsubject INIT(FALSE); /* not 1st art in subject thread */ EXT ART_LINE topline INIT(-1); /* top line of current screen */ EXT bool do_hiding INIT(TRUE); /* hide header lines with -h? */ #ifdef ROTATION EXT bool rotate INIT(FALSE); /* has rotation been requested? */ #endif EXT char *prompt; /* pointer to current prompt */ EXT char *firstline INIT(Nullch); /* special first line? */ #ifdef CUSTOMLINES EXT char *hideline INIT(Nullch); /* custom line hiding? */ EXT char *pagestop INIT(Nullch); /* custom page terminator? */ EXT COMPEX hide_compex; EXT COMPEX page_compex; #endif trn-3.6/rcstuff.h0000664000000000000000000000423305420174107010704 0ustar /* $Id: rcstuff.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT char **rcline INIT(NULL);/* pointers to lines of .newsrc */ EXT ART_UNREAD *toread INIT(NULL); /* number of articles to be read in newsgroup */ /* <0 => invalid or unsubscribed newsgroup */ #define TR_ONE ((ART_UNREAD) 1) #define TR_NONE ((ART_UNREAD) 0) #define TR_UNSUB ((ART_UNREAD) -1) /* keep this one as -1, some tests use >= TR_UNSUB */ #define TR_BOGUS ((ART_UNREAD) -2) #define TR_JUNK ((ART_UNREAD) -3) #define RCCHAR(ch) ((ch) == '0' ? ':' : (ch)) #define ADDNEW_SUB ':' #define ADDNEW_UNSUB '!' #define GNG_RELOC 0x0001 #define GNG_FUZZY 0x0002 EXT char *rcchar INIT(NULL); /* holds the character : or ! while spot is \0 */ EXT char *rcnums INIT(NULL); /* offset from rcline to numbers on line */ EXT ACT_POS *softptr INIT(NULL); /* likely ptr to active file entry for newsgroup */ EXT bool paranoid INIT(FALSE); /* did we detect some inconsistency in .newsrc? */ EXT int maxrcline INIT(0); /* current maximum # of lines in .newsrc */ EXT int addnewbydefault INIT(0); bool rcstuff_init _((void)); void abandon_ng _((NG_NUM)); bool get_ng _((char*,int)); /* return TRUE if newsgroup is found or added */ NG_NUM add_newsgroup _((char*,char_int)); #ifdef RELOCATE NG_NUM relocate_newsgroup _((NG_NUM,NG_NUM)); /* move newsgroup around */ #endif void list_newsgroups _((void)); NG_NUM find_ng _((char*)); /* return index of newsgroup */ void cleanup_rc _((void)); void sethash _((NG_NUM)); int hash _((char*)); void newsrc_check _((void)); void checkpoint_rc _((void)); void write_rc _((void)); void get_old_rc _((void)); trn-3.6/last.h0000664000000000000000000000207205420174077010200 0ustar /* $Id: last.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT char *lastngname INIT(Nullch); /* last newsgroup read, from .rnlast file */ EXT time_t lasttime INIT(0); /* time last rn was started up */ EXT long lastactsiz INIT(0); /* size of active file when rn last started up */ EXT time_t lastnewtime INIT(0); /* time last new group was found */ EXT long lastnewsize INIT(0); /* size of active.times file when rn last started up */ void last_init _((char*)); void writelast _((void)); trn-3.6/config.sh.old0000775000000000000000000000760507116537436011462 0ustar #!/bin/sh # # This file was produced by running the Configure script. It holds all the # definitions figured out by Configure. Should you modify one of these values, # do not forget to propagate your changes by running "Configure -der". You may # instead choose to run each of the .SH files by yourself, or "Configure -S". # # Configuration time: Sun Jun 4 21:41:43 BST 2000 # Configured by: matthew # Target system: linux pick 2.2.14 #1 wed jan 12 13:49:58 gmt 2000 i686 unknown d_bsd='undef' d_eunice='undef' d_xenix='undef' eunicefix=':' Mcc='Mcc' awk='awk' basename='basename' bash='bash' cat='cat' cp='cp' cpp='cpp' csh='csh' date='date' diff='diff' echo='echo' ed='ed' egrep='egrep' expr='expr' grep='grep' inews='inews' ispell='ispell' ksh='ksh' less='less' lint='' mail='mail' metamail='metamail' mhn='mhn' mkdir='mkdir' more='more' munpack='munpack' mv='mv' nroff='nroff' pg='pg' rm='rm' rmail='rmail' sed='sed' sendmail='sendmail' smail='smail' sort='sort' spell='spell' tail='tail' test='test' tr='tr' uname='uname' uniq='uniq' uuname='uuname' vi='vi' vspell='' who='' hint='previous' myuname='linux pick 2.2.14 #1 wed jan 12 13:49:58 gmt 2000 i686 unknown ' srcdir='.' vincludes='' vpath='.' Id='$Id' Log='$Log' active='none' acttimes='nntp' d_acttimes='define' myactive='.falseactive' bin='/usr/bin' binexp='/usr/bin' installbin='/usr/bin' cc='gcc' ccflags='' cppflags='' ldflags='-s' lkflags='' optimize='-O3' cf_by='matthew' cf_time='Sun Jun 4 21:41:43 BST 2000' contains='grep' cpplast='-' cppminus='-' cpprun='gcc -E' cppstdin='gcc -E' d_ftime='define' aphostcmd='' d_gethname='undef' d_phostcmd='undef' d_uname='undef' d_getpwent='define' d_getcwd='undef' d_getwd='define' d_havetlib='define' termlib='-lcurses' d_ignoreorg='undef' d_internet='define' d_memcmp='define' d_memcpy='define' d_memset='define' d_mimeshow='undef' d_mimestore='undef' mimeshow="none" mimestore="none" d_newsadm='define' newsadmin='news' d_nntp='define' d_xdata='define' d_genauth='define' servername='/etc/news/server' d_nolnbuf='undef' d_normsig='define' jobslib='' d_novoid='undef' void='' d_portable='define' d_rdchk='undef' d_rename='define' d_sigblock='define' d_sighold='undef' d_sizet='undef' d_strccmp='define' d_strchr='define' d_strftime='define' strftimec='' strftimeo='' d_libndir='undef' d_usendir='undef' libndir='' ndirc='' ndiro='' d_vfork='define' d_voidsig='define' signal_t='void' defeditor='/usr/bin/emacs' filexp='/usr/lib/trn/filexp' d_dirnamlen='' i_dirent='define' i_ptem='undef' i_stdlib='define' i_string='define' strings='/usr/include/string.h' i_sysdir='define' d_voidtty='' i_bsdioctl='' i_sysfilio='undef' i_sysioctl='define' i_syssockio='' i_sysndir='undef' i_sgtty='undef' i_termio='undef' i_termios='define' i_systime='define' i_time='undef' i_unistd='define' i_vfork='undef' ispell_options='' ispell_prg='/usr/bin/ispell' libc='/usr/lib/libc.a' plibpth='/usr/ccs/lib /usr/lib /usr/ucblib /usr/local/lib' xlibpth='/usr/lib/386 /lib/386' libs=' ' citydist='none' cntrydist='none' contdist='none' locdist='none' multistatedist='none' orgdist='none' statedist='none' mailer='/usr/sbin/sendmail' mailfile='/var/spool/mail/%L' installmansrc='/usr/man/man1' manext='1' mansrc='/usr/man/man1' mansrcexp='/usr/man/man1' mboxchar='F' c='' n='-n' d_berknames='define' d_passnames='define' d_usgnames='undef' nametype='bsd' inewsloc='inews' newslib='/tmp' newslibexp='/tmp' newsspool='/tmp' orgname='/etc/news/organization' package='trn' spackage='Trn' pager='/usr/bin/pager' phost='/etc/news/whoami' phostcmd='' prefshell='/bin/sh' installprivlib='/usr/lib/trn' privlib='/usr/lib/trn' privlibexp='/usr/lib/trn' rootid='0' sharpbang='#!' shsharp='true' spitshell='cat' startsh='#!/bin/sh' sysman='/usr/man/man1' d_usemt='define' d_useov='define' overviewdir='/tmp' threaddir='/tmp' trn_init='FALSE' trn_select='TRUE' nm_opt='' runnm='false' usenm='true' incpath='' mips='' mips_type='' usrinc='/usr/include' PATCHLEVEL="3.6 (20 Nov 1994)" CONFIG=true trn-3.6/rt-page.c0000664000000000000000000004231305654303434010571 0ustar /* $Id: rt-page.c,v 3.0 1992/12/14 00:14:12 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "cache.h" #include "term.h" #include "ngdata.h" #include "trn.h" #include "util.h" #include "rthread.h" #include "rt-select.h" #include "rt-util.h" #include "INTERN.h" #include "rt-page.h" extern char *display_mode; extern char sel_disp_char[]; bool set_sel_mode(ch) char_int ch; { switch (ch) { case 'a': set_selector(SM_ARTICLE, sel_artsort); break; case 's': set_selector(SM_SUBJECT, sel_threadsort); break; case 't': if (in_ng && !ThreadedGroup) { bool always_save = thread_always; ThreadedGroup = TRUE; thread_always = TRUE; if (sel_rereading) firstart = absfirst; printf("\nThreading the group. "), fflush(stdout); thread_open(); thread_always = always_save; if (last_cached < lastart) ThreadedGroup = FALSE; } /* FALL THROUGH */ case 'T': set_selector(SM_THREAD, sel_threadsort); break; default: return FALSE; } return TRUE; } bool set_sel_sort(ch) char_int ch; { if (isupper(ch)) { sel_direction = -1; ch = tolower(ch); } else sel_direction = 1; switch (ch) { case 'd': sel_sort = SS_DATE; break; case 's': sel_sort = SS_SUBJECT; break; case 'a': sel_sort = SS_AUTHOR; break; case 'c': sel_sort = SS_COUNT; break; case 'n': sel_sort = SS_NUMBER; break; case 'g': sel_sort = SS_GROUPS; break; default: return FALSE; } if (sel_mode == SM_ARTICLE) set_selector(sel_mode, sel_sort); else set_selector(sel_threadmode, sel_sort); return TRUE; } void set_selector(smode, ssort) int smode; int ssort; { sel_mode = smode; sel_sort = ssort; if (!ThreadedGroup && sel_mode == SM_THREAD) sel_mode = SM_SUBJECT; if (sel_mode == SM_ARTICLE) { if (sel_sort == SS_COUNT) sel_sort = SS_DATE; } else if (sel_sort == SS_AUTHOR || sel_sort == SS_GROUPS || sel_sort == SS_NUMBER) sel_sort = SS_DATE; switch (sel_mode) { case SM_THREAD: sel_mode_string = "threads"; sel_threadmode = smode; sel_threadsort = ssort; break; case SM_SUBJECT: sel_mode_string = "subjects"; sel_threadmode = smode; sel_threadsort = ssort; break; case SM_ARTICLE: sel_mode_string = "articles"; sel_artsort = ssort; break; } switch (sel_sort) { case SS_DATE: sel_sort_string = "date"; break; case SS_SUBJECT: sel_sort_string = "subject"; break; case SS_AUTHOR: sel_sort_string = "author"; break; case SS_COUNT: sel_sort_string = "count"; break; case SS_NUMBER: sel_sort_string = "number"; break; case SS_GROUPS: sel_sort_string = "SubjDate"; break; } } void sel_page_init() { sel_chars = getval("SELECTCHARS", SELECTCHARS); sel_max_cnt = strlen(sel_chars); if (sel_max_cnt > MAX_SEL) sel_max_cnt = MAX_SEL; if (sel_max_cnt > LINES-5) sel_max_cnt = LINES-5; sel_line = 2; sel_page_arts = 0; sel_item_cnt = 0; } void init_pages(fill_last_page) bool_int fill_last_page; { try_again: sel_prior_arts = sel_total_arts = 0; if (sel_mode == SM_ARTICLE) { ARTICLE *ap, **app, **limit; if (sel_page_app) { int desired_flags = (sel_rereading? AF_READ : 0); limit = artptr_list + artptr_list_size; ap = Nullart; for (app = sel_page_app; app < limit; app++) { ap = *app; if ((ap->flags & (AF_MISSING|AF_READ)) == desired_flags) break; } sort_articles(); if (ap == Nullart) sel_page_app = artptr_list + artptr_list_size; else { for (app = artptr_list; app < limit; app++) { if (*app == ap) { sel_page_app = app; break; } } } } else sort_articles(); while (sel_page_sp && sel_page_sp->misc == 0) sel_page_sp = sel_page_sp->next; /* The artptr_list contains only unread or read articles, never both */ limit = artptr_list + artptr_list_size; for (app = artptr_list; app < limit; app++) { ap = *app; if (sel_rereading && !(ap->flags & sel_mask)) ap->flags |= AF_DEL; if (sel_page_app == app || (!sel_page_app && ap->subj == sel_page_sp)) { sel_page_app = app; sel_prior_arts = sel_total_arts; } if (!sel_exclusive || (ap->flags & sel_mask)) { sel_total_arts++; ap->flags |= AF_INCLUDED; } else ap->flags &= ~AF_INCLUDED; } if (sel_exclusive && !sel_total_arts) { sel_exclusive = FALSE; goto try_again; } if (!sel_page_app) (void) first_page(); else if (sel_page_app >= limit) (void) last_page(); else if (sel_prior_arts && fill_last_page) { sel_page_init(); app = sel_page_app; do { if (!((*app)->flags & AF_INCLUDED)) continue; sel_page_arts++; sel_item_cnt++; } while (++app < limit && sel_item_cnt < sel_max_cnt); if (sel_prior_arts + sel_page_arts == sel_total_arts) { (void) last_page(); } } } else { SUBJECT *sp, *group_sp; int group_arts; if (sel_page_sp) { while (sel_page_sp && sel_page_sp->misc == 0) sel_page_sp = sel_page_sp->next; sort_subjects(); if (!sel_page_sp) sel_page_sp = last_subject; } else sort_subjects(); for (sp = first_subject; sp; sp = sp->next) { if (sel_rereading && !(sp->flags & sel_mask)) sp->flags |= SF_DEL; group_sp = sp; group_arts = sp->misc; if (!sel_exclusive || (sp->flags & sel_mask)) sp->flags |= SF_INCLUDED; else sp->flags &= ~SF_INCLUDED; if (sel_page_sp == group_sp) sel_prior_arts = sel_total_arts; if (sel_mode == SM_THREAD) { while (sp->next && sp->next->thread == sp->thread) { sp = sp->next; if (sp == sel_page_sp) { sel_prior_arts = sel_total_arts; sel_page_sp = group_sp; } sp->flags &= ~SF_INCLUDED; if (sp->flags & sel_mask) group_sp->flags |= SF_INCLUDED; else if (sel_rereading) sp->flags |= SF_DEL; group_arts += sp->misc; } } if (group_sp->flags & SF_INCLUDED) sel_total_arts += group_arts; } if (sel_exclusive && !sel_total_arts) { sel_exclusive = FALSE; goto try_again; } if (!sel_page_sp) (void) first_page(); else if (sel_page_sp == last_subject) (void) last_page(); else if (sel_prior_arts && fill_last_page) { int line_cnt, sel; sel_page_init(); sp = sel_page_sp; do { if (sp->flags & SF_INCLUDED) { if (sel_mode == SM_THREAD) line_cnt = count_thread_lines(sp, &sel); else line_cnt = count_subject_lines(sp, &sel); if (line_cnt) { if (line_cnt > LINES - 5) line_cnt = LINES - 5; if (sel_line + line_cnt > LINES - 3) break; sel_page_arts += sp->misc; sel_item_cnt++; } } else line_cnt = 0; if (sel_mode == SM_THREAD) { while (sp->next && sp->next->thread == sp->thread) { sp = sp->next; if (!line_cnt || !sp->misc) continue; sel_page_arts += sp->misc; } } } while ((sp=sp->next)!=Nullsubj && sel_item_cntflags & AF_INCLUDED) { if (sel_page_app != app) { sel_page_app = app; return TRUE; } break; } } } else { SUBJECT *sp; for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_INCLUDED) { if (sel_page_sp != sp) { sel_page_sp = sp; return TRUE; } break; } } } return FALSE; } bool last_page() { sel_prior_arts = sel_total_arts; if (sel_mode == SM_ARTICLE) { ARTICLE **app = sel_page_app; sel_page_app = artptr_list + artptr_list_size; if (!prev_page()) sel_page_app = app; else if (app != sel_page_app) return TRUE; } else { SUBJECT *sp = sel_page_sp; sel_page_sp = Nullsubj; if (!prev_page()) sel_page_sp = sp; else if (sp != sel_page_sp) return TRUE; } return FALSE; } bool next_page() { if (sel_mode == SM_ARTICLE) { if (sel_next_app < artptr_list + artptr_list_size) { sel_page_app = sel_next_app; sel_prior_arts += sel_page_arts; return TRUE; } } else { if (sel_next_sp) { sel_page_sp = sel_next_sp; sel_prior_arts += sel_page_arts; return TRUE; } } return FALSE; } bool prev_page() { int item_cnt = 0; /* Scan the items in reverse to go back a page */ if (sel_mode == SM_ARTICLE) { ARTICLE *ap, **app, **page_app = sel_page_app; for (app = sel_page_app; --app >= artptr_list; ) { ap = *app; if (ap->flags & AF_INCLUDED) { page_app = app; sel_prior_arts--; if (++item_cnt >= sel_max_cnt) break; } } if (sel_page_app != page_app) { sel_page_app = page_app; return TRUE; } } else { SUBJECT *sp, *page_sp = sel_page_sp; int line_cnt, item_arts, line; line = 2; for (sp = (!page_sp? last_subject : page_sp->prev); sp; sp=sp->prev) { item_arts = sp->misc; if (sel_mode == SM_THREAD) { while (sp->prev && sp->prev->thread == sp->thread) { sp = sp->prev; item_arts += sp->misc; } line_cnt = count_thread_lines(sp, NULL); } else line_cnt = count_subject_lines(sp, NULL); if (!(sp->flags & SF_INCLUDED) || !line_cnt) continue; if (line_cnt > LINES - 5) line_cnt = LINES - 5; line += line_cnt; if (line > LINES - 3) { sp = page_sp; break; } sel_prior_arts -= item_arts; page_sp = sp; if (++item_cnt >= sel_max_cnt) break; } if (sel_page_sp != page_sp) { sel_page_sp = (page_sp? page_sp : first_subject); return TRUE; } } return FALSE; } void display_page() { int sel; #ifdef CLEAREOL if (can_home_clear) { home_cursor(); maybe_eol(); } else #endif clear(); carriage_return(); #ifdef NOFIREWORKS no_sofire(); #endif standout(); fputs(ngname, stdout); un_standout(); printf(" %ld %sarticle%s", (long)sel_total_arts, sel_rereading? "read " : nullstr, article_count == 1 ? nullstr : "s"); if (sel_exclusive) printf(" out of %ld", (long)article_count); printf("%s\n", moderated); #ifdef CLEAREOL maybe_eol(); #endif if (redirected && redirected != nullstr) printf("\t** Please start using %s **", redirected); putchar('\n') FLUSH; try_again: sel_page_init(); if (!sel_total_arts) ; else if (sel_mode == SM_ARTICLE) { ARTICLE *ap, **app, **limit; limit = artptr_list + artptr_list_size; app = sel_page_app; do { ap = *app; if (ap == sel_last_ap) sel_item_index = sel_item_cnt; if (!(ap->flags & AF_INCLUDED)) continue; sel = !!(ap->flags & sel_mask) + (ap->flags & AF_DEL); sel_items[sel_item_cnt].ptr = (VOIDPTR*)ap; sel_items[sel_item_cnt].line = sel_line; sel_items[sel_item_cnt].sel = sel; sel_page_arts++; /* Output the article, with optional author */ display_article(ap, sel_chars[sel_item_cnt], sel); sel_item_cnt++; } while (++app < limit && sel_item_cnt < sel_max_cnt); if (!sel_page_arts) { if (last_page()) goto try_again; } sel_next_app = app; } else { SUBJECT *sp; int line_cnt; bool etc; char ch = ' '; sp = sel_page_sp; do { if (sp == sel_last_sp) sel_item_index = sel_item_cnt; etc = FALSE; if (sp->flags & SF_INCLUDED) { /* Compute how many lines we need to display this group */ if (sel_mode == SM_THREAD) line_cnt = count_thread_lines(sp, &sel); else line_cnt = count_subject_lines(sp, &sel); if (line_cnt) { /* If this item is too long to fit on the screen all by ** itself, trim it to fit and set the "etc" flag. */ if (line_cnt > LINES - 5) { line_cnt = LINES - 5; etc = TRUE; } /* If it doesn't fit, save it for the next page */ if (sel_line + line_cnt > LINES - 3) break; sel_items[sel_item_cnt].ptr = (VOIDPTR*)sp; sel_items[sel_item_cnt].line = sel_line; sel_items[sel_item_cnt].sel = sel; sel_page_arts += sp->misc; ch = sel_chars[sel_item_cnt]; sel = sel_items[sel_item_cnt].sel; sel_item_cnt++; if (sp->misc) { display_subject(sp, ch, sel); ch = ' '; } } } else line_cnt = 0; if (sel_mode == SM_THREAD) { while (sp->next && sp->next->thread == sp->thread) { sp = sp->next; if (!line_cnt || !sp->misc) continue; if (sel_line < LINES - 3) display_subject(sp, ch, sel); ch = ' '; sel_page_arts += sp->misc; } } if (etc) fputs(" ...etc.", stdout); } while ((sp=sp->next)!=Nullsubj && !etc && sel_item_cntflags & sel_mask) + (ap->flags & AF_DEL)) continue; } else { SUBJECT *sp = (SUBJECT*)sel_items[j].ptr; int real_sel; if (sel_mode == SM_THREAD) (void) count_thread_lines(sp, &real_sel); else (void) count_subject_lines(sp, &real_sel); if (sel == real_sel) continue; } goto_line(sel_line, sel_items[j].line); sel_line = sel_items[j].line; sel_item_index = j; output_sel(!sel); } if (++sel_item_index == sel_item_cnt) sel_item_index = 0; } void output_sel(sel) int sel; { putchar(sel_chars[sel_item_index]); putchar(sel_disp_char[sel]); sel_items[sel_item_index].sel = sel; } /* Counts the number of lines needed to output a subject, including ** optional authors. */ static int count_subject_lines(subj, selptr) SUBJECT *subj; int *selptr; { register ARTICLE *ap; register int sel; if (subj->flags & SF_DEL) sel = 2; else if (subj->flags & sel_mask) { sel = 1; for (ap = subj->articles; ap; ap = ap->subj_next) { if ((!(ap->flags&AF_READ) ^ sel_rereading) && !(ap->flags & sel_mask)) { sel = 3; break; } } } else sel = 0; if (selptr) *selptr = sel; if (*display_mode == 'l') return subj->misc; if (*display_mode == 'm') return (subj->misc <= 4? subj->misc : (subj->misc - 4) / 3 + 4); return (subj->misc != 0); } /* Counts the number of lines needed to output a thread, including ** optional authors. */ static int count_thread_lines(subj, selptr) SUBJECT *subj; int *selptr; { register int total = 0; register ARTICLE *thread = subj->thread; int sel = -1, subj_sel; do { if (subj->misc) { total += count_subject_lines(subj, &subj_sel); if (sel < 0) sel = subj_sel; else if (sel != subj_sel) sel = 3; } } while ((subj = subj->next) != Nullsubj && subj->thread == thread); if (selptr) *selptr = (sel < 0? 0 : sel); return total; } /* Display an article, perhaps with its author. */ static void display_article(ap, ch, sel) register ARTICLE *ap; char_int ch; int sel; { int subj_width = COLS - 5; int from_width = COLS / 5; #ifdef CLEAREOL maybe_eol(); #endif if (subj_width < 32) subj_width = 32; putchar(ch); putchar(sel_disp_char[sel]); if (*display_mode == 's' || from_width < 8) printf(" %s\n",compress_subj(ap->subj->articles,subj_width)) FLUSH; else { printf("%s %s\n", compress_from(ap, from_width), compress_subj(ap, subj_width - from_width)) FLUSH; } sel_line++; } /* Display the given subject group, with optional authors. */ static void display_subject(subj, ch, sel) SUBJECT *subj; char_int ch; int sel; { register ARTICLE *ap; register int j, i; int subj_width = COLS - 8; int from_width = COLS / 5; #ifdef CLEAREOL maybe_eol(); #endif if (subj_width < 32) subj_width = 32; j = subj->misc; putchar(ch); if (ch != ' ') putchar(sel_disp_char[sel]); else putchar(' '); if (*display_mode == 's' || from_width < 8) printf("%3d %s\n",j,compress_subj(subj->articles,subj_width)) FLUSH; else { /* Find the first unread article so we get the author right */ for (ap = subj->articles; ap; ap = ap->subj_next) { if (!(ap->flags&AF_READ) ^ sel_rereading) break; } printf("%s%3d %s\n", compress_from(ap, from_width), j, compress_subj(ap, subj_width - from_width)) FLUSH; i = -1; if (--j && ap) { for (ap = ap->subj_next; ap && j; ap = ap->subj_next) { if (!(!(ap->flags&AF_READ) ^ sel_rereading)) continue; j--; if (i < 0) i = 0; else if (*display_mode == 'm') { if (!j) { if (i) putchar('\n'); } else { if (i == 3 || !i) { if (i) putchar('\n'); if (++sel_line >= LINES - 3) return; #ifdef CLEAREOL maybe_eol(); #endif i = 1; } else i++; printf(" %s ", compress_from(ap, from_width)) FLUSH; continue; } } if (++sel_line >= LINES - 3) return; #ifdef CLEAREOL maybe_eol(); #endif printf(" %s\n", compress_from(ap, from_width)) FLUSH; } } } sel_line++; } trn-3.6/newsgroups.10000664000000000000000000000400305424541447011361 0ustar .\" $Id: newsgroups.1,v 4.4.3.1 1992/02/01 03:17:20 sob PATCH_3 sob $ .\" .\" This software is Copyright 1991 by Stan Barber. .\" .\" Permission is hereby granted to copy, reproduce, redistribute or otherwise .\" use this software as long as: there is no monetary profit gained .\" specifically from the use or reproduction of this software, it is not .\" sold, rented, traded or otherwise marketed, and this copyright notice is .\" included prominently in any copy made. .\" .\" The authors make no claims as to the fitness or correctness of this software .\" for any use whatsoever, and it is provided as is. Any use of this software .\" is at the user's own risk. .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH NEWSGROUPS 1 LOCAL .SH NAME newsgroups - a program to list unsubscribed newsgroups. .SH SYNOPSIS .B newsgroups pattern flag .SH DESCRIPTION The .I newsgroups program compares your .newsrc file with the file of active newsgroups, and prints a list of unsubscribed newsgroups matching pattern. If the second argument \*(L"flag\*(R" is present, only newsgroups not found in your .newsrc are listed, and the display is not paged. If the second argument is missing, the display is paged, and an additional list of unsubscribed newsgroups occurring in your .newsrc is printed. .SH ENVIRONMENT .IP DOTDIR 8 Where to find your .newsrc, if not in your home directory. .Sp Default: $HOME .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .SH FILES /usr/lib/news/active or a reasonable facsimile .br ${DOTDIR-{$HOME-$LOGDIR}}/.newsrc .SH SEE ALSO rn(1), newsrc(5) .SH DIAGNOSTICS .SH BUGS The flag argument is a kludge. trn-3.6/bits.c0000664000000000000000000004141305663312255010174 0ustar /* $Id: bits.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "cache.h" #include "INTERN.h" #include "bits.h" #include "EXTERN.h" #include "rcstuff.h" #include "head.h" #include "term.h" #include "util.h" #include "util2.h" #include "final.h" #include "trn.h" #include "ng.h" #include "artio.h" #include "intrp.h" #include "ngdata.h" #include "rcln.h" #include "ndir.h" #include "nntp.h" #include "rthread.h" #include "rt-select.h" #include "rt-util.h" #ifdef DBM_XREFS # ifdef NULL # undef NULL # endif # include #endif static long chase_count = 0; void bits_init() { ; } void rc_to_bits() { char *mybuf = buf; /* place to decode rc line */ register char *s, *c, *h; register long i; register ART_NUM unread; register ARTICLE *ap; /* modify the article flags to reflect what has already been read */ for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ; /* find numbers in rc line */ i = strlen(s); #ifndef lint if (i >= LBUFLEN-2) /* bigger than buf? */ mybuf = safemalloc((MEM_SIZE)(i+2)); #endif strcpy(mybuf,s); /* make scratch copy of line */ mybuf[i++] = ','; /* put extra comma on the end */ mybuf[i] = '\0'; s = mybuf; /* initialize the for loop below */ if (strnEQ(s,"1-",2)) { /* can we save some time here? */ firstart = atol(s+2)+1; /* process first range thusly */ if (firstart < absfirst) firstart = absfirst; s=index(s,',') + 1; for (i = absfirst, ap = article_ptr(i); i < firstart; i++, ap++) ap->flags |= AF_READ; } else { i = firstart = absfirst; ap = article_ptr(i); } unread = lastart - firstart + 1; /* assume this range unread */ #ifdef DEBUG if (debug & DEB_CTLAREA_BITMAP) { printf("\n%s\n",mybuf) FLUSH; for (i = absfirst, ap = article_ptr(i); i < firstart; i++, ap++) if (!(ap->flags & AF_READ)) printf("%ld ",(long)i) FLUSH; } #endif for ( ; (c = index(s,',')) != Nullch; s = ++c) { /* for each range */ ART_NUM min, max; *c = '\0'; /* do not let index see past comma */ h = index(s,'-'); min = atol(s); if (h) /* is there a dash? */ max = atol(h+1); else max = min; if (min < firstart) /* make sure range is in range */ min = firstart; if (min > lastart) min = lastart+1; for (; i < min; i++, ap++) ap->flags &= ~AF_READ; if (max > lastart) max = lastart; if (min <= max) { /* non-null range? */ unread -= max - min + 1;/* adjust unread count */ /* mark all arts in range as read */ for (; i <= max; i++, ap++) ap->flags |= AF_READ; } #ifdef DEBUG if (debug & DEB_CTLAREA_BITMAP) { printf("\n%s\n",s) FLUSH; for (i=absfirst; i <= lastart; i++) if (!was_read(i)) printf("%ld ",(long)i) FLUSH; } #endif } for (; i <= lastart; i++, ap++) ap->flags &= ~AF_READ; #ifdef DEBUG if (debug & DEB_CTLAREA_BITMAP) { fputs("\n(hit CR)",stdout) FLUSH; fgets(cmd_buf, sizeof cmd_buf, stdin); } #endif if (mybuf != buf) free(mybuf); toread[ng] = unread; } /* reconstruct the .newsrc line in a human readable form */ void bits_to_rc() { register char *s, *mybuf = buf; register ART_NUM i; ART_NUM count=0; int safelen = LBUFLEN - 32; strcpy(buf,rcline[ng]); /* start with the newsgroup name */ s = buf + rcnums[ng] - 1; /* use s for buffer pointer */ *s++ = RCCHAR(rcchar[ng]); /* put the requisite : or !*/ for (i=absfirst; i<=lastart; i++) if (!was_read(i)) break; sprintf(s," 1-%ld,",(long)i-1); s += strlen(s); for (; i<=lastart; i++) { /* for each article in newsgroup */ if (s-mybuf > safelen) { /* running out of room? */ safelen *= 2; if (mybuf == buf) { /* currently static? */ *s = '\0'; mybuf = safemalloc((MEM_SIZE)safelen + 32); strcpy(mybuf,buf); /* so we must copy it */ s = mybuf + (s-buf); /* fix the pointer, too */ } else { /* just grow in place, if possible */ char *newbuf; newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 32); s = newbuf + (s-mybuf); mybuf = newbuf; } } if (!was_read(i)) /* still unread? */ count++; /* then count it */ else { /* article was read */ ART_NUM oldi; sprintf(s,"%ld",(long)i); /* put out the min of the range */ s += strlen(s); /* keeping house */ oldi = i; /* remember this spot */ do i++; while (i <= lastart && was_read(i)); /* find 1st unread article or end */ i--; /* backup to last read article */ if (i > oldi) { /* range of more than 1? */ sprintf(s,"-%ld,",(long)i); /* then it out as a range */ s += strlen(s); /* and housekeep */ } else *s++ = ','; /* otherwise, just a comma will do */ } } if (*(s-1) == ',') /* is there a final ','? */ s--; /* take it back */ *s++ = '\0'; /* and terminate string */ #ifdef DEBUG if (debug & DEB_NEWSRC_LINE && !panic) { printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]) FLUSH; printf("%s\n",mybuf) FLUSH; } #endif free(rcline[ng]); /* return old rc line */ if (mybuf == buf) { rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1); /* grab a new rc line */ strcpy(rcline[ng], buf); /* and load it */ } else { mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1); /* be nice to the heap */ rcline[ng] = mybuf; } *(rcline[ng] + rcnums[ng] - 1) = '\0'; if (rcchar[ng] == NEGCHAR) { /* did they unsubscribe? */ printf(unsubto,ngname) FLUSH; toread[ng] = TR_UNSUB; /* make line invisible */ } else /*NOSTRICT*/ toread[ng] = (ART_UNREAD)count; /* remember how many unread there are */ } #ifdef USE_NNTP /* Parse the LISTGROUP output and set anything not mentioned as missing. */ void setmissingbits() /* NNTP version */ { register ART_NUM num, priornum; register ARTICLE *ap; if (!nntp_listgroup()) return; for (priornum = absfirst-1, ap = article_ptr(absfirst);; ap++) { nntp_gets(ser_line, sizeof ser_line); if (NNTP_LIST_END(ser_line)) break; num = atol(ser_line); while (++priornum < num) uncache_article(ap++,FALSE); } } #else /* !USE_NNTP */ /* Scan the directory to find which articles are present. */ void setfoundbits() { register ART_NUM first = lastart+1; register DIR *dirp; register Direntry_t *dp; long an; char ch; if (!(dirp = opendir("."))) return; found_min = absfirst; an = (lastart-found_min)/BITSPERBYTE+20; found_bits = safemalloc((MEM_SIZE)an); bzero(found_bits, an); while ((dp = readdir(dirp)) != Null(Direntry_t*)) { if (sscanf(dp->d_name, "%ld%c", &an, &ch) == 1) { if (an <= lastart && an >= found_min) { if (an < first) first = an; foundart(an); } } } closedir(dirp); abs1st[ng] = first; if (first > absfirst) checkexpired(ng,first); absfirst = first; } void setmissingbits() /* non-NNTP version */ { register ARTICLE *ap; register ART_NUM an; if (!found_bits) return; for (an = absfirst, ap = article_ptr(an); an <= lastart; an++, ap++) { if (artismissing(an)) onemissing(ap); } free(found_bits); found_bits = NULL; } #endif /* !USE_NNTP */ /* mark an article unread, keeping track of toread[] */ void onemore(ap) ARTICLE *ap; { if (ap->flags & AF_READ) { register ART_NUM artnum = article_num(ap); check_first(artnum); ap->flags &= ~AF_READ; ++toread[ng]; ap->flags &= ~AF_DEL; if (ap->subj) { if (selected_only) { if (ap->subj->flags & sel_mask) { ap->flags |= sel_mask; selected_count++; } } else ap->subj->flags |= SF_VISIT; } } } /* mark an article read, keeping track of toread[] */ void oneless(ap) ARTICLE *ap; { if (!(ap->flags & AF_READ)) { ap->flags |= AF_READ; /* Keep selected_count accurate */ if (ap->flags & sel_mask) { selected_count--; ap->flags &= ~sel_mask; } if (toread[ng] > TR_NONE) --toread[ng]; } } void onemissing(ap) ARTICLE *ap; { oneless(ap); ap->flags = (ap->flags & ~(AF_HAS_RE|AF_YANKBACK|AF_FROMTRUNCED)) | AF_MISSING|AF_CACHED|AF_THREADED; } /* mark an article as unread, with possible xref chasing */ void unmark_as_read() { register ARTICLE *ap = article_ptr(art); onemore(ap); #ifdef MCHASE if (ap->xrefs != nullstr && !(ap->flags & AF_MCHASE)) { ap->flags |= AF_MCHASE; chase_count++; } #endif } /* Mark an article as read in this newsgroup and possibly chase xrefs. ** Don't call this on missing articles. */ void set_read(ap) register ARTICLE *ap; { oneless(ap); if (!olden_days && ap->xrefs != nullstr && !(ap->flags & AF_KCHASE)) { ap->flags |= AF_KCHASE; chase_count++; } } /* temporarily mark article as read. When newsgroup is exited, articles */ /* will be marked as unread. Called via M command */ void delay_unmark(ap) ARTICLE *ap; { if (!(ap->flags & AF_YANKBACK)) { ap->flags |= AF_YANKBACK; dmcount++; } } /* mark article as read. If article is cross referenced to other */ /* newsgroups, mark them read there also. */ void mark_as_read() { register ARTICLE *ap = article_ptr(art); oneless(ap); if (ap->xrefs != nullstr && !(ap->flags & AF_KCHASE)) { ap->flags |= AF_KCHASE; chase_count++; } checkcount++; /* get more worried about crashes */ } /* keep firstart pointing at the first unread article */ void check_first(min) ART_NUM min; { if (min < absfirst) min = absfirst; if (min < firstart) firstart = min; } /* bring back articles marked with M */ void yankback() { register ARTICLE *ap; if (dmcount) { /* delayed unmarks pending? */ if (mode == 't') sprintf(buf, "Returned %ld Marked article%s.",(long)dmcount, dmcount == 1 ? nullstr : "s"); else #ifdef VERBOSE printf("\nReturning %ld Marked article%s...\n",(long)dmcount, dmcount == 1 ? nullstr : "s") FLUSH; #endif for (art=absfirst, ap=article_ptr(art); art <= lastart; art++, ap++) { if ((ap->flags & (AF_YANKBACK|AF_MISSING)) == AF_YANKBACK) { unmark_as_read(); if (selected_only) select_article(ap, 0); ap->flags &= ~AF_YANKBACK; } } dmcount = 0; } } static int chase_xref _((ART_NUM,int)); int chase_xrefs(until_key) bool_int until_key; { register ARTICLE *ap; register ART_NUM an; if (!chase_count) return 1; if (until_key) setspin(SPIN_BACKGROUND); for (an = absfirst, ap = article_ptr(an); an <= lastart; an++, ap++) { if (ap->flags & AF_KCHASE) { chase_xref(an,TRUE); ap->flags &= ~AF_KCHASE; if (!--chase_count) break; } #ifdef MCHASE if (ap->flags & AF_MCHASE) { chase_xref(an,TRUE); ap->flags &= ~AF_MCHASE; if (!--chase_count) break; } #endif if (until_key && input_pending()) return 0; } chase_count = 0; return 1; } /* run down xref list and mark as read or unread */ #ifndef DBM_XREFS static int chase_xref(artnum,markread) /* The Xref-line-using version */ ART_NUM artnum; int markread; { # ifdef VALIDATE_XREF_SITE bool valid_xref_site(); # endif register char *xartnum; register ART_NUM x; char *xref_buf, *curxref; char tmpbuf[128]; if (inbackground()) spin(10); else { if (output_chase_phrase) { #ifdef VERBOSE IF(verbose) fputs("\nChasing xrefs", stdout); ELSE #endif #ifdef TERSE fputs("\nXrefs", stdout); #endif output_chase_phrase = 0; } putchar('.'), fflush(stdout); } xref_buf = fetchcache(artnum, XREF_LINE, FILL_CACHE); if (!xref_buf || !*xref_buf) return 0; xref_buf = savestr(xref_buf); # ifdef DEBUG if (debug & DEB_XREF_MARKER) printf("Xref: %s\n",xref_buf) FLUSH; # endif curxref = cpytill(tmpbuf,xref_buf,' ') + 1; # ifdef VALIDATE_XREF_SITE if (valid_xref_site(artnum,tmpbuf)) # endif { while (*curxref) { /* for each newsgroup */ curxref = cpytill(tmpbuf,curxref,' '); xartnum = index(tmpbuf,':'); if (!xartnum) break; *xartnum++ = '\0'; if (!(x = atol(xartnum))) continue; if (strEQ(tmpbuf,ngname)) {/* is this the current newsgroup? */ if (x < absfirst || x > lastart) continue; if (markread) oneless(article_ptr(x)); /* take care of old C newses */ #ifdef MCHASE else onemore(article_ptr(x)); #endif } else { if (markread) { if (addartnum(x,tmpbuf)) break; } # ifdef MCHASE else subartnum(x,tmpbuf); # endif } while (*curxref && isspace(*curxref)) curxref++; } } free(xref_buf); return 0; } # ifdef VALIDATE_XREF_SITE /* Make sure the site name on Xref matches what inews thinks the site * is. Check first against last inews_site. If it matches, fine. * If not, fetch inews_site from current Path or Relay-Version line and * check again. This is so that if the new administrator decides * to change the system name as known to inews, rn will still do * Xrefs correctly--each article need only match itself to be valid. */ bool valid_xref_site(artnum, site) ART_NUM artnum; char *site; { static char *inews_site = Nullch; char *sitebuf, *s; if (inews_site && strEQ(site,inews_site)) return TRUE; if (inews_site) free(inews_site); #ifndef ANCIENT_NEWS /* Grab the site from the first component of the Path line */ sitebuf = fetchlines(artnum,PATH_LINE); if ((s = index(sitebuf, '!')) != Nullch) { *s = '\0'; inews_site = savestr(sitebuf); } #else /* ANCIENT_NEWS */ /* Grab the site from the Posting-Version line */ sitebuf = fetchlines(artnum,RVER_LINE); if ((s = instr(sitebuf,"; site ",TRUE)) != Nullch) { char *t = index(s+7, '.'); if (t) *t = '\0'; inews_site = savestr(s+7); } #endif /* ANCIENT_NEWS */ else inews_site = savestr(nullstr); free(sitebuf); if (strEQ(site,inews_site)) return TRUE; #ifdef DEBUG if (debug) printf("Xref not from %s--ignoring\n",inews_site) FLUSH; #endif return FALSE; } # endif /* VALIDATE_XREF_SITE */ #else /* DBM_XREFS */ static int chase_xref(artnum,markread) /* The DBM version */ ART_NUM artnum; int markread; { datum lhs, rhs; datum fetch(); register char *idp; char *ident_buf; static FILE *hist_file = Nullfp; long pos; register char *xartnum; register ART_NUM x; char *xref_buf, *curxref; char tmpbuf[128]; if (inbackground()) spin(10); else { if (output_chase_phrase) { #ifdef VERBOSE IF(verbose) fputs("\nChasing xrefs", stdout); ELSE #endif #ifdef TERSE fputs("\nXrefs", stdout); #endif output_chase_phrase = 0; } putchar('.'), fflush(stdout); } xref_buf = fetchcache(artnum, NGS_LINE, FILL_CACHE); if (!xref_buf || !*xref_buf) return 0; xref_buf = safemalloc((MEM_SIZE)BUFSIZ); if (hist_file == Nullfp) { /* Init. file accesses */ #ifdef DEBUG if (debug) printf("chase_xref: opening files\n"); #endif dbminit(filexp(ARTFILE)); if ((hist_file = fopen(filexp(ARTFILE), "r")) == Nullfp) return 0; } ident_buf = fetchlines(artnum,MESSID_LINE); /* get Message-ID */ #ifdef DEBUG if (debug) printf ("chase_xref: Message-ID: %s\n", ident_buf); #endif if ((idp = index(ident_buf, '@')) != Nullch) { while (*++idp) /* make message-id case insensitive */ if (isupper(*idp)) *idp = tolower(*idp); } lhs.dptr = ident_buf; /* look up article by id */ lhs.dsize = strlen(lhs.dptr) + 1; rhs = fetch(lhs); /* fetch the record */ if (rhs.dptr == NULL) /* if null, nothing there */ goto wild_goose; bcopy(rhs.dptr,(char*)&pos, 4); fseek(hist_file, pos, 0); /* datum returned is position in hist file */ fgets(xref_buf, BUFSIZ, hist_file); #ifdef DEBUG if (debug) printf ("Xref from history: %s\n", xref_buf); #endif curxref = cpytill(tmpbuf, xref_buf, '\t') + 1; curxref = cpytill(tmpbuf, curxref, '\t') + 1; #ifdef DEBUG if (debug) printf ("chase_xref: curxref: %s\n", curxref); #endif while (*curxref) { /* for each newsgroup */ curxref = cpytill(tmpbuf,curxref,' '); xartnum = index(tmpbuf,'/'); if (!xartnum) /* probably an old-style Xref */ break; *xartnum++ = '\0'; if (!(x = atol(xartnum))) continue; if (strNE(tmpbuf,ngname)) { /* not the current newsgroup? */ if (markread) { if (addartnum(x,tmpbuf)) goto wild_goose; } #ifdef MCHASE else subartnum(x,tmpbuf); #endif } while (*curxref && isspace(*curxref)) curxref++; } wild_goose: free(xref_buf); free(ident_buf); return 0; } #endif /* DBM_XREFS */ trn-3.6/support/0000775000000000000000000000000005663312304010573 5ustar trn-3.6/support/trnkill0000664000000000000000000000130305663312265012200 0ustar #!/bin/sh # trnkill - shell script to apply trn KILL files in the background # 14 Mar 89 created for rn by Jim Olsen # 10 Sep 93 modified for trn 3 (or 2) by Wayne Davison # Options: -d debug mode -- you see all gory action as it happens. # Visit all newsgroups (if trn asks about anything else, just say no) export TRNINIT TRNMACRO RNMACRO TERM TRNINIT='-q -s -T -t -x +X' TRNMACRO=/tmp/trnkill$$ TERM=dumb # support for trn 2.x RNMACRO=$TRNMACRO trap 'rm -f $TRNMACRO; exit' 1 2 3 15 cat >$TRNMACRO <<'EOF' z %(%m=[nf]?.q^J:n)^(z^) ^m ^(z^) ^j ^(z^) EOF if test X$1 = X-d; then echo "z" | trn else echo "z" | trn >/dev/null 2>&1 fi rm -f $TRNMACRO exit 0 trn-3.6/newsetup.10000664000000000000000000000402305433756741011026 0ustar .\" $Id: newsetup.1,v 4.4.3.1 1992/02/01 03:17:20 sob PATCH_3 sob $ .\" .\" This software is Copyright 1991 by Stan Barber. .\" .\" Permission is hereby granted to copy, reproduce, redistribute or otherwise .\" use this software as long as: there is no monetary profit gained .\" specifically from the use or reproduction of this software, it is not .\" sold, rented, traded or otherwise marketed, and this copyright notice is .\" included prominently in any copy made. .\" .\" The authors make no claims as to the fitness or correctness of this software .\" for any use whatsoever, and it is provided as is. Any use of this software .\" is at the user's own risk. .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH NEWSETUP 1 LOCAL .SH NAME newsetup - a program to set up a .newsrc file .SH SYNOPSIS .B newsetup .SH DESCRIPTION The .I newsetup program creates a new .newsrc file containing an list of recommended newsgroups for the first time user. If your news admin has specified such a list (usually in the file NEWSLIB/subscriptions) trn will use that as a default for your .newsrc. Otherwise you get a ridiculously small list that you'll want to add to using the \*(L"a pattern\*(R" command in trn (such as \*(L"a linux\*(R"). If you already have a .newsrc, it will be backed up with the name \*(L".newsrc-old\*(R". .SH ENVIRONMENT .IP DOTDIR 8 Where to put your .newsrc, if not in your home directory. .Sp Default: $HOME .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .SH FILES /usr/lib/news/subscriptions or a reasonable facsimile .br ${DOTDIR-{$HOME-$LOGDIR}}/.newsrc .SH SEE ALSO trn(1), rn(1), newsrc(5) .SH DIAGNOSTICS .SH BUGS trn-3.6/ndir.h0000664000000000000000000000367005545455743010210 0ustar /* $Id: ndir.h,v 3.0 1991/09/09 20:23:31 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifdef I_NDIR #include #else #ifdef I_DIRENT #include #else #ifdef I_SYS_NDIR #include #else #ifdef I_SYS_DIR #include #else #ifndef DEV_BSIZE #define DEV_BSIZE 512 #endif #define DIRBLKSIZ DEV_BSIZE #define MAXNAMLEN 255 Direntry_t { long d_ino; /* inode number of entry */ short d_reclen; /* length of this record */ short d_namlen; /* length of string in d_name */ char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */ }; /* * The DIRSIZ macro gives the minimum record length which will hold * the directory entry. This requires the amount of space in Direntry_t * without the d_name field, plus enough space for the name with a terminating * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. */ #undef DIRSIZ #define DIRSIZ(dp) ((sizeof(Direntry_t)-(MAXNAMLEN+1))+(((dp)->d_namlen+1+3)&~3)) /* * Definitions for library routines operating on directories. */ typedef struct _dirdesc { int dd_fd; long dd_loc; long dd_size; char dd_buf[DIRBLKSIZ]; } DIR; #ifndef NULL #define NULL 0 #endif extern DIR *opendir _((char*)); extern Direntry_t *readdir _((DIR*)); extern long telldir _((DIR*)); extern void seekdir _((DIR*)); #define rewinddir(dirp) seekdir((dirp), (long)0) extern void closedir _((DIR*)); #endif #endif #endif #endif trn-3.6/rt-page.h0000664000000000000000000000161505556612416010602 0ustar /* $Id: rt-page.h,v 3.0 1992/12/14 00:14:12 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #define PRESERVE_PAGE 0 #define FILL_LAST_PAGE 1 bool set_sel_mode _((char_int)); bool set_sel_sort _((char_int)); void set_selector _((int,int)); void init_pages _((bool_int)); bool first_page _((void)); bool last_page _((void)); bool next_page _((void)); bool prev_page _((void)); void display_page _((void)); void update_page _((void)); void output_sel _((int)); /* Stuff internal to rt-select.c */ #ifdef DOINIT static int count_subject_lines _((SUBJECT*, int*)); static int count_thread_lines _((SUBJECT*, int*)); static void display_article _((ARTICLE*, char_int, int)); static void display_subject _((SUBJECT*, char_int, int)); #endif trn-3.6/rt-wumpus.c0000664000000000000000000003211205420174120011176 0ustar /* $Id: rt-wumpus.c,v 3.0 1992/12/14 00:14:00 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "cache.h" #include "ng.h" #include "head.h" #include "util.h" #include "term.h" #include "final.h" #include "ngdata.h" #include "artio.h" #include "backpage.h" #include "rthread.h" #include "rt-select.h" #include "INTERN.h" #include "rt-wumpus.h" static char tree_indent[] = { ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0 }; char letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+"; static ARTICLE *tree_article; static int max_depth, max_line = -1; static int first_depth, first_line; static int my_depth, my_line; static bool node_on_line; static int node_line_cnt; static int line_num; static int header_indent; static char *tree_lines[11]; static char tree_buff[128], *str; /* Prepare tree display for inclusion in the article header. */ void init_tree() { ARTICLE *thread; SUBJECT *sp; int num; while (max_line >= 0) /* free any previous tree data */ free(tree_lines[max_line--]); if (!(tree_article = curr_artp) || !tree_article->subj) return; if (!(thread = tree_article->subj->thread)) return; /* Enumerate our subjects for display */ sp = thread->subj; num = 0; do { sp->misc = num++; sp = sp->thread_link; } while (sp != thread->subj); max_depth = max_line = my_depth = my_line = node_line_cnt = 0; find_depth(thread, 0); if (max_depth <= 5) { first_depth = 0; } else { if (my_depth+2 > max_depth) { first_depth = max_depth - 5; } else if ((first_depth = my_depth - 3) < 0) { first_depth = 0; } max_depth = first_depth + 5; } if (--max_line < max_tree_lines) { first_line = 0; } else { if (my_line + max_tree_lines/2 > max_line) { first_line = max_line - (max_tree_lines-1); } else if ((first_line = my_line - (max_tree_lines-1)/2) < 0) { first_line = 0; } max_line = first_line + max_tree_lines-1; } str = tree_buff; /* initialize first line's data */ *str++ = ' '; node_on_line = FALSE; line_num = 0; /* cache our portion of the tree */ cache_tree(thread, 0, tree_indent); max_depth = (max_depth-first_depth) * 5; /* turn depth into char width */ max_line -= first_line; /* turn max_line into count */ /* shorten tree if lower lines aren't visible */ if (node_line_cnt < max_line) { max_line = node_line_cnt + 1; } } /* A recursive routine to find the maximum tree extents and where we are. */ static void find_depth(article, depth) ARTICLE *article; int depth; { if (depth > max_depth) { max_depth = depth; } for (;;) { if (article == tree_article) { my_depth = depth; my_line = max_line; } if (article->child1) { find_depth(article->child1, depth+1); } else { max_line++; } if (!(article = article->sibling)) { break; } } } /* Place the tree display in a maximum of 11 lines x 6 nodes. */ static void cache_tree(ap, depth, cp) ARTICLE *ap; int depth; char *cp; { int depth_mode; cp[1] = ' '; if (depth >= first_depth && depth <= max_depth) { cp += 5; depth_mode = 1; } else if (depth+1 == first_depth) { depth_mode = 2; } else { cp = tree_indent; depth_mode = 0; } for (;;) { switch (depth_mode) { case 1: { char ch; *str++ = ((ap->flags & AF_HAS_RE) || ap->parent) ? '-' : ' '; if (ap == tree_article) *str++ = '*'; if (ap->flags & AF_READ) { *str++ = '('; ch = ')'; } else if (!selected_only || (ap->flags & AF_SEL)) { *str++ = '['; ch = ']'; } else { *str++ = '<'; ch = '>'; } if (ap == recent_artp && ap != tree_article) *str++ = '@'; *str++ = letter(ap); *str++ = ch; if (ap->child1) { *str++ = (ap->child1->sibling? '+' : '-'); } if (ap->sibling) *cp = '|'; else *cp = ' '; node_on_line = TRUE; break; } case 2: *tree_buff = (!ap->child1)? ' ' : (ap->child1->sibling)? '+' : '-'; break; default: break; } if (ap->child1) { cache_tree(ap->child1, depth+1, cp); cp[1] = '\0'; } else { if (!node_on_line && first_line == line_num) { first_line++; } if (line_num >= first_line) { if (str[-1] == ' ') { str--; } *str = '\0'; tree_lines[line_num-first_line] = safemalloc(str-tree_buff + 1); strcpy(tree_lines[line_num - first_line], tree_buff); if (node_on_line) { node_line_cnt = line_num - first_line; } } line_num++; node_on_line = FALSE; } if (!(ap = ap->sibling) || line_num > max_line) break; if (!ap->sibling) *cp = '\\'; if (!first_depth) tree_indent[5] = ' '; strcpy(tree_buff, tree_indent+5); str = tree_buff + strlen(tree_buff); } } /* Output a header line with possible tree display on the right hand side. ** Does automatic wrapping of lines that are too long. */ int tree_puts(orig_line, header_line, use_underline) char *orig_line; ART_LINE header_line; int use_underline; { char *buf; register char *line, *cp, *end; int pad_cnt, wrap_at; ART_LINE start_line = header_line; int i; char ch; /* Make a modifiable copy of the line */ buf = safemalloc(strlen(orig_line) + 2); /* yes, I mean "2" */ strcpy(buf, orig_line); line = buf; /* Change any embedded control characters to spaces */ for (end = line; *end && *end != '\n'; end++) { if ((unsigned char)*end < ' ') { *end = ' '; } } *end = '\0'; if (!*line) { strcpy(line, " "); end = line+1; } /* If this is the first subject line, output it with a preceeding [1] */ if (ThreadedGroup && use_underline && (unsigned char)*line > ' ') { #ifdef NOFIREWORKS no_sofire(); #endif standout(); putchar('['); putchar(letter(curr_artp)); putchar(']'); un_standout(); putchar(' '); header_indent = 4; line += 9; i = 0; } else { if (*line != ' ') { /* A "normal" header line -- output keyword and set header_indent ** _except_ for the first line, which is a non-standard header. */ if (!header_line || !(cp = index(line, ':')) || *++cp != ' ') { header_indent = 0; } else { *cp = '\0'; fputs(line, stdout); putchar(' '); header_indent = ++cp - line; line = cp; if (!*line) { *--line = ' '; } } i = 0; } else { /* Skip whitespace of continuation lines and prepare to indent */ while (*++line == ' ') { ; } i = header_indent; } } for ( ; *line; i = header_indent) { #ifdef CLEAREOL maybe_eol(); #endif if (i) { putchar('+'); while (--i) { putchar(' '); } } /* If no (more) tree lines, wrap at COLS-1 */ if (max_line < 0 || header_line > max_line+1) { wrap_at = COLS-1; } else { wrap_at = COLS - max_depth - 5 - 3; } /* Figure padding between header and tree output, wrapping long lines */ pad_cnt = wrap_at - (end - line + header_indent); if (pad_cnt <= 0) { cp = line + wrap_at - header_indent - 1; pad_cnt = 1; while (cp > line && *cp != ' ') { if (*--cp == ',' || *cp == '.' || *cp == '-' || *cp == '!') { cp++; break; } pad_cnt++; } if (cp == line) { cp += wrap_at - header_indent; pad_cnt = 0; } ch = *cp; *cp = '\0'; /* keep rn's backpager happy */ vwtary(artline, vrdary(artline - 1)); artline++; } else { cp = end; ch = '\0'; } if (use_underline) { underprint(line); } else { fputs(line, stdout); } *cp = ch; /* Skip whitespace in wrapped line */ while (*cp == ' ') { cp++; } line = cp; /* Check if we've got any tree lines to output */ if (wrap_at != COLS-1 && header_line <= max_line) { char *cp1, *cp2; do { putchar(' '); } while (pad_cnt--); /* Check string for the '*' flagging our current node ** and the '@' flagging our prior node. */ cp = tree_lines[header_line]; cp1 = index(cp, '*'); cp2 = index(cp, '@'); if (cp1 != Nullch) { *cp1 = '\0'; } if (cp2 != Nullch) { *cp2 = '\0'; } fputs(cp, stdout); /* Handle standout output for '*' and '@' marked nodes, then ** continue with the rest of the line. */ while (cp1 || cp2) { standout(); if (cp1 && (!cp2 || cp1 < cp2)) { cp = cp1; cp1 = Nullch; *cp++ = '*'; putchar(*cp++); putchar(*cp++); } else { cp = cp2; cp2 = Nullch; *cp++ = '@'; } putchar(*cp++); un_standout(); if (*cp) { fputs(cp, stdout); } }/* while */ }/* if */ putchar('\n') FLUSH; header_line++; }/* for remainder of line */ /* free allocated copy of line */ free(buf); /* return number of lines displayed */ return header_line - start_line; } /* Output any parts of the tree that are left to display. Called at the ** end of each header. */ int finish_tree(last_line) ART_LINE last_line; { ART_LINE start_line = last_line; while (last_line <= max_line) { artline++; last_line += tree_puts("+", last_line, 0); vwtary(artline, artpos); /* keep rn's backpager happy */ } return last_line - start_line; } /* Output the entire article tree for the user. */ void entire_tree(ap) ARTICLE *ap; { ARTICLE *thread; SUBJECT *sp; int num; if (!ap) { #ifdef VERBOSE IF (verbose) fputs("\nNo article tree to display.\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNo tree.\n", stdout) FLUSH; #endif return; } if (!ThreadedGroup) { ThreadedGroup = TRUE; printf("Threading the group. "), fflush(stdout); thread_open(); if (!ThreadedGroup) { printf("*failed*\n") FLUSH; return; } count_subjects(CS_NORM); putchar('\n') FLUSH; } if (!(ap->flags & AF_THREADED)) parseheader(article_num(ap)); if (check_page_line()) return; putchar('\n'); thread = ap->subj->thread; /* Enumerate our subjects for display */ sp = thread->subj; num = 0; do { if (check_page_line()) return; printf("[%c] %s\n",letters[num>9+26+26? 9+26+26:num],sp->str+4) FLUSH; sp->misc = num++; sp = sp->thread_link; } while (sp != thread->subj); if (check_page_line()) return; putchar('\n'); if (check_page_line()) return; putchar(' '); buf[3] = '\0'; display_tree(thread, tree_indent); if (check_page_line()) return; putchar('\n'); } /* A recursive routine to output the entire article tree. */ static void display_tree(article, cp) ARTICLE *article; char *cp; { if (cp - tree_indent > COLS || page_line < 0) return; cp[1] = ' '; cp += 5; for (;;) { putchar(((article->flags&AF_HAS_RE) || article->parent) ? '-' : ' '); if (article->flags & AF_READ) { buf[0] = '('; buf[2] = ')'; } else if (!selected_only || (article->flags & AF_SEL)) { buf[0] = '['; buf[2] = ']'; } else { buf[0] = '<'; buf[2] = '>'; } buf[1] = letter(article); if (article == curr_artp) { standout(); fputs(buf, stdout); un_standout(); } else if (article == recent_artp) { putchar(buf[0]); standout(); putchar(buf[1]); un_standout(); putchar(buf[2]); } else { fputs(buf, stdout); } if (article->sibling) { *cp = '|'; } else { *cp = ' '; } if (article->child1) { putchar((article->child1->sibling)? '+' : '-'); display_tree(article->child1, cp); cp[1] = '\0'; } else { putchar('\n') FLUSH; } if (!(article = article->sibling)) { break; } if (!article->sibling) { *cp = '\\'; } tree_indent[5] = ' '; if (check_page_line()) { return; } fputs(tree_indent+5, stdout); } } static int check_page_line() { if (page_line < 0) return -1; if (page_line >= LINES || int_count) { register int cmd = -1; if (int_count || (cmd = get_anything())) { page_line = -1; /* disable further printing */ if (cmd > 0) pushchar(cmd); return cmd; } } page_line++; return 0; } /* Calculate the subject letter representation. "Place-holder" nodes ** are marked with a ' ', others get a letter in the sequence: ** ' ', '1'-'9', 'A'-'Z', 'a'-'z', '+' */ static char letter(ap) register ARTICLE *ap; { int subj = ap->subj->misc; if (!(ap->flags & AF_CACHED) && (absfirst < first_cached || last_cached < lastart || !cached_all_in_range)) return '?'; if (ap->flags & AF_MISSING) return ' '; return letters[subj > 9+26+26 ? 9+26+26 : subj]; } trn-3.6/norm.saver.SH0000664000000000000000000000242005321027340011375 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting norm.saver (with variable substitutions)" $spitshell >norm.saver <> \$7 !GROK!THIS! $eunicefix norm.saver chmod 755 norm.saver trn-3.6/backpage.c0000664000000000000000000000475605420174060010770 0ustar /* $Id: backpage.c,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "intrp.h" #include "final.h" #include "INTERN.h" #include "backpage.h" ART_LINE maxindx = -1; void backpage_init() { char *varyname; varyname = filexp(VARYNAME); close(creat(varyname,0600)); varyfd = open(varyname,2); UNLINK(varyname); if (varyfd < 0) { printf(cantopen,varyname) FLUSH; sig_catcher(0); } } /* virtual array read */ ART_POS vrdary(indx) ART_LINE indx; { int subindx; long offset; #ifdef DEBUG if (indx > maxindx) { printf("vrdary(%ld) > %ld\n",(long)indx, (long)maxindx) FLUSH; return 0; } #endif if (indx < 0) return 0; subindx = indx % VARYSIZE; offset = (indx - subindx) * sizeof(varybuf[0]); if (offset != oldoffset) { if (oldoffset >= 0) { #ifndef lint (void)lseek(varyfd,oldoffset,0); write(varyfd, (char *)varybuf,sizeof(varybuf)); #endif /* lint */ } #ifndef lint (void)lseek(varyfd,offset,0); read(varyfd,(char *)varybuf,sizeof(varybuf)); #endif /* lint */ oldoffset = offset; } return varybuf[subindx]; } /* write to virtual array */ void vwtary(indx,newvalue) ART_LINE indx; ART_POS newvalue; { int subindx; long offset; #ifdef DEBUG if (indx < 0) printf("vwtary(%ld)\n",(long)indx) FLUSH; if (!indx) maxindx = 0; if (indx > maxindx) { if (indx != maxindx + 1) printf("indx skipped %d-%d\n",maxindx+1,indx-1) FLUSH; maxindx = indx; } #endif subindx = indx % VARYSIZE; offset = (indx - subindx) * sizeof(varybuf[0]); if (offset != oldoffset) { if (oldoffset >= 0) { #ifndef lint (void)lseek(varyfd,oldoffset,0); write(varyfd,(char *)varybuf,sizeof(varybuf)); #endif /* lint */ } #ifndef lint (void)lseek(varyfd,offset,0); read(varyfd,(char *)varybuf,sizeof(varybuf)); #endif /* lint */ oldoffset = offset; } varybuf[subindx] = newvalue; } trn-3.6/rthread.c0000664000000000000000000010171605660373067010674 0ustar /* $Id: rthread.c,v 3.0 1992/12/14 00:14:13 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "intrp.h" #include "trn.h" #include "cache.h" #include "bits.h" #include "ng.h" #include "rcln.h" #include "search.h" #include "artstate.h" #include "rcstuff.h" #include "ngdata.h" #include "final.h" #include "kfile.h" #include "head.h" #include "util.h" #include "hash.h" #include "nntp.h" #include "rt-mt.h" #include "rt-ov.h" #include "rt-page.h" #include "rt-process.h" #include "rt-select.h" #include "rt-util.h" #include "rt-wumpus.h" #include "INTERN.h" #include "rthread.h" HASHTABLE *msgid_hash = 0; void thread_init() { if (try_ov > 0) try_ov = ov_init()? 1 : -1; if (try_mt > 0) try_mt = mt_init()? 1 : -1; } /* Generate the thread data we need for this group. We must call ** thread_close() before calling this again. */ void thread_open() { if (!msgid_hash) msgid_hash = hashcreate(201, msgid_cmp); /*TODO: pick a better size */ if (ThreadedGroup) { /* Parse input and use msgid_hash for quick article lookups. */ /* If cached but not threaded articles exist, set up to thread them. */ if (first_subject) { first_cached = firstart; last_cached = firstart - 1; parsed_art = 0; } } if (sel_mode == SM_ARTICLE) set_selector(sel_mode, sel_artsort); else set_selector(sel_threadmode, sel_threadsort); if (try_mt > 0 && !first_subject) if (!mt_data()) return; if (try_ov > 0 && first_cached > last_cached) if (thread_always) (void) ov_data(absfirst, lastart, FALSE); else if (firstart > lastart) { /* If no unread articles, see if ov. exists as fast as possible */ (void) ov_data(absfirst, absfirst, FALSE); cached_all_in_range = FALSE; } else (void) ov_data(firstart, lastart, FALSE); #ifdef USE_NNTP if (!ov_opened) setmissingbits(); #endif #ifndef USE_NNTP if (last_cached > lastart) { toread[ng] += (ART_UNREAD)(last_cached-lastart); /* ensure getngsize() knows the new maximum */ ngmax[ng] = lastart = last_cached; } #endif thread_grow(); /* thread any new articles not yet in the database */ added_articles = 0; sel_page_sp = 0; sel_page_app = 0; } /* Update the group's thread info. */ void thread_grow() { added_articles += lastart - last_cached; if (added_articles && thread_always) cache_range(last_cached + 1, lastart); count_subjects(CS_NORM); if (artptr_list) sort_articles(); else sort_subjects(); } static void kill_tmp_arts(data, extra) HASHDATUM *data; int extra; { register ARTICLE *ap = (ARTICLE*)data->dat_ptr; if (ap) { clear_article(ap); free((char*)ap); } } void thread_close() { curr_artp = artp = Nullart; init_tree(); /* free any tree lines */ if (msgid_hash) { hashwalk(msgid_hash, kill_tmp_arts, 0); hashdestroy(msgid_hash); msgid_hash = 0; } sel_page_sp = 0; sel_page_app = 0; sel_last_ap = 0; sel_last_sp = 0; selected_only = FALSE; sel_exclusive = 0; ov_close(); } void top_article() { art = lastart+1; artp = Nullart; inc_art(selected_only, FALSE); if (art > lastart && last_cached < lastart) art = firstart; } ARTICLE * first_art(sp) register SUBJECT *sp; { register ARTICLE *ap = (ThreadedGroup? sp->thread : sp->articles); if (ap && (ap->flags & AF_MISSING)) ap = next_art(ap); return ap; } ARTICLE * last_art(sp) register SUBJECT *sp; { register ARTICLE *ap; if (!ThreadedGroup) { ap = sp->articles; while (ap->subj_next) ap = ap->subj_next; return ap; } ap = sp->thread; if (ap) { for (;;) { if (ap->sibling) ap = ap->sibling; else if (ap->child1) ap = ap->child1; else break; } if (ap->flags & AF_MISSING) ap = prev_art(ap); } return ap; } /* Bump art/artp to the next article, wrapping from thread to thread. ** If sel_flag is TRUE, only stops at selected articles. ** If rereading is FALSE, only stops at unread articles. */ void inc_art(sel_flag, rereading) bool_int sel_flag, rereading; { register ARTICLE *ap = artp; int subj_mask = (rereading? 0 : SF_VISIT); /* Use the explicit article-order if it exists */ if (artptr_list) { ARTICLE **limit = artptr_list + artptr_list_size; if (!ap) artptr = artptr_list-1; else if (!artptr || *artptr != ap) { for (artptr = artptr_list; artptr < limit; artptr++) { if (*artptr == ap) break; } } do { if (++artptr >= limit) break; ap = *artptr; } while ((!rereading && (ap->flags & AF_READ)) || (sel_flag && !(ap->flags & AF_SEL))); if (artptr < limit) { artp = *artptr; art = article_num(artp); } else { artp = Nullart; art = lastart+1; artptr = artptr_list; } return; } /* Use subject- or thread-order when possible */ if (ThreadedGroup || srchahead) { register SUBJECT *sp; if (ap) sp = ap->subj; else sp = next_subj(Nullsubj, subj_mask); if (!sp) goto num_inc; do { if (ap) ap = next_art(ap); else ap = first_art(sp); while (!ap) { sp = next_subj(sp, subj_mask); if (!sp) break; ap = first_art(sp); } } while (ap && ((!rereading && (ap->flags & AF_READ)) || (sel_flag && !(ap->flags & AF_SEL)))); if ((artp = ap) != Nullart) art = article_num(ap); else { if (art <= last_cached) art = last_cached+1; else art++; if (art <= lastart) artp = article_ptr(art); else art = lastart+1; } return; } /* Otherwise, just increment through the art numbers */ num_inc: if (!ap) { art = firstart-1; ap = article_ptr(art); } do { if (++art > lastart) { ap = Nullart; break; } ap++; } while ((!rereading && (ap->flags & AF_READ)) || (sel_flag && !(ap->flags & AF_SEL)) || (ap->flags & AF_MISSING)); artp = ap; } /* Bump art/artp to the previous article, wrapping from thread to thread. ** If sel_flag is TRUE, only stops at selected articles. ** If rereading is FALSE, only stops at unread articles. */ void dec_art(sel_flag, rereading) bool_int sel_flag, rereading; { register ARTICLE *ap = artp; int subj_mask = (rereading? 0 : SF_VISIT); /* Use the explicit article-order if it exists */ if (artptr_list) { ARTICLE **limit = artptr_list + artptr_list_size; if (!ap) artptr = limit; else if (!artptr || *artptr != ap) { for (artptr = artptr_list; artptr < limit; artptr++) { if (*artptr == ap) break; } } do { if (artptr == artptr_list) break; ap = *--artptr; } while ((!rereading && (ap->flags & AF_READ)) || (sel_flag && !(ap->flags & AF_SEL))); artp = *artptr; art = article_num(artp); return; } /* Use subject- or thread-order when possible */ if (ThreadedGroup || srchahead) { register SUBJECT *sp; if (ap) sp = ap->subj; else sp = prev_subj(Nullsubj, subj_mask); if (!sp) goto num_dec; do { if (ap) ap = prev_art(ap); else ap = last_art(sp); while (!ap) { sp = prev_subj(sp, subj_mask); if (!sp) break; ap = last_art(sp); } } while (ap && ((!rereading && (ap->flags & AF_READ)) || (sel_flag && !(ap->flags & AF_SEL)))); if ((artp = ap) != Nullart) art = article_num(ap); else art = absfirst-1; return; } /* Otherwise, just decrement through the art numbers */ num_dec: ap = article_ptr(art); do { if (--art < absfirst) { ap = Nullart; break; } ap--; } while ((!rereading && (ap->flags & AF_READ)) || (sel_flag && !(ap->flags & AF_SEL)) || (ap->flags & AF_MISSING)); artp = ap; } /* Bump the param to the next article in depth-first order. */ ARTICLE * bump_art(ap) register ARTICLE *ap; { if (ap->child1) return ap->child1; while (!ap->sibling) { if (!(ap = ap->parent)) return Nullart; } return ap->sibling; } /* Bump the param to the next REAL article. Uses subject order in a ** non-threaded group; honors the breadth_first flag in a threaded one. */ ARTICLE * next_art(ap) register ARTICLE *ap; { try_again: if (!ThreadedGroup) { ap = ap->subj_next; goto done; } if (breadth_first) { if (ap->sibling) { ap = ap->sibling; goto done; } if (ap->parent) ap = ap->parent->child1; else ap = ap->subj->thread; } do { if (ap->child1) { ap = ap->child1; goto done; } while (!ap->sibling) { if (!(ap = ap->parent)) return Nullart; } ap = ap->sibling; } while (breadth_first); done: if (ap && (ap->flags & AF_MISSING)) goto try_again; return ap; } /* Bump the param to the previous REAL article. Uses subject order in a ** non-threaded group. */ ARTICLE * prev_art(ap) register ARTICLE *ap; { register ARTICLE *initial_ap; try_again: initial_ap = ap; if (!ThreadedGroup) { if ((ap = ap->subj->articles) == initial_ap) ap = Nullart; else while (ap->subj_next != initial_ap) ap = ap->subj_next; goto done; } ap = (ap->parent ? ap->parent->child1 : ap->subj->thread); if (ap == initial_ap) { ap = ap->parent; goto done; } while (ap->sibling != initial_ap) ap = ap->sibling; while (ap->child1) { ap = ap->child1; while (ap->sibling) ap = ap->sibling; } done: if (ap && (ap->flags & AF_MISSING)) goto try_again; return ap; } /* Find the next art/artp with the same subject as this one. Returns ** FALSE if no such article exists. */ bool next_art_with_subj() { register ARTICLE *ap = artp; if (!ap) return FALSE; do { ap = ap->subj_next; if (!ap) { if (!art) art = firstart; return FALSE; } } while ((ap->flags & (AF_READ|AF_MISSING)) || (selected_only && !(ap->flags & AF_SEL))); artp = ap; art = article_num(ap); #ifdef ARTSEARCH srchahead = -1; #endif return TRUE; } /* Find the previous art/artp with the same subject as this one. Returns ** FALSE if no such article exists. */ bool prev_art_with_subj() { register ARTICLE *ap = artp, *ap2; if (!ap) return FALSE; do { ap2 = ap->subj->articles; if (ap2 == ap) ap = Nullart; else { while (ap2 && ap2->subj_next != ap) ap2 = ap2->subj_next; ap = ap2; } if (!ap) { if (!art) art = lastart; return FALSE; } } while ((ap->flags & AF_MISSING) || (selected_only && !(ap->flags & AF_SEL))); artp = ap; art = article_num(ap); return TRUE; } SUBJECT * next_subj(sp, subj_mask) register SUBJECT *sp; int subj_mask; { if (!sp) sp = first_subject; else if (sel_mode == SM_THREAD) { ARTICLE *ap = sp->thread; do { sp = sp->next; } while (sp && sp->thread == ap); } else sp = sp->next; while (sp && (sp->flags & subj_mask) != subj_mask) { sp = sp->next; } return sp; } SUBJECT * prev_subj(sp, subj_mask) register SUBJECT *sp; int subj_mask; { if (!sp) sp = last_subject; else if (sel_mode == SM_THREAD) { ARTICLE *ap = sp->thread; do { sp = sp->prev; } while (sp && sp->thread == ap); } else sp = sp->prev; while (sp && (sp->flags & subj_mask) != subj_mask) { sp = sp->prev; } return sp; } /* Select a single article. */ void select_article(ap, auto_flags) register ARTICLE *ap; int auto_flags; { int desired_flags = (sel_rereading? AF_READ : 0); #ifdef VERBOSE bool echo; if (auto_flags & AUTO_ECHO) { echo = TRUE; auto_flags &= ~AUTO_ECHO; } else echo = FALSE; #else auto_flags &= ~AUTO_ECHO; #endif if (auto_flags & (AUTO_SELECT|AUTO_SELECTALL)) localkf_changes = 2; if ((ap->flags & (AF_MISSING|AF_READ)) == desired_flags) { if (!(ap->flags & sel_mask)) { selected_count++; #ifdef VERBOSE if (echo) { IF(verbose) fputs("\tSelected",stdout); } #endif } ap->flags = (ap->flags & ~AF_DEL) | sel_mask; ap->autofl |= auto_flags; } else ap->autofl |= auto_flags; if (ap->subj) { if (!(ap->subj->flags & sel_mask)) selected_subj_cnt++; ap->subj->flags = (ap->subj->flags&~SF_DEL) | sel_mask | SF_VISIT; } selected_only = (selected_only || selected_count != 0); } /* Select this article's subject. */ void select_arts_subject(ap, auto_flags) register ARTICLE *ap; int auto_flags; { if (ap->subj && ap->subj->articles) select_subject(ap->subj, auto_flags); else select_article(ap, auto_flags); } /* Select all the articles in a subject. */ void select_subject(subj, auto_flags) SUBJECT *subj; int auto_flags; { register ARTICLE *ap; int desired_flags = (sel_rereading? AF_READ : 0); int old_count = selected_count; if (auto_flags & (AUTO_SELECT|AUTO_SELECTALL)) localkf_changes = 2; for (ap = subj->articles; ap; ap = ap->subj_next) { if ((ap->flags & (AF_MISSING|AF_READ|sel_mask)) == desired_flags) { ap->flags |= sel_mask; ap->autofl |= auto_flags; selected_count++; } else ap->autofl |= auto_flags; } if (selected_count > old_count) { if (!(subj->flags & sel_mask)) selected_subj_cnt++; subj->flags = (subj->flags & ~SF_DEL) | sel_mask | SF_VISIT | SF_WASSELECTED; selected_only = TRUE; } else subj->flags |= SF_WASSELECTED; } /* Select this article's thread. */ void select_arts_thread(ap, auto_flags) register ARTICLE *ap; int auto_flags; { if (ap->subj && ap->subj->thread) select_thread(ap->subj->thread, auto_flags); else select_arts_subject(ap, auto_flags); } /* Select all the articles in a thread. */ void select_thread(thread, auto_flags) register ARTICLE *thread; int auto_flags; { register SUBJECT *sp; sp = thread->subj; do { select_subject(sp, auto_flags); sp = sp->thread_link; } while (sp != thread->subj); } /* Select the subthread attached to this article. */ void select_subthread(ap, auto_flags) register ARTICLE *ap; int auto_flags; { register ARTICLE *limit; SUBJECT *subj; int desired_flags = (sel_rereading? AF_READ : 0); int old_count = selected_count; if (!ap) return; subj = ap->subj; for (limit = ap; limit; limit = limit->parent) { if (limit->sibling) { limit = limit->sibling; break; } } if (auto_flags & (AUTO_SELECT|AUTO_SELECTALL)) localkf_changes = 2; for (; ap != limit; ap = bump_art(ap)) { if ((ap->flags & (AF_MISSING|AF_READ|sel_mask)) == desired_flags) { ap->flags |= sel_mask; ap->autofl |= auto_flags; selected_count++; } else ap->autofl |= auto_flags; } if (subj && selected_count > old_count) { if (!(subj->flags & sel_mask)) selected_subj_cnt++; subj->flags = (subj->flags & ~SF_DEL) | sel_mask | SF_VISIT; selected_only = TRUE; } } /* Deselect a single article. */ void deselect_article(ap) register ARTICLE *ap; { if (ap->flags & sel_mask) { ap->flags &= ~sel_mask; if (!selected_count--) selected_count = 0; #ifdef VERBOSE if (mode != 't') { IF(verbose) fputs("\tDeselected",stdout); } #endif } if (sel_rereading && sel_mode == SM_ARTICLE) ap->flags |= AF_DEL; } /* Deselect this article's subject. */ void deselect_arts_subject(ap) register ARTICLE *ap; { if (ap->subj && ap->subj->articles) deselect_subject(ap->subj); else deselect_article(ap); } /* Deselect all the articles in a subject. */ void deselect_subject(subj) SUBJECT *subj; { register ARTICLE *ap; for (ap = subj->articles; ap; ap = ap->subj_next) { if (ap->flags & sel_mask) { ap->flags &= ~sel_mask; if (!selected_count--) selected_count = 0; } } if (subj->flags & sel_mask) { subj->flags &= ~sel_mask; selected_subj_cnt--; } subj->flags &= ~(SF_VISIT | SF_WASSELECTED); if (sel_rereading) subj->flags |= SF_DEL; else subj->flags &= ~SF_DEL; } /* Deselect this article's thread. */ void deselect_arts_thread(ap) register ARTICLE *ap; { if (ap->subj && ap->subj->thread) deselect_thread(ap->subj->thread); else deselect_arts_subject(ap); } /* Deselect all the articles in a thread. */ void deselect_thread(thread) register ARTICLE *thread; { register SUBJECT *sp; sp = thread->subj; do { deselect_subject(sp); sp = sp->thread_link; } while (sp != thread->subj); } /* Deselect everything. */ void deselect_all() { register SUBJECT *sp; for (sp = first_subject; sp; sp = sp->next) deselect_subject(sp); selected_count = selected_subj_cnt = 0; sel_page_sp = 0; sel_page_app = 0; sel_last_ap = 0; sel_last_sp = 0; selected_only = FALSE; } /* Kill all unread articles attached to this article's subject. */ void kill_arts_subject(ap, auto_flags) register ARTICLE *ap; int auto_flags; { if (ap->subj && ap->subj->articles) kill_subject(ap->subj, auto_flags); else { set_read(ap); ap->autofl |= AUTO_KILLALL; } } /* Kill all unread articles attached to the given subject. */ void kill_subject(subj, auto_flags) SUBJECT *subj; int auto_flags; { register ARTICLE *ap; register int killmask = ((auto_flags&KF_ALL)? AF_READ:(AF_READ|sel_mask)); if (auto_flags & KF_KILLFILE) { localkf_changes = 2; auto_flags = AUTO_KILLALL; } else auto_flags = 0; for (ap = subj->articles; ap; ap = ap->subj_next) { if (!(ap->flags & killmask)) set_read(ap); ap->autofl |= auto_flags; } subj->flags &= ~(SF_VISIT | SF_WASSELECTED); } /* Kill all unread articles attached to this article's thread. */ void kill_arts_thread(ap, auto_flags) register ARTICLE *ap; int auto_flags; { if (ap->subj && ap->subj->thread) kill_thread(ap->subj->thread, auto_flags); else kill_arts_subject(ap, auto_flags); } /* Kill all unread articles attached to the given thread. */ void kill_thread(thread, auto_flags) register ARTICLE *thread; int auto_flags; { register SUBJECT *sp; sp = thread->subj; do { kill_subject(sp, auto_flags); sp = sp->thread_link; } while (sp != thread->subj); } /* Kill the subthread attached to this article. */ void kill_subthread(ap, auto_flags) register ARTICLE *ap; int auto_flags; { register ARTICLE *limit; if (!ap) return; for (limit = ap; limit; limit = limit->parent) { if (limit->sibling) { limit = limit->sibling; break; } } if (auto_flags & KF_KILLFILE) { localkf_changes = 2; auto_flags = AUTO_KILL; } else auto_flags = 0; for (; ap != limit; ap = bump_art(ap)) { if (!(ap->flags & (AF_READ|AF_MISSING))) set_read(ap); ap->autofl |= auto_flags; } } /* Unkill all the articles attached to the given subject. */ void unkill_subject(subj) SUBJECT *subj; { register ARTICLE *ap; for (ap = subj->articles; ap; ap = ap->subj_next) { if (sel_rereading) { if ((ap->flags & (AF_DELSEL|AF_MISSING)) == AF_DELSEL) { if (ap->flags & AF_READ) toread[ng]++; ap->flags = (ap->flags & ~(AF_DELSEL|AF_READ)) | AF_SEL; } else ap->flags &= ~(AF_DEL|AF_DELSEL); } else { if ((ap->flags & (AF_READ|AF_MISSING)) == AF_READ) onemore(ap); if (selected_only && !(ap->flags & (AF_SEL|AF_READ))) { ap->flags = (ap->flags & ~AF_DEL) | AF_SEL; selected_count++; } } } if (!sel_rereading && selected_only && !(subj->flags & SF_SEL)) { subj->flags |= SF_SEL | SF_VISIT | SF_WASSELECTED; selected_subj_cnt++; } subj->flags &= ~(SF_DEL|SF_DELSEL); } /* Unkill all the articles attached to the given thread. */ void unkill_thread(thread) register ARTICLE *thread; { register SUBJECT *sp; sp = thread->subj; do { unkill_subject(sp); sp = sp->thread_link; } while (sp != thread->subj); } /* Unkill the subthread attached to this article. */ void unkill_subthread(ap) register ARTICLE *ap; { register ARTICLE *limit; register SUBJECT *sp; if (!ap) return; for (limit = ap; limit; limit = limit->parent) { if (limit->sibling) { limit = limit->sibling; break; } } sp = ap->subj; for (; ap != limit; ap = bump_art(ap)) { if ((ap->flags & (AF_READ|AF_MISSING)) == AF_READ) onemore(ap); if (selected_only && !(ap->flags & AF_SEL)) { ap->flags |= AF_SEL; selected_count++; } } if (!(sp->flags & sel_mask)) selected_subj_cnt++; sp->flags = (sp->flags & ~SF_DEL) | SF_SEL | SF_VISIT; selected_only = (selected_only || selected_count != 0); } /* Clear the auto flags in all unread articles attached to the given subject. */ void clear_subject(subj) SUBJECT *subj; { register ARTICLE *ap; for (ap = subj->articles; ap; ap = ap->subj_next) { ap->autofl = 0; } localkf_changes = 2; } /* Clear the auto flags in all unread articles attached to the given thread. */ void clear_thread(thread) register ARTICLE *thread; { register SUBJECT *sp; sp = thread->subj; do { clear_subject(sp); sp = sp->thread_link; } while (sp != thread->subj); } /* Clear the auto flags in the subthread attached to this article. */ void clear_subthread(ap) register ARTICLE *ap; { register ARTICLE *limit; if (!ap) return; for (limit = ap; limit; limit = limit->parent) { if (limit->sibling) { limit = limit->sibling; break; } } for (; ap != limit; ap = bump_art(ap)) { ap->autofl = 0; } localkf_changes = 2; } ARTICLE * subj_art(sp) SUBJECT *sp; { register ARTICLE *ap = Nullart; int art_mask = (selected_only? AF_SEL : 0); bool TG_save = ThreadedGroup; ThreadedGroup = (sel_mode == SM_THREAD); ap = first_art(sp); while (ap && (ap->flags & (art_mask|AF_READ)) != art_mask) ap = next_art(ap); if (!ap) { reread = TRUE; ap = first_art(sp); if (art_mask) { while (ap && !(ap->flags & AF_SEL)) ap = next_art(ap); if (!ap) ap = first_art(sp); } } ThreadedGroup = TG_save; return ap; } /* Find the next thread (first if art > lastart). If articles are selected, ** only choose from threads with selected articles. */ void visit_next_thread() { register SUBJECT *sp; register ARTICLE *ap = artp; sp = (ap? ap->subj : Nullsubj); while ((sp = next_subj(sp, SF_VISIT)) != Nullsubj) { if ((ap = subj_art(sp)) != Nullart) { art = article_num(ap); artp = ap; return; } reread = FALSE; } artp = Nullart; art = lastart+1; forcelast = TRUE; } /* Find previous thread (or last if artp == NULL). If articles are selected, ** only choose from threads with selected articles. */ void visit_prev_thread() { register SUBJECT *sp; register ARTICLE *ap = artp; sp = (ap? ap->subj : Nullsubj); while ((sp = prev_subj(sp, SF_VISIT)) != Nullsubj) { if ((ap = subj_art(sp)) != Nullart) { art = article_num(ap); artp = ap; return; } reread = FALSE; } artp = Nullart; art = lastart+1; forcelast = TRUE; } /* Find artp's parent or oldest ancestor. Returns FALSE if no such ** article. Sets art and artp otherwise. */ bool find_parent(keep_going) bool_int keep_going; { register ARTICLE *ap = artp; if (!ap->parent) return FALSE; do { ap = ap->parent; } while (keep_going && ap->parent); if (((artp = ap)->flags & AF_TMPMEM) == AF_TMPMEM) art = 0; else art = article_num(ap); return TRUE; } /* Find artp's first child or youngest decendent. Returns FALSE if no ** such article. Sets art and artp otherwise. */ bool find_leaf(keep_going) bool_int keep_going; { register ARTICLE *ap = artp; if (!ap->child1) return FALSE; do { ap = ap->child1; } while (keep_going && ap->child1); if (((artp = ap)->flags & AF_TMPMEM) == AF_TMPMEM) art = 0; else art = article_num(ap); return TRUE; } static ARTICLE *first_sib(), *last_sib(); /* Find the next "sibling" of artp, including cousins that are the ** same distance down the thread as we are. Returns FALSE if no such ** article. Sets art and artp otherwise. */ bool find_next_sib() { ARTICLE *ta, *tb; int ascent; ascent = 0; ta = artp; for (;;) { while (ta->sibling) { ta = ta->sibling; if (tb = first_sib(ta, ascent)) { if (((artp = tb)->flags & AF_TMPMEM) == AF_TMPMEM) art = 0; else art = article_num(tb); return TRUE; } } if (!(ta = ta->parent)) break; ascent++; } return FALSE; } /* A recursive routine to find the first node at the proper depth. This ** article is at depth 0. */ static ARTICLE * first_sib(ta, depth) ARTICLE *ta; int depth; { ARTICLE *tb; if (!depth) return ta; for (;;) { if (ta->child1 && (tb = first_sib(ta->child1, depth-1))) return tb; if (!ta->sibling) return Nullart; ta = ta->sibling; } } /* Find the previous "sibling" of artp, including cousins that are ** the same distance down the thread as we are. Returns FALSE if no ** such article. Sets art and artp otherwise. */ bool find_prev_sib() { ARTICLE *ta, *tb; int ascent; ascent = 0; ta = artp; for (;;) { tb = ta; if (ta->parent) ta = ta->parent->child1; else ta = ta->subj->thread; if (tb = last_sib(ta, ascent, tb)) { if (((artp = tb)->flags & AF_TMPMEM) == AF_TMPMEM) art = 0; else art = article_num(tb); return TRUE; } if (!(ta = ta->parent)) break; ascent++; } return FALSE; } /* A recursive routine to find the last node at the proper depth. This ** article is at depth 0. */ static ARTICLE * last_sib(ta, depth, limit) ARTICLE *ta; int depth; ARTICLE *limit; { ARTICLE *tb, *tc; if (ta == limit) return Nullart; if (ta->sibling) { tc = ta->sibling; if (tc != limit && (tb = last_sib(tc,depth,limit))) return tb; } if (!depth) return ta; if (ta->child1) return last_sib(ta->child1, depth-1, limit); return Nullart; } /* Get each subject's article count; count total articles and selected ** articles (use sel_rereading to determine whether to count read or ** unread articles); deselect any subjects we find that are empty if ** CS_UNSELECT or CS_UNSEL_STORE is specified. If mode is CS_RESELECT ** is specified, the selections from the last CS_UNSEL_STORE are ** reselected. */ void count_subjects(cmode) int cmode; { register int count, sel_count; register ARTICLE *ap; register SUBJECT *sp; int desired_flags = (sel_rereading? AF_READ : 0); time_t subjdate; article_count = selected_count = selected_subj_cnt = 0; if (last_cached >= lastart) firstart = lastart+1; if (cmode != CS_RETAIN) for (sp = first_subject; sp; sp = sp->next) sp->flags &= ~SF_VISIT; for (sp = first_subject; sp; sp = sp->next) { subjdate = 0; count = sel_count = 0; for (ap = sp->articles; ap; ap = ap->subj_next) { if ((ap->flags & (AF_MISSING|AF_READ)) == desired_flags) { count++; if (ap->flags & sel_mask) sel_count++; if (!subjdate) subjdate = ap->date; if (article_num(ap) < firstart) firstart = article_num(ap); } } if (cmode == CS_UNSEL_STORE) { if (sp->flags & SF_SEL) sp->flags |= SF_OLDSEL; else sp->flags &= ~SF_OLDSEL; } else if (cmode == CS_RESELECT) { if (sp->flags & SF_OLDSEL) sp->flags |= SF_SEL; else sp->flags &= ~SF_SEL; } sp->misc = count; if (subjdate) sp->date = subjdate; else if (!sp->date && sp->articles) sp->date = sp->articles->date; article_count += count; if (sel_count) { sp->flags = (sp->flags & ~(SF_SEL|SF_DEL)) | sel_mask; selected_count += sel_count; selected_subj_cnt++; } else if (cmode >= CS_UNSELECT) sp->flags &= ~sel_mask; else if (sp->flags & sel_mask) { sp->flags &= ~SF_DEL; selected_subj_cnt++; } if (count && (!selected_only || (sp->flags & sel_mask))) { sp->flags |= SF_VISIT; } } if (cmode != CS_RETAIN && !article_count && !selected_only) { for (sp = first_subject; sp; sp = sp->next) sp->flags |= SF_VISIT; } } int subjorder_subject(spp1, spp2) register SUBJECT **spp1; register SUBJECT **spp2; { return strcasecmp((*spp1)->str+4, (*spp2)->str+4) * sel_direction; } int subjorder_date(spp1, spp2) register SUBJECT **spp1; register SUBJECT **spp2; { return (int)((*spp1)->date - (*spp2)->date) * sel_direction; } int subjorder_count(spp1, spp2) register SUBJECT **spp1; register SUBJECT **spp2; { int eq; if ((eq = (int)((*spp1)->misc - (*spp2)->misc)) != 0) return eq * sel_direction; return (int)((*spp1)->date - (*spp2)->date); } int threadorder_subject(spp1, spp2) SUBJECT **spp1; SUBJECT **spp2; { register ARTICLE *t1 = (*spp1)->thread; register ARTICLE *t2 = (*spp2)->thread; if (t1 != t2 && t1 && t2) return strcasecmp(t1->subj->str+4, t2->subj->str+4) * sel_direction; return (int)((*spp1)->date - (*spp2)->date); } int threadorder_date(spp1, spp2) SUBJECT **spp1; SUBJECT **spp2; { register ARTICLE *t1 = (*spp1)->thread; register ARTICLE *t2 = (*spp2)->thread; if (t1 != t2 && t1 && t2) { register SUBJECT *sp1, *sp2; int eq; if (!(sp1 = t1->subj)->misc) for (sp1=sp1->thread_link; sp1 != t1->subj; sp1=sp1->thread_link) if (sp1->misc) break; if (!(sp2 = t2->subj)->misc) for (sp2=sp2->thread_link; sp2 != t2->subj; sp2=sp2->thread_link) if (sp2->misc) break; if (!(eq = (int)(sp1->date - sp2->date))) return strcasecmp(sp1->str+4, sp2->str+4); return eq * sel_direction; } return (int)((*spp1)->date - (*spp2)->date); } int threadorder_count(spp1, spp2) SUBJECT **spp1; SUBJECT **spp2; { register int size1 = (*spp1)->misc; register int size2 = (*spp2)->misc; if ((*spp1)->thread != (*spp2)->thread) { register SUBJECT *sp; for (sp = (*spp1)->thread_link; sp != *spp1; sp = sp->thread_link) size1 += sp->misc; for (sp = (*spp2)->thread_link; sp != *spp2; sp = sp->thread_link) size2 += sp->misc; } if (size1 != size2) return (size1 - size2) * sel_direction; return threadorder_date(spp1, spp2); } /* Sort the subjects according to the chosen order. */ void sort_subjects() { register SUBJECT *sp; register int i; SUBJECT **lp, **subj_list; int (*sort_procedure)(); /* If we don't have at least two subjects, we're done! */ if (!first_subject || !first_subject->next) return; switch (sel_sort) { case SS_DATE: default: sort_procedure = (sel_mode == SM_THREAD? threadorder_date : subjorder_date); break; case SS_SUBJECT: sort_procedure = (sel_mode == SM_THREAD? threadorder_subject : subjorder_subject); break; case SS_COUNT: sort_procedure = (sel_mode == SM_THREAD? threadorder_count : subjorder_count); break; } subj_list = (SUBJECT**)safemalloc(subject_count * sizeof (SUBJECT*)); for (lp = subj_list, sp = first_subject; sp; sp = sp->next) *lp++ = sp; assert(lp - subj_list == subject_count); qsort(subj_list, subject_count, sizeof (SUBJECT*), sort_procedure); first_subject = sp = subj_list[0]; sp->prev = Nullsubj; for (i = subject_count, lp = subj_list; --i; lp++) { lp[0]->next = lp[1]; lp[1]->prev = lp[0]; if (sel_mode == SM_THREAD) { if (lp[0]->thread && lp[0]->thread == lp[1]->thread) lp[0]->thread_link = lp[1]; else { lp[0]->thread_link = sp; sp = lp[1]; } } } last_subject = lp[0]; last_subject->next = Nullsubj; if (sel_mode == SM_THREAD) last_subject->thread_link = sp; free((char*)subj_list); } int artorder_date(art1, art2) register ARTICLE **art1; register ARTICLE **art2; { return (int)((*art1)->date - (*art2)->date) * sel_direction; } int artorder_subject(art1, art2) register ARTICLE **art1; register ARTICLE **art2; { if ((*art1)->subj == (*art2)->subj) return (int)((*art1)->date - (*art2)->date); return strcasecmp((*art1)->subj->str + 4, (*art2)->subj->str + 4) * sel_direction; } int artorder_author(art1, art2) register ARTICLE **art1; register ARTICLE **art2; { int eq; if ((eq = strcasecmp((*art1)->from, (*art2)->from)) != 0) return eq * sel_direction; return (int)((*art1)->date - (*art2)->date); } int artorder_number(art1, art2) register ARTICLE **art1; register ARTICLE **art2; { return (int)(article_num(*art1) - article_num(*art2)) * sel_direction; } int artorder_groups(art1, art2) register ARTICLE **art1; register ARTICLE **art2; { if ((*art1)->subj == (*art2)->subj) return (int)((*art1)->date - (*art2)->date); return (int)((*art1)->subj->date - (*art2)->subj->date) * sel_direction; } /* Sort the articles according to the chosen order. */ void sort_articles() { int (*sort_procedure)(); build_artptrs(); /* If we don't have at least two articles, we're done! */ if (artptr_list_size < 2) return; switch (sel_sort) { case SS_DATE: default: sort_procedure = artorder_date; break; case SS_SUBJECT: sort_procedure = artorder_subject; break; case SS_AUTHOR: sort_procedure = artorder_author; break; case SS_NUMBER: sort_procedure = artorder_number; break; case SS_GROUPS: sort_procedure = artorder_groups; break; } sel_page_app = 0; qsort(artptr_list, artptr_list_size, sizeof (ARTICLE*), sort_procedure); } static void build_artptrs() { ARTICLE **app, *ap; ART_NUM count = article_count; int desired_flags = (sel_rereading? AF_READ : 0); if (!artptr_list || artptr_list_size != count) { artptr_list = (ARTICLE**)saferealloc((char*)artptr_list, (MEM_SIZE)count * sizeof (ARTICLE*)); artptr_list_size = count; } for (app = artptr_list, ap = article_list; count; ap++) { if ((ap->flags & (AF_MISSING|AF_READ)) == desired_flags) { *app++ = ap; count--; } } } trn-3.6/util2.h0000664000000000000000000000134505654303443010276 0ustar /* $Id: util2.h,v 3.6 1994/10/28 11:11:11 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ char *instr _((char*,char*,bool_int)); void export _((char*,char*)); int envix _((char*)); trn-3.6/nntp.h0000664000000000000000000000176205646162630010223 0ustar /* $Id: nntp.h,v 3.0 1992/12/14 00:14:55 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifdef USE_NNTP #define FB_BACKGROUND 0 #define FB_OUTPUT 1 #define FB_SILENT 2 int nntp_list _((char*,char*,int)); bool nntp_group _((char*,NG_NUM)); bool nntp_stat _((ART_NUM)); ART_NUM nntp_stat_id _((char*)); bool nntp_header _((ART_NUM)); FILE *nntp_body _((ART_NUM)); long nntp_artsize _((void)); int nntp_finishbody _((int)); void nntp_seekart _((long)); long nntp_tellart _((void)); char *nntp_readart _((char*,int)); time_t nntp_time _((void)); bool nntp_newgroups _((time_t)); bool nntp_listgroup _((void)); char *nntp_artname _((void)); char nntp_handle_timeout _((bool_int)); void nntp_cleanup _((void)); #ifdef USE_XTHREAD long nntp_readcheck _((void)); long nntp_read _((char*,long)); #endif #include "nntpclient.h" #endif /* USE_NNTP */ trn-3.6/Configure0000775000000000000000000030475607041327652010751 0ustar #! /bin/sh # # If these # comments don't work, trim them. Don't worry about any other # shell scripts, Configure will trim # comments from them for you. # # (If you are trying to port this package to a machine without sh, # I would suggest you have a look at the prototypical config.h.SH file # and edit it to reflect your system. Some packages may include samples # of config.h for certain machines, so you might look for one of those.) # # Yes, you may rip this off to use in other distribution packages. This # script belongs to the public domain and cannot be copyrighted. # # (Note: this Configure script was generated automatically. Rather than # working with this copy of Configure, you may wish to get metaconfig. # The dist-3.0 package (which contains metaconfig) was posted in # comp.sources.misc so you may fetch it yourself from your nearest # archive site. Check with Archie if you don't know where that can be.) # # $Id: Head.U,v 3.0.1.3 1993/12/15 08:15:07 ram Exp $ # # Generated on Thu May 5 12:36:48 PDT 1994 [metaconfig 3.0 PL22] cat >/tmp/c1$$ </tmp/c2$$ </dev/null 2>&1; then : already under /bin/ksh else cat <<'EOM' (Feeding myself to ksh to avoid nasty sh bug in "here document" expansion.) EOM exec /bin/ksh $0 "$@" fi else : Warn them if they use ksh on other systems (PATH=.; alias -x) >/dev/null 2>&1 && \ cat <<'EOM' (I see you are using the Korn shell. Some ksh's blow up on Configure, especially on exotic machines. If yours does, try the Bourne shell instead.) EOM fi : Configure runs within the UU subdirectory test -d UU || mkdir UU cd UU && rm -f * d_bsd='' d_eunice='' d_xenix='' eunicefix='' Mcc='' awk='' basename='' bash='' cat='' cp='' cpp='' csh='' date='' diff='' echo='' ed='' egrep='' expr='' grep='' inews='' ispell='' ksh='' less='' lint='' mail='' metamail='' mhn='' mkdir='' more='' munpack='' mv='' nroff='' pg='' rm='' rmail='' sed='' sendmail='' smail='' sort='' spell='' tail='' test='' tr='' uname='' uniq='' uuname='' vi='' vspell='' who='' hint='' myuname='' srcdir='' vincludes='' vpath='' Id='' Log='' active='' acttimes='' d_acttimes='' myactive='' bin='' binexp='' installbin='' cc='' ccflags='' cppflags='' ldflags='' lkflags='' optimize='' cf_by='' cf_time='' contains='' cpplast='' cppminus='' cpprun='' cppstdin='' d_ftime='' aphostcmd='' d_gethname='' d_phostcmd='' d_uname='' d_getpwent='' d_getcwd='' d_getwd='' d_havetlib='' termlib='' d_ignoreorg='' d_internet='' d_memcmp='' d_memcpy='' d_memset='' d_mimeshow='' d_mimestore='' mimeshow='' mimestore='' d_newsadm='' newsadmin='' d_nntp='' d_xdata='' d_genauth='' servername='' d_nolnbuf='' d_normsig='' jobslib='' d_novoid='' void='' d_portable='' d_rdchk='' d_rename='' d_sigblock='' d_sighold='' d_sizet='' d_strccmp='' d_strchr='' d_strftime='' strftimec='' strftimeo='' d_libndir='' d_usendir='' libndir='' ndirc='' ndiro='' d_vfork='' d_voidsig='' signal_t='' defeditor='' filexp='' d_dirnamlen='' i_dirent='' i_ptem='' i_stdlib='' i_string='' strings='' i_sysdir='' d_voidtty='' i_bsdioctl='' i_sysfilio='' i_sysioctl='' i_syssockio='' i_sysndir='' i_sgtty='' i_termio='' i_termios='' i_systime='' i_time='' i_unistd='' i_vfork='' ispell_options='' ispell_prg='' libc='' plibpth='' xlibpth='' libs='' citydist='' cntrydist='' contdist='' locdist='' multistatedist='' orgdist='' statedist='' mailer='' mailfile='' installmansrc='' manext='' mansrc='' mansrcexp='' mboxchar='' c='' n='' d_berknames='' d_passnames='' d_usgnames='' nametype='' inewsloc='' newslib='' newslibexp='' newsspool='' orgname='' package='' spackage='' pager='' phost='' phostcmd='' prefshell='' installprivlib='' privlib='' privlibexp='' rootid='' sharpbang='' shsharp='' spitshell='' startsh='' sysman='' d_usemt='' d_useov='' overviewdir='' threaddir='' trn_init='' trn_select='' nm_opt='' runnm='' usenm='' incpath='' mips='' mips_type='' usrinc='' d_whoami='' CONFIG='' : set package name package=trn first=`echo $package | sed -e 's/^\(.\).*/\1/'` last=`echo $package | sed -e 's/^.\(.*\)/\1/'` spackage=`echo $first | tr '[a-z]' '[A-Z]'`$last : determine where the sources are cd .. objdir=`pwd` _srcdir=`expr X$0 : 'X\(.*\)/'` if test -f MANIFEST -o ! -f $_srcdir/MANIFEST; then _srcdir='..' _vpath='.' else case "$_srcdir" in /*) ;; *) _srcdir=`(cd $_srcdir ; pwd)` ;; esac echo "Aha! Your $package sources are really in $_srcdir." _vpath="$_srcdir" fi cd UU : script used to extract .SH files with variable substitutions cat >extract <<'EOS' CONFIG=true case "$_srcdir" in ..) _srcdir='.';; esac echo "Doing variable substitutions on .SH files..." if test -f $_srcdir/MANIFEST; then set x `awk '$1 ~ /.SH$/ {print $1}' $_srcdir/MANIFEST` else echo "(Looking for .SH files under the current directory.)" set x `(cd $_srcdir ; find . -name "*.SH" -print)` fi shift case $# in 0) set x *.SH ; shift case "$1" in \*.SH) shift ;; esac ;; esac for file in $*; do . $_srcdir/$file done EOS : produce awk script to parse command line options cat >options.awk <<'EOF' BEGIN { optstr = "deEf:hrsSV"; # getopt-style specification len = length(optstr); for (i = 1; i <= len; i++) { c = substr(optstr, i, 1); if (i < len) a = substr(optstr, i + 1, 1); else a = ""; if (a == ":") { arg[c] = 1; i++; } opt[c] = 1; } } { expect = 0; str = $0; if (substr(str, 1, 1) != "-") { printf("'%s'\n", str); next; } len = length($0); for (i = 2; i <= len; i++) { c = substr(str, i, 1); if (!opt[c]) { printf("-%s\n", substr(str, i)); next; } printf("-%s\n", c); if (arg[c]) { if (i < len) printf("'%s'\n", substr(str, i + 1)); else expect = 1; next; } } } END { if (expect) print "?"; } EOF : process the command line options set X `for arg in "$@"; do echo $arg; done | awk -f options.awk` eval "set $*" shift rm -f options.awk : set up default values fastread='' reuseval=false config_sh='' alldone='' error='' silent='' extractsh='' : option parsing while test $# -gt 0; do case "$1" in -d) shift; fastread=yes;; -e) shift; alldone=cont;; -f) shift cd .. if test -r "$1"; then config_sh="$1" else echo "Configure: cannot read config file $1." >&2 error=true fi cd UU shift;; -h) shift; error=true;; -r) shift; reuseval=true;; -s) shift; silent=true;; -E) shift; alldone=exit;; -S) shift; extractsh=true;; -V) echo "Configure generated by metaconfig 3.0 PL22." >&2 exit 0;; --) break;; -*) echo "Configure: unknown option $1" >&2; shift; error=true;; *) break;; esac done case "$error" in true) cat >&2 <&1 case "$silent" in true) exec 1>/dev/null;; esac case "$extractsh" in true) case "$config_sh" in '') config_sh='config.sh'; config='./config.sh';; /*) config="$config_sh";; *) config="./$config_sh";; esac echo " " echo "Fetching answers from $config_sh..." cd .. . $config echo " " . ./UU/extract rm -rf UU echo "Done." exit 0 ;; esac : Eunice requires " " instead of "", can you believe it echo " " : Here we go... echo "Beginning of configuration questions for $package." define='define' undef='undef' smallmach='pdp11 i8086 z8000 i80286 iAPX286' rmlist='' trap 'echo " "; rm -rf $rmlist; exit 1' 1 2 3 15 : We must find out about Eunice early eunicefix=':' if test -f /etc/unixtovms; then eunicefix=/etc/unixtovms fi if test -f /etc/unixtovms.exe; then eunicefix=/etc/unixtovms.exe fi : list of known cpp symbols attrlist="__alpha __bsdi__ BSD_NET2 DGUX M_I186 M_I286 M_I386" attrlist="$attrlist M_I8086 M_XENIX UTS __DGUX__" attrlist="$attrlist _AIX __STDC__ __m88k__ ansi bsd4_2 gcos gimpel" attrlist="$attrlist hp9000s300 hp9000s400 hp9000s500 hp9000s700" attrlist="$attrlist hp9000s800 hpux" attrlist="$attrlist i186 i386 i486 i8086 iAPX286 ibm interdata" attrlist="$attrlist m88k mc300 mc500 mc68000 mc68k mc700 mert" attrlist="$attrlist mips NeXT ns16000 ns32000 nsc32000 os" attrlist="$attrlist __osf__ pdp11 posix" attrlist="$attrlist pyr sinix sony sparc sun tower tower32 tower32_600" attrlist="$attrlist tower32_800 tss u3b2 u3b20 u3b200 u3b5 ultrix unix" attrlist="$attrlist __unix__ vax venix xenix z8000" i_sysselct='' : change the next line if compiling for Xenix/286 on Xenix/386 xlibpth='/usr/lib/386 /lib/386' : general looking path for locating libraries libpth='/usr/lib/large /lib '$xlibpth' /lib/large' libpth=$libpth' /usr/lib/small /lib/small' : no additional library wanted by default libswanted='' CTRLA=`echo a | tr a '\001'` large='' inclwanted='/usr/include/NET-5000' libswanted='malloc bsd socket bsdipc sun net hdb bbn str nls nsl c_s intl x ucb' : Some greps do not return status, grrr. echo "grimblepritz" >grimble if grep blurfldyick grimble >/dev/null 2>&1 ; then contains=contains elif grep grimblepritz grimble >/dev/null 2>&1 ; then contains=grep else contains=contains fi rm -f grimble : the following should work in any shell case "$contains" in contains*) echo " " echo "AGH! Grep doesn't return a status. Attempting remedial action." cat >contains <<'EOSS' grep "$1" "$2" >.greptmp && cat .greptmp && test -s .greptmp EOSS chmod +x contains esac : first determine how to suppress newline on echo command echo " " echo "Checking echo to see how to suppress newlines..." (echo "hi there\c" ; echo " ") >.echotmp if $contains c .echotmp >/dev/null 2>&1 ; then echo "...using -n." n='-n' c='' else cat <<'EOM' ...using \c EOM n='' c='\c' fi echo $n "The star should be here-->$c" echo '*' rm -f .echotmp : Now test for existence of everything in MANIFEST echo " " if test -f $_srcdir/MANIFEST; then echo "First let's make sure your kit is complete. Checking..." >&4 awk '$1 !~ /PACK[A-Z]+/ {print $1}' $_srcdir/MANIFEST | split -50 rm -f missing for filelist in x??; do (cd $_srcdir; ls `cat $objdir/UU/$filelist`) >/dev/null 2>>missing done if test -s missing; then cat missing >&4 cat >&4 <<'EOM' THIS PACKAGE SEEMS TO BE INCOMPLETE. You have the option of continuing the configuration process, despite the distinct possibility that your kit is damaged, by typing 'y'es. If you do, don't blame me if something goes wrong. I advise you to type 'n'o and contact the author (davison@borland.com). EOM echo $n "Continue? [n] $c" >&4 read ans case "$ans" in y*) echo "Continuing..." >&4 rm -f missing ;; *) echo "ABORTING..." >&4 kill $$ ;; esac else echo "Looks good..." >&4 fi else echo "There is no MANIFEST file. I hope your kit is complete !" fi rm -f missing x?? : compute the number of columns on the terminal for proper question formatting case "$COLUMNS" in '') COLUMNS='80';; esac : set up the echo used in my read myecho="case \"\$xxxm\" in '') echo $n \"\$rp $c\" >&4;; *) case \"\$rp\" in '') echo $n \"[\$xxxm] $c\";; *) if test \`echo \"\$rp [\$xxxm] \" | wc -c\` -ge $COLUMNS; then echo \"\$rp\" >&4 echo $n \"[\$xxxm] $c\" >&4 else echo $n \"\$rp [\$xxxm] $c\" >&4 fi ;; esac;; esac" : now set up to do reads with possible shell escape and default assignment cat <myread xxxm=\$dflt $myecho ans='!' case "\$fastread" in yes) case "\$dflt" in '') ;; *) ans=''; case "\$silent-\$rp" in true-) ;; *) echo " " >&4;; esac;; esac;; *) case "\$silent" in true) case "\$rp" in '') ans='';; esac;; esac;; esac while expr "X\$ans" : "X!" >/dev/null; do read answ set x \$xxxm shift aok=''; eval "ans=\"\$answ\"" && aok=y case "\$answ" in "\$ans") case "\$ans" in "!") sh 1>&4 echo " " $myecho ;; !*) set x \`expr "X\$ans" : "X!\(.*\)\$"\` shift sh 1>&4 -c "\$*" echo " " $myecho ;; esac;; *) case "\$aok" in y) echo "*** Substitution done -- please confirm." xxxm="\$ans" ans=\`echo $n "\$ans$c" | tr '\012' ' '\` xxxm="\$ans" ans=! ;; *) echo "*** Error -- try again." ans=! ;; esac $myecho ;; esac case "\$ans\$xxxm\$nostick" in '') ans=! $myecho ;; esac done case "\$ans" in '') ans="\$xxxm";; esac EOSC : create .config dir to save info across Configure sessions test -d ../.config || mkdir ../.config cat >../.config/README </dev/null || whoami) 2>&1` if $contains "^$user\$" ../.config/instruct >/dev/null 2>&1; then firsttime=false echo " " rp='Would you like to see the instructions?' dflt=n . ./myread case "$ans" in [yY]*) ;; *) needman=false;; esac fi if $needman; then cat <>../.config/instruct;; esac fi : see if sh knows # comments echo " " echo "Checking your sh to see if it knows about # comments..." >&4 if sh -c '#' >/dev/null 2>&1 ; then echo "Your sh handles # comments correctly." shsharp=true spitshell=cat echo " " echo "Okay, let's see if #! works on this system..." cat=/usr/bin/cat test -f $cat || cat=/bin/cat echo "#!$cat" >try $eunicefix try chmod +x try ./try > today if test -s today; then echo "It does." sharpbang='#!' else echo "#! /bin/cat" > try $eunicefix try chmod +x try ./try > today if test -s today; then echo "It does." sharpbang='#! ' else echo "It's just a comment." sharpbang=': use ' fi fi else echo "Your sh doesn't grok # comments--I will strip them later on." shsharp=false echo "exec grep -v '^[ ]*#'" >spitshell chmod +x spitshell $eunicefix spitshell spitshell=`pwd`/spitshell echo "I presume that if # doesn't work, #! won't work either!" sharpbang=': use ' fi rm -f try today : figure out how to guarantee sh startup echo " " echo "Checking out how to guarantee sh startup..." >&4 case "$SYSTYPE" in *bsd*|sys5*) startsh=$sharpbang"/$SYSTYPE/bin/sh";; *) startsh=$sharpbang'/bin/sh';; esac echo "Let's see if '$startsh' works..." cat >try <&4 cat <loc $startsh case \$# in 0) exit 1;; esac thing=\$1 shift dflt=\$1 shift for dir in \$*; do case "\$thing" in .) if test -d \$dir/\$thing; then echo \$dir exit 0 fi ;; *) if test -f \$dir/\$thing; then echo \$dir/\$thing exit 0 elif test -f \$dir/\$thing.exe; then : on Eunice apparently echo \$dir/\$thing exit 0 fi ;; esac done echo \$dflt exit 1 EOSC chmod +x loc $eunicefix loc loclist=" awk cat cp echo expr grep mkdir mv rm sed sleep sort tail tr uniq " trylist=" Mcc basename bash cpp csh date diff ed egrep inews ispell ksh less mail metamail mhn more munpack nroff pg rmail sendmail smail spell test uname uuname vi " pth=`echo $PATH | sed -e 's/:/ /g'` pth="$pth /lib /usr/lib" for file in $loclist; do xxx=`./loc $file $file $pth` eval $file=$xxx eval _$file=$xxx case "$xxx" in /*) echo $file is in $xxx. ;; *) echo "I don't know where $file is. I hope it's in everyone's PATH." ;; esac done echo " " echo "Don't worry if any of the following aren't found..." say=offhand for file in $trylist; do xxx=`./loc $file $file $pth` eval $file=$xxx eval _$file=$xxx case "$xxx" in /*) echo $file is in $xxx. ;; *) echo "I don't see $file out there, $say." say=either ;; esac done case "$egrep" in egrep) echo "Substituting grep for egrep." egrep=$grep ;; esac case "$test" in test) echo "Hopefully test is built into your sh." ;; *) if sh -c "PATH= test true" >/dev/null 2>&1; then echo "Using the test built into your sh." test=test _test=test fi ;; esac case "$echo" in echo) echo "Hopefully echo is built into your sh." ;; '') ;; *) echo " " echo "Checking compatibility between $echo and builtin echo (if any)..." >&4 $echo $n "hi there$c" >foo1 echo $n "hi there$c" >foo2 if cmp foo1 foo2 >/dev/null 2>&1; then echo "They are compatible. In fact, they may be identical." else case "$n" in '-n') n='' c='\c';; *) n='-n' c='';; esac cat <$c" $echo "*" fi $rm -f foo1 foo2 ;; esac : Try to determine whether config.sh was made on this system case "$config_sh" in '') myuname=`( ($uname -a) 2>/dev/null || hostname) 2>&1` myuname=`echo $myuname | $sed -e 's/^[^=]*=//' | \ tr '[A-Z]' '[a-z]' | tr '\012' ' '` dflt=n if test "$fastread" = yes; then dflt=y elif test -f ../config.sh; then oldmyuname='' if $contains myuname= ../config.sh >/dev/null 2>&1; then eval "old`grep myuname= ../config.sh`" oldmyuname=`echo $oldmyuname | $sed -e 's/^[^=]*=//' | \ tr '[A-Z]' '[a-z]' | tr '\012' ' '` fi if test "X$myuname" = "X$oldmyuname"; then dflt=y fi fi : Get old answers from old config file if Configure was run on the : same system, otherwise use the hints. hint=default if test -f ../config.sh; then cd .. echo " " rp="I see a config.sh file. Do you want to use it to set the defaults?" . UU/myread case "$ans" in n*|N*) echo "OK, I'll ignore it."; mv config.sh config.sh.old;; *) echo "Fetching default answers from your old config.sh file..." >&4 tmp="$n" tans="$c" . ./config.sh cp config.sh UU n="$tmp" c="$tans" hint=previous ;; esac cd UU fi if test ! -f ../config.sh; then $cat <&4 dflt='' : Half the following guesses are probably wrong... If you have better : tests or hints, please send them to davison@borland.com : The metaconfig authors would also appreciate a copy... $test -f /vmunix && $test -f /usr/convex/spucmd && dflt="$dflt convexos" $test -f /irix && dflt="$dflt sgi" $test -f /xenix && dflt="$dflt sco_xenix" $test -f /dynix && dflt="$dflt dynix" $test -f /dnix && dflt="$dflt dnix" $test -f /bin/mips && /bin/mips && dflt="$dflt mips" $test -f /lib/libc && $test -f /lib/clib && dflt="$dflt domainos" $test -d /NextApps && test -f /usr/adm/software_version && dflt="$dflt next" $test -f Policy.sh && dflt="Policy $dflt" $test -d /usr/include/minix && dflt="$dflt minix" if $test -f $uname; then set X $myuname shift $test -f $5.sh && dflt="$dflt $5" case "$5" in fps*) dflt="$dflt fps";; mips*) case "$4" in umips) dflt="$dflt umips";; *) dflt="$dflt mips";; esac;; [23]100) dflt="$dflt mips";; next*) dflt="$dflt next" ;; news*) dflt="$dflt news" ;; esac case "$1" in aix) dflt="$dflt aix_rs" ;; dnix) dflt="$dflt dnix" ;; dgux) dflt="$dflt dgux" ;; genix) dflt="$dflt genix" ;; hp*ux) dflt="$dflt hpux" ;; next) dflt="$dflt next" ;; irix) dflt="$dflt sgi" ;; convexos) dflt="$dflt convexos";; domainos) dflt="$dflt domainos";; OSF1*alpha) dflt="$dflt dec_osf1";; $2) if $test -f /usr/lib/sysadm/sysadm.menu; then if $test ! -f /etc/copyrights/01.sco; then set 3 2 0 else set `uname -X 2>&1 | grep Release` if test "x$1" != "xRelease"; then set 3 2 1 else set `echo $3 | sed 's/[^0-9]/ /g'` fi fi if $test -f sco_$1_$2_$3.sh; then dflt="$dflt sco_$1_$2_$3" elif $test -f sco_$1_$2.sh; then dflt="$dflt sco_$1_$2" elif $test -f sco_$1.sh; then dflt="$dflt sco_$1" fi fi ;; ultrix) case "$3" in 3*) dflt="$dflt ultrix_3" ;; 4*) dflt="$dflt ultrix_4" ;; esac ;; uts) dflt="$dflt uts" ;; $2) if test -f /etc/systemid; then set `echo $3 | sed 's/\./ /'` $4 if $test -f sco_$1_$2_$3.sh; then dflt="$dflt sco_$1_$2_$3" elif $test -f sco_$1_$2.sh; then dflt="$dflt sco_$1_$2" elif $test -f sco_$1.sh; then dflt="$dflt sco_$1" fi fi ;; esac else if test -f /vmunix -a -f news_os.sh; then (what /vmunix | tr '[A-Z]' '[a-z]') > $objdir/UU/kernel.what 2>&1 if $contains news-os $objdir/UU/kernel.what >/dev/null 2>&1; then dflt="$dflt news_os" fi $rm -f $objdir/UU/kernel.what fi fi set X `echo $dflt | tr ' ' '\012' | sort | uniq` shift dflt='' for file in $*; do if $test -f "$file.sh"; then dflt="$dflt $file" fi done set X $dflt shift dflt=${1+"$@"} case "$dflt" in '') dflt=none;; esac $cat <> $objdir/UU/config.sh elif $test X$ans = X -o X$ans = Xnone ; then : nothing else echo "$file.sh does not exist -- ignored" fi done hint=recommended cd $objdir/UU fi ;; *) echo " " echo "Fetching default answers from $config_sh..." >&4 tmp="$n" tans="$c" cd .. cp $config_sh config.sh 2>/dev/null . ./config.sh cd UU cp ../config.sh . n="$tmp" c="$tans" hint=previous ;; esac : Restore computed paths for file in $loclist $trylist srcdir vpath; do eval $file="\$_$file" done : include path for Make if test "X$vpath" = "X." ; then vincludes= else vincludes="-I. -I$srcdir" fi : who configured the system cf_time=`$date 2>&1` cf_by=`( (logname) 2>/dev/null || whoami) 2>&1` : set up shell script to do ~ expansion (CONFIG=true . $srcdir/filexp.SH) >/dev/null : check if NNTP is to be used case "$d_nntp" in "$define") dflt="y";; *) dflt="n";; esac $echo " " rp="Do you want to access news via NNTP?" . ./myread case "$ans" in n*) d_nntp="$undef" d_xdata="$undef" d_genauth="$undef";; y*) d_nntp="$define" case "$d_genauth" in define) dflt="y";; *) dflt="n";; esac $cat < .falseactive case "$servername" in '') dflt="no default" ;; *) dflt="$servername";; esac $cat <getfile tilde='' fullpath='' already='' skip='' none_ok='' exp_file='' nopath_ok='' orig_rp="$rp" orig_dflt="$dflt" case "$fn" in *:*) loc_file=`expr $fn : '.*:\(.*\)'` fn=`expr $fn : '\(.*\):.*'` ;; esac case "$fn" in *~*) tilde=true;; esac case "$fn" in */*) fullpath=true;; esac case "$fn" in *+*) skip=true;; esac case "$fn" in *n*) none_ok=true;; esac case "$fn" in *e*) exp_file=true;; esac case "$fn" in *p*) nopath_ok=true;; esac case "$fn" in *f*) type='File';; *d*) type='Directory';; *l*) type='Locate';; esac what="$type" case "$what" in Locate) what='File';; esac case "$exp_file" in '') case "$d_portable" in "$define") ;; *) exp_file=true;; esac ;; esac cd .. while test "$type"; do redo='' rp="$orig_rp" dflt="$orig_dflt" case "$tilde" in true) rp="$rp (~name ok)";; esac . UU/myread case "$ans" in none) value='' ansexp='' case "$none_ok" in true) type='';; esac ;; *) case "$tilde" in '') value="$ans" ansexp="$ans";; *) value=`UU/filexp $ans` case $? in 0) if test "$ans" != "$value"; then echo "(That is $value on this particular system.)" fi ;; *) value="$ans";; esac ansexp="$value" case "$exp_file" in '') value="$ans";; esac ;; esac case "$fullpath" in true) case "$ansexp" in /*) value="$ansexp" ;; *) redo=true case "$already" in true) echo "I shall only accept a full path name, as in /bin/ls." >&4 echo "Use a ! shell escape if you wish to check pathnames." >&4 ;; *) echo "Please give a full path name, starting with slash." >&4 case "$tilde" in true) echo "Note that using ~name is ok provided it expands well." >&4 already=true ;; esac esac ;; esac ;; esac case "$redo" in '') case "$type" in File) if test -f "$ansexp"; then type='' elif test -r "$ansexp" || (test -h "$ansexp") >/dev/null 2>&1 then echo "($value is not a plain file, but that's ok.)" type='' fi ;; Directory) if test -d "$ansexp"; then type='' fi ;; Locate) if test -d "$value"; then echo "(Looking for $loc_file in directory $value.)" value="$value/$loc_file" fi if test -f "$value"; then type='' fi case "$nopath_ok" in true) case "$value" in */*) ;; *) echo "Assuming $value will be in people's path." type='' ;; esac ;; esac ;; esac case "$skip" in true) type=''; esac case "$type" in '') ;; *) if test "$fastread" = yes; then dflt=y else dflt=n fi rp="$what $value doesn't exist. Use that name anyway?" . UU/myread dflt='' case "$ans" in y*) type='';; *) echo " ";; esac ;; esac ;; esac ;; esac done cd UU ans="$value" rp="$orig_rp" dflt="$orig_dflt" EOSC : figure out news library case "$d_nntp" in define) newslib=/tmp newslibexp=/tmp ;; *) case "$newslib" in '') dflt=/usr/lib/news;; *) dflt=$newslib;; esac $echo " " fn=d~ rp='Where is the news library directory?' . ./getfile newslib="$ans" newslibexp=`./filexp $newslib` if $test -f $newslibexp/inews; then $echo "Aha! Inews is really in $newslibexp!" >&4 case "$inews" in inews) : null;; *) $echo "(Make sure $inews isn't an old version.)";; esac inews=$newslibexp/inews fi esac case "$inewsloc" in '') dflt="$inews";; *) dflt="$inewsloc";; esac $echo " " fn=pl:inews rp='Which inews should be used for posting articles?' . ./getfile inewsloc="$ans" : locate active file and active.times file $echo " " case "$d_nntp" in define) active=none myactive=.falseactive : check for active.times case "$acttimes" in ''|none) dflt="n";; *) dflt="y";; esac cat <<'EOM' If your server maintains an accurate "active.times" file, trn can make use of the NEWGROUPS call in NNTP to find new groups instead of using the old method of adding all groups not listed in your .newsrc. If you aren't sure, guess and see if you get notified of the new groups as they are created. EOM rp="Does your server support a reliable NEWGROUPS call?" . ./myread case "$ans" in y*) acttimes="nntp" d_acttimes="$define";; *) acttimes='none' d_acttimes="$undef";; esac ;; *) case "$active" in '') dflt=$newslib/active ;; *) dflt="$active";; esac fn=l~:active rp='Where is the active file?' . ./getfile active="$ans" myactive=`./filexp $active` : check for active.times case "$acttimes" in ''|nntp) dflt="${active}.times";; *) dflt="$acttimes";; esac $cat <<'EOM' If your news system maintains an "active.times" file, trn can use it for a fast new-group check. If it's not around, answer "none" and the active file will be scanned for new groups when it changes size. EOM fn=ln~:active.times rp='The full pathname of active.times or "none"?' . ./getfile acttimes="$ans" case "$acttimes" in '') acttimes=none d_acttimes="$undef";; *) d_acttimes="$define";; esac esac : make some quick guesses about what we are up against echo " " $echo $n "Hmm... $c" echo exit 1 >bsd echo exit 1 >usg echo exit 1 >v7 echo exit 1 >osf1 echo exit 1 >eunice echo exit 1 >xenix echo exit 1 >venix d_bsd="$undef" $cat /usr/include/signal.h /usr/include/sys/signal.h >foo 2>/dev/null if test -f /osf_boot || $contains 'OSF/1' /usr/include/ctype.h >/dev/null 2>&1 then echo "Looks kind of like an OSF/1 system, but we'll see..." echo exit 0 >osf1 elif test `echo abc | tr a-z A-Z` = Abc ; then xxx=`./loc addbib blurfl $pth` if $test -f $xxx; then echo "Looks kind of like a USG system with BSD features, but we'll see..." echo exit 0 >bsd echo exit 0 >usg else if $contains SIGTSTP foo >/dev/null 2>&1 ; then echo "Looks kind of like an extended USG system, but we'll see..." else echo "Looks kind of like a USG system, but we'll see..." fi echo exit 0 >usg fi elif $contains SIGTSTP foo >/dev/null 2>&1 ; then echo "Looks kind of like a BSD system, but we'll see..." d_bsd="$define" echo exit 0 >bsd else echo "Looks kind of like a Version 7 system, but we'll see..." echo exit 0 >v7 fi case "$eunicefix" in *unixtovms*) $cat <<'EOI' There is, however, a strange, musty smell in the air that reminds me of something...hmm...yes...I've got it...there's a VMS nearby, or I'm a Blit. EOI echo exit 0 >eunice d_eunice="$define" : it so happens the Eunice I know will not run shell scripts in Unix format ;; *) echo " " echo "Congratulations. You aren't running Eunice." d_eunice="$undef" ;; esac if test -f /xenix; then echo "Actually, this looks more like a XENIX system..." echo exit 0 >xenix d_xenix="$define" else echo " " echo "It's not Xenix..." d_xenix="$undef" fi chmod +x xenix $eunicefix xenix if test -f /venix; then echo "Actually, this looks more like a VENIX system..." echo exit 0 >venix else echo " " if xenix; then : null else echo "Nor is it Venix..." fi fi chmod +x bsd usg v7 osf1 eunice xenix venix $eunicefix bsd usg v7 osf1 eunice xenix venix $rm -f foo : find out how to find out full name case "$d_berknames" in "$define") dflt=y;; "$undef") dflt=n;; *) if bsd; then dflt=y elif xenix; then dflt=y else dflt=n fi ;; esac $cat <<'EOM' Does your /etc/passwd file keep full names in Berkeley/V7 format (name first thing after ':' in GCOS field)? In that case, a typical entry in the password file looks like this: guest:**paswword**:10:100:Mister Guest User:/usr/users:/bin/sh ^^^^^^^^^^^^^^^^^ EOM rp="Berkeley/V7 format for full name in /etc/passwd?" . ./myread case "$ans" in y*) d_passnames="$define" d_berknames="$define" d_usgnames="$undef" nametype=bsd ;; *) case "$d_usgnames" in "$define") dflt=y;; "$undef") dflt=n;; *) if usg; then dflt=y else dflt=n fi ;; esac $cat <<'EOM' Does your passwd file keep full names in USG format (name sandwiched between a '-' and a '(')? In that case, a typical entry in the password file looks like this: guest:**paswword**:10:100:000-Mister Guest User(000):/usr/users:/bin/sh ^^^^^^^^^^^^^^^^^ EOM rp="USG format for full name in /etc/passwd?" . ./myread case "$ans" in n*) echo "Full name will be taken from ~/.fullname" d_passnames="$undef" d_berknames="$undef" d_usgnames="$undef" nametype=other ;; *) d_passnames="$define" d_berknames="$undef" d_usgnames="$define" nametype=usg ;; esac;; esac : determine where manual pages are on this system echo " " case "$sysman" in '') syspath='/usr/man/man1 /usr/man/man1 /usr/man/mann' syspath="$syspath /usr/man/manl /usr/man/local/man1" syspath="$syspath /usr/man/u_man/man1 /usr/share/man/man1" syspath="$syspath /usr/catman/u_man/man1 /usr/man/l_man/man1" syspath="$syspath /usr/local/man/u_man/man1 /usr/local/man/l_man/man1" syspath="$syspath /usr/man/man.L /local/man/man1" sysman=`./loc . $syspath` ;; esac if $test -d "$sysman"; then echo "System manual is in $sysman." >&4 else echo "Could not find manual pages in source form." >&4 fi : see if we need a special compiler echo " " if usg; then case "$cc" in '') case "$Mcc" in /*) dflt='Mcc';; *) case "$large" in -M*) dflt='cc';; *) if $contains '\-M' $sysman/cc.1 >/dev/null 2>&1 ; then if $contains '\-M' $sysman/cpp.1 >/dev/null 2>&1; then dflt='cc' else dflt='cc -M' fi else dflt='cc' fi;; esac;; esac;; *) dflt="$cc";; esac $cat <<'EOM' On some systems the default C compiler will not resolve multiple global references that happen to have the same name. On some such systems the "Mcc" command may be used to force these to be resolved. On other systems a "cc -M" command is required. (Note that the -M flag on other systems indicates a memory model to use!) If you have the Gnu C compiler, you might wish to use that instead. EOM rp="What command will force resolution on this system?" . ./myread cc="$ans" else case "$cc" in '') dflt=cc;; *) dflt="$cc";; esac rp="Use which C compiler?" . ./myread cc="$ans" fi case "$cc" in gcc*) cpp=`./loc gcc-cpp $cpp $pth`;; esac : What should the include directory be ? echo " " $echo $n "Hmm... $c" dflt='/usr/include' incpath='' mips_type='' if $test -f /bin/mips && /bin/mips; then echo "Looks like a MIPS system..." $cat >usr.c <<'EOCP' #ifdef SYSTYPE_BSD43 /bsd43 #endif EOCP if $cc -E usr.c > usr.out && $contains / usr.out >/dev/null 2>&1; then dflt='/bsd43/usr/include' incpath='/bsd43' mips_type='BSD 4.3' else mips_type='System V' fi $rm -f usr.c usr.out echo "and you're compiling with the $mips_type compiler and libraries." else echo "Doesn't look like a MIPS system." echo "exit 1" >mips chmod +x mips $eunicefix mips fi echo " " case "$usrinc" in '') ;; *) dflt="$usrinc";; esac fn=d/ rp='Where are the include files you want to use?' . ./getfile usrinc="$ans" : Set private lib path case "$plibpth" in '') if mips; then plibpth="$incpath/usr/lib /usr/local/lib /usr/ccs/lib" else plibpth="/usr/ccs/lib /usr/lib /usr/ucblib /usr/local/lib" fi;; esac libpth="$plibpth $libpth" : Looking for optional libraries echo " " echo "Checking for optional libraries..." >&4 case "$libs" in ' '|'') dflt='';; *) dflt="$libs";; esac case "$libswanted" in '') libswanted='c_s';; esac for thislib in $libswanted; do case "$thislib" in dbm) thatlib=ndbm;; *_s) thatlib=NONE;; *) thatlib="${thislib}_s";; *) thatlib=NONE;; esac yyy="$incpath/usr/ccs/lib $incpath/usr/lib $incpath/usr/ucblib" yyy="$yyy $incpath/usr/local/lib $incpath/lib" xxx=`./loc lib$thislib.a X $yyy` if $test -f $xxx; then echo "Found -l$thislib." case "$dflt" in *-l$thislib*|*-l$thatlib*);; *) dflt="$dflt -l$thislib";; esac else xxx=`./loc lib$thislib.a X $libpth` if $test -f $xxx; then echo "Found $xxx." case "$dflt" in *$xxx*);; *) dflt="$dflt $xxx";; esac else xxx=`./loc Slib$thislib.a X $xlibpth` if $test -f $xxx; then echo "Found -l$thislib." case "$dflt" in *-l$thislib*|*-l$thatlib*);; *) dflt="$dflt -l$thislib";; esac else xxx=`./loc lib$thislib.so X $libpth` if $test -f $xxx; then echo "Found -l$thislib." case "$dflt" in *-l$thislib*|*-l$thatlib*);; *) dflt="$dflt -l$thislib";; esac else echo "No -l$thislib." fi fi fi fi done set X $dflt shift dflt="$*" case "$libs" in '') dflt="$dflt";; *) dflt="$libs";; esac case "$dflt" in ' '|'') dflt='none';; esac $cat </dev/null 2>&1; then echo "Your C library seems to be in $libc, as you said before." elif $test -r $incpath/usr/lib/libc.a; then libc=$incpath/usr/lib/libc.a; echo "Your C library seems to be in $libc. That's fine." elif $test -r /lib/libc.a; then libc=/lib/libc.a; echo "Your C library seems to be in $libc. You're normal." else if tans=`./loc libc.a blurfl/dyick $libpth`; $test -r "$tans"; then : elif tans=`./loc libc blurfl/dyick $libpth`; $test -r "$tans"; then libnames="$libnames "`./loc clib blurfl/dyick $libpth` elif tans=`./loc clib blurfl/dyick $libpth`; $test -r "$tans"; then : elif tans=`./loc Slibc.a blurfl/dyick $xlibpth`; $test -r "$tans"; then : elif tans=`./loc Mlibc.a blurfl/dyick $xlibpth`; $test -r "$tans"; then : else tans=`./loc Llibc.a blurfl/dyick $xlibpth` fi if $test -r "$tans"; then echo "Your C library seems to be in $tans, of all places." libc=$tans else libc='blurfl' fi fi if $test $xxx = apollo -o -r "$libc" || (test -h "$libc") >/dev/null 2>&1; then dflt="$libc" cat < libpath cat >&4 < libnames set X `cat libnames` shift xxx=files case $# in 1) xxx=file; esac echo "Extracting names from the following $xxx for later perusal:" >&4 echo " " $sed 's/^/ /' libnames >&4 echo " " $echo $n "This may take a while...$c" >&4 nm $nm_opt $* 2>/dev/null >libc.tmp $echo $n ".$c" $grep fprintf libc.tmp > libc.ptf xscan='eval "libc.list"; $echo $n ".$c" >&4' xrun='eval "libc.list"; echo "done" >&4' if com="$sed -n -e 's/^.* [ADTSI] *_[_.]*//p' -e 's/^.* [ADTSI] //p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^__*//' -e 's/^\([a-zA-Z_0-9$]*\).*xtern.*/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e '/|UNDEF/d' -e '/FUNC..GL/s/^.*|__*//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^.* D __*//p' -e 's/^.* D //p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^_//' -e 's/^\([a-zA-Z_0-9]*\).*xtern.*text.*/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^.*|FUNC |GLOB .*|//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$grep '|' | $sed -n -e '/|COMMON/d' -e '/|DATA/d' \ -e '/ file/d' -e 's/^\([^ ]*\).*/\1/p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^.*|FUNC |GLOB .*|//p' -e 's/^.*|FUNC |WEAK .*|//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun elif com="$sed -n -e 's/^__//' -e '/|Undef/d' -e '/|Proc/s/ .*//p'";\ eval $xscan;\ $contains '^fprintf$' libc.list >/dev/null 2>&1; then eval $xrun else nm -p $* 2>/dev/null >libc.tmp com="$sed -n -e 's/^.* [ADTSI] *_[_.]*//p' -e 's/^.* [ADTSI] //p'";\ eval "libc.list" if $contains '^fprintf$' libc.list >/dev/null 2>&1; then nm_opt='-p' echo "done" >&4 else echo " " echo "nm didn't seem to work right. Trying ar instead..." >&4 com='' if ar t $libc > libc.tmp; then for thisname in $libnames; do ar t $thisname >>libc.tmp done $sed -e 's/\.o$//' < libc.tmp > libc.list echo "Ok." >&4 else echo "ar didn't seem to work right." >&4 echo "Maybe this is a Cray...trying bld instead..." >&4 if bld t $libc | $sed -e 's/.*\///' -e 's/\.o:.*$//' > libc.list; then for thisname in $libnames; do bld t $libnames | \ $sed -e 's/.*\///' -e 's/\.o:.*$//' >>libc.list ar t $thisname >>libc.tmp done echo "Ok." >&4 else echo "That didn't work either. Giving up." >&4 exit 1 fi fi fi fi nm_extract="$com" if $test -f /lib/syscalls.exp; then echo " " echo "Also extracting names from /lib/syscalls.exp for good ole AIX..." >&4 $sed -n 's/^\([^ ]*\)[ ]*syscall$/\1/p' /lib/syscalls.exp >>libc.list fi ;; esac $rm -f libnames libpath : determine optimize, if desired, or use for debug flag also case "$optimize" in ' ') dflt="none";; '') dflt="-O";; *) dflt="$optimize";; esac $cat </dev/null 2>&1 then dflt="$dflt -posix" fi ;; esac ;; *) dflt="$ccflags" ;; esac case "$mips_type" in *BSD*) ;; '') ;; *) inclwanted="$inclwanted $usrinc/bsd";; esac for thisincl in $inclwanted; do if $test -d $thisincl; then if $test x$thisincl != x$usrinc; then case "$dflt" in *$thisincl*);; *) dflt="$dflt -I$thisincl";; esac fi fi done inctest='if $contains $2 $usrinc/$1 >/dev/null 2>&1; then xxx=true; elif $contains $2 $usrinc/sys/$1 >/dev/null 2>&1; then xxx=true; else xxx=false; fi; if $xxx; then case "$dflt" in *$2*);; *) dflt="$dflt -D$2";; esac; fi' if ./osf1; then set signal.h __LANGUAGE_C__; eval $inctest else set signal.h LANGUAGE_C; eval $inctest fi case "$cc" in *gcc) ;; *) set signal.h NO_PROTOTYPE; eval $inctest set signal.h _NO_PROTO; eval $inctest ;; esac case "$dflt" in '') dflt=none;; esac $cat <whoa dflt=y echo " " echo "*** WHOA THERE!!! ***" >&4 echo " The $hint value for \$$var on this machine was \"$was\"!" >&4 rp=" Keep the $hint value?" . ./myread case "$ans" in y) td=$was; tu=$was;; esac EOSC : define an is-in-libc? function inlibc='echo " "; td=$define; tu=$undef; sym=$1; var=$2; eval "was=\$$2"; tx=yes; case "$reuseval$was" in true) ;; true*) tx=no;; esac; case "$tx" in yes) set $sym tres -f; eval $csym; case "$tres" in true) echo "$sym() found." >&4; case "$was" in $undef) . whoa; esac; eval "$var=\$td";; *) echo "$sym() NOT found." >&4; case "$was" in $define) . whoa; esac; eval "$var=\$tu";; esac;; *) case "$was" in $define) echo "$sym() found." >&4;; *) echo "$sym() NOT found." >&4;; esac;; esac' : function used to set $1 to $val setvar='var=$1; eval "was=\$$1"; td=$define; tu=$undef; case "$val$was" in $define$undef) . whoa; eval "$var=\$td";; $undef$define) . whoa; eval "$var=\$tu";; *) eval "$var=$val";; esac' : see if ftime exists set ftime d_ftime eval $inlibc : see if getwd or getcwd exists set getwd d_getwd; eval $inlibc case "$d_getwd" in $define) val=$undef; set d_getcwd; eval $setvar ;; *) set getcwd d_getcwd; eval $inlibc ;; esac : now get the host name that appears in news articles $echo " " $echo "Figuring out local host name..." cont=true $echo 'Maybe "hostname" will work...' if ans=`sh -c hostname 2>&1` ; then thishost=$ans phostcmd=hostname cont='' fi if $test "$cont"; then if xenix; then $echo 'Oh, dear. Maybe "/etc/systemid" is the key...' if ans=`cat /etc/systemid 2>&1` ; then thishost=$ans phostcmd='cat /etc/systemid' $echo "Whadyaknow. Xenix always was a bit strange..." cont='' fi elif $test -r /etc/systemid; then $echo "(What is a non-Xenix system doing with /etc/systemid?)" fi fi if $test "$cont"; then $echo 'No, maybe "uuname -l" will work...' if ans=`sh -c 'uuname -l' 2>&1` ; then thishost=$ans phostcmd='uuname -l' else $echo 'Strange. Maybe "uname -n" will work...' if ans=`sh -c 'uname -n' 2>&1` ; then thishost=$ans phostcmd='uname -n' else $echo 'Oh well, maybe I can mine it out of whoami.h...' if ans=`sh -c $contains' sysname $usrinc/whoami.h' 2>&1` ; then thishost=`$echo "$ans" | $sed 's/^.*"\(.*\)"/\1/'` phostcmd="sed -n -e '"'/sysname/s/^.*\"\\(.*\\)\"/\1/{'"' -e p -e q -e '}' <$usrinc/whoami.h" else phostcmd='' case "$phost" in '') $echo "Does this machine have an identity crisis or something?" ;; *) $echo "Well, you said $phost before..." thishost=$phost ;; esac fi fi fi fi : you do not want to know about this set $thishost thishost=$1 echo "Your local hostname was computed to be '$thishost'." : translate upper to lower if necessary case "$thishost" in *[A-Z]*) thishost=`$echo $thishost | tr '[A-Z]' '[a-z]'` ;; esac case "$phost" in '') dflt="$thishost" case "$dflt" in *.*) ;; *) dflt="$dflt.UUCP";; esac ;; *) dflt="$phost";; esac $cat <&4 d_gethname="$define" ans=gethostname elif set uname val -f d_uname; eval $csym; $val; then if xenix; then $cat <<'EOM' uname() was found, but you're running xenix, and older versions of xenix have a broken uname(). If you don't really know whether your xenix is old enough to have a broken system call, use the default answer. EOM dflt=y case "$d_uname" in "$define") dflt=n;; esac rp='Is your uname() broken?' . ./myread case "$ans" in n*) d_uname="$define"; ans=uname;; esac else echo 'uname() found.' >&4 d_uname="$define" ans=uname fi fi case "$d_gethname" in '') d_gethname="$undef";; esac case "$d_uname" in '') d_uname="$undef";; esac case "$phostcmd" in '') aphostcmd='';; *) case "$aphostcmd" in /*) ;; *) set X $phostcmd shift file=$1 shift file=`loc $file $file $pth` aphostcmd=`echo $file $*` ;; esac ;; esac case "$d_uname$d_gethname" in *define*) ;; *) case "$phostcmd" in '') ;; *) $cat <&4 val="$define" else echo "No getpwent() found -- will use getpw() instead." >&4 val="$undef" fi set d_getpwent eval $setvar : where do we get termlib routines from echo " " xxx=`./loc libcurses.a x $libpth` case "$xxx" in /*) ar t $xxx >grimble if $contains tputs.o grimble >/dev/null 2>&1; then termlib='-lcurses' d_havetlib="$define" echo "Terminfo library found." >&4 elif $test "x`$uname 2>/dev/null`" = xAIX; then # Ok, do the AIX shr.o fun thing /usr/ccs/bin/nm -en $xxx 2>/dev/null >grimble if $contains '^tputs .*|extern|' grimble >/dev/null 2>&1; then termlib='-lcurses' d_havetlib="$define" echo "AIX Terminfo library found." >&4 else xxx=x fi else xxx=x fi rm -f grimble ;; esac case "$xxx" in x) xxx=`./loc libtermlib.a x $libpth` case "$xxx" in /usr/lib*|/lib*) termlib='-ltermlib' d_havetlib="$define" echo "Termlib library found." >&4 ;; /*) termlib="$xxx" d_havetlib="$define" echo "Termlib library found." >&4 ;; *) xxx=`./loc libtermcap.a x $libpth` case "$xxx" in /usr/lib*|/lib*) termlib='-ltermcap' d_havetlib="$define" echo "Termcap library found." >&4 ;; /*) termlib="$xxx" d_havetlib="$define" echo "Termcap library found." >&4 ;; *) case "$termlib" in '') dflt=y rp="Your system appears to NOT have termlib-style routines. Is this true?" . ./myread case "$ans" in n*|f*) d_havetlib="$define" $cat <<'EOM' Then where are the termlib-style routines kept? Specify either -llibname or a full pathname (~name ok). EOM dflt='' rp='Specify termlib path:' . ./myread termlib=`./filexp $ans` ;; *) d_havetlib="$undef" termlib='' echo "You will have to play around with term.c then." >&4 ;; esac echo " ";; *) echo "You said termlib was $termlib before." >&4;; esac;; esac;; esac;; esac : Decide whether to ignore the ORGANIZATION environment variable case "$d_ignoreorg" in "$define") dflt=y ;; *) dflt=n ;; esac cat <&4 cat <<'EOT' >testcpp.c #define ABC abc #define XYZ xyz ABC.XYZ EOT cd .. echo 'cat >.$$.c; '"$cc"' -E ${1+"$@"} .$$.c; rm .$$.c' >cppstdin chmod 755 cppstdin wrapper=`pwd`/cppstdin ok='false' cd UU if $test "X$cppstdin" != "X" && \ $cppstdin $cppminus testcpp.out 2>&1 && \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 then echo "You used to use $cppstdin $cppminus so we'll use that again." case "$cpprun" in '') echo "But let's see if we can live without a wrapper..." ;; *) if $cpprun $cpplast testcpp.out 2>&1 && \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 then echo "(And we'll use $cpprun $cpplast to preprocess directly.)" ok='true' else echo "(However, $cpprun $cpplast does not work, let's see...)" fi ;; esac else case "$cppstdin" in '') ;; *) echo "Good old $cppstdin $cppminus does not seem to be of any help..." ;; esac fi if $ok; then : nothing elif echo 'Maybe "'"$cc"' -E" will work...'; \ $cc -E testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "Yup, it does." x_cpp="$cc -E" x_minus=''; elif echo 'Nope...maybe "'"$cc"' -E -" will work...'; \ $cc -E - testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "Yup, it does." x_cpp="$cc -E" x_minus='-'; elif echo 'Nope...maybe "'"$cc"' -P" will work...'; \ $cc -P testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "Yipee, that works!" x_cpp="$cc -P" x_minus=''; elif echo 'Nope...maybe "'"$cc"' -P -" will work...'; \ $cc -P - testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "At long last!" x_cpp="$cc -P" x_minus='-'; elif echo 'No such luck, maybe "'$cpp'" will work...'; \ $cpp testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "It works!" x_cpp="$cpp" x_minus=''; elif echo 'Nixed again...maybe "'$cpp' -" will work...'; \ $cpp - testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "Hooray, it works! I was beginning to wonder." x_cpp="$cpp" x_minus='-'; elif echo 'Uh-uh. Time to get fancy. Trying a wrapper...'; \ $wrapper testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then x_cpp="$wrapper" x_minus='' echo "Eureka!" else dflt='' rp="No dice. I can't find a C preprocessor. Name one:" . ./myread x_cpp="$ans" x_minus='' $x_cpp testcpp.out 2>&1 if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then echo "OK, that will do." >&4 else echo "Sorry, I can't get that to work. Go find one and rerun Configure." >&4 exit 1 fi fi case "$ok" in false) cppstdin="$x_cpp" cppminus="$x_minus" cpprun="$x_cpp" cpplast="$x_minus" set X $x_cpp shift case "$1" in "$cpp") echo "Perhaps can we force $cc -E using a wrapper..." if $wrapper testcpp.out 2>&1; \ $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 then echo "Yup, we can." cppstdin="$wrapper" cppminus=''; else echo "Nope, we'll have to live without it..." fi ;; esac case "$cpprun" in "$wrapper") cpprun='' cpplast='' ;; esac ;; esac case "$cppstdin" in "$wrapper") ;; *) $rm -f $wrapper;; esac $rm -f testcpp.c testcpp.out : determine filename position in cpp output echo " " echo "Computing filename position in cpp output for #include directives..." >&4 echo '#include ' > foo.c $cat >fieldn </dev/null | \ $grep '^[ ]*#.*stdio\.h' | \ while read cline; do pos=1 set \$cline while $test \$# -gt 0; do if $test -r \`echo \$1 | $tr -d '"'\`; then echo "\$pos" exit 0 fi shift pos=\`expr \$pos + 1\` done done EOF chmod +x fieldn fieldn=`./fieldn` $rm -f foo.c fieldn case $fieldn in '') pos='???';; 1) pos=first;; 2) pos=second;; 3) pos=third;; *) pos="${fieldn}th";; esac echo "Your cpp writes the filename in the $pos field of the line." : locate header file $cat >findhdr <" > foo\$\$.c $cppstdin $cppminus $cppflags < foo\$\$.c 2>/dev/null | \ $grep "^[ ]*#.*\$wanted" | \ while read cline; do name=\`echo \$cline | $awk "\$awkprg" | $tr -d '"'\` case "\$name" in */\$wanted) echo "\$name"; exit 0;; *) name='';; esac; done; $rm -f foo\$\$.c; case "\$name" in '') exit 1;; esac EOF chmod +x findhdr : see if there are directory access routines out there echo " " if $test `./findhdr ndir.h` && \ ( $test -r /usr/lib/libndir.a || $test -r /usr/local/lib/libndir.a ); then echo "Ndir library found." >&4 if $test -r /usr/lib/libndir.a; then libndir='-lndir' else libndir="/usr/local/lib/libndir.a" fi d_libndir="$define" d_usendir="$undef" ndirc='' ndiro='' else libndir='' d_libndir="$undef" if set readdir val -f; eval $csym; $val; then echo "No ndir library found, but you have readdir() so we'll use that." >&4 d_usendir="$undef" ndirc='' ndiro='' else echo "No ndir library found--using ./ndir.c." >&4 d_usendir="$define" ndirc='ndir.c' ndiro='ndir.o' fi fi : see if memcmp exists set memcmp d_memcmp eval $inlibc : see if memcpy exists set memcpy d_memcpy eval $inlibc : see if memset exists set memset d_memset eval $inlibc : see if they want mime display processing case "$d_mimeshow" in '') if $test "$metamail" != metamail; then dflt="$metamail -e -p -m trn %A" elif $test "$mhn" != mhn; then dflt="$mhn -show -serialonly %A" else dflt=none fi ;; "$define") dflt="$mimeshow";; *) dflt=none;; esac $cat </dev/null 2>&1 ; then dflt=news elif $contains "^usenet:" /etc/passwd >/dev/null 2>&1 ; then dflt=usenet elif eunice; then dflt=system else dflt=root fi ;; *) dflt="$newsadmin" ;; esac $cat <<'EOM' Many systems keep their news in a private directory, or have a non-superuser in charge of administering news. (If you don't have such a user, take the default answer.) I need the login name (not directory) which is used for news administration. EOM rp="News admin login?" . ./myread newsadmin="$ans" case "$newsadmin" in root) val="$undef" ;; *) val="$define" ;; esac set d_newsadm eval $setvar : check for buffering of stdout echo " " case "$d_nolnbuf" in '') $cat <<'EOT' >blurfl.c #include main() { int i; for (i = 0; i < 5; i++) { putchar(i+'a'); sleep(1); } putchar('\n'); } EOT $cc blurfl.c -o blurfl >/dev/null 2>&1; $rm -f blurfl.c $cat >&4 <<'EOM' Checking for buffering of stdout to terminal. Observe the following characters as they are printed out, to see whether they print out all at once, or with a 1 second pause between each of them. If they print out one by one, you don't have buffering. If they print together (after about a 5 second pause), you do have buffering. EOM dflt='Type return to start printing the test characters' rp='' . ./myread xxx=y while $test "$xxx" = 'y'; do ./blurfl 1>&4 dflt=n rp='Would you like to see that again?' . ./myread case "$ans" in [yY]*) xxx="y";; *) xxx="n";; esac done dflt=y rp="Do you have buffering (printed all at once)?" . ./myread case "$ans" in n*) val="$define";; *) val="$undef";; esac ;; *) case "$d_nolnbuf" in "$define") dflt=n;; *) dflt=y;; esac rp="Do you have buffering on stdout to terminals?" . ./myread case "$ans" in n*) val="$define";; *) val="$undef";; esac ;; esac set d_nolnbuf eval $setvar : see if we need -ljobs and if we have sigset, etc. echo " " if $test -r /usr/lib/libjobs.a || $test -r /usr/local/lib/libjobs.a ; then echo "Jobs library found." >&4 val="$undef" jobslib='-ljobs' else if bsd; then echo "No jobs library found. (I suppose this is at least 4.2...)" >&4 else echo "No jobs library found. (That's okay, we all have our faults.)" >&4 fi val="$define" jobslib='' fi set d_normsig eval $setvar : check for void type echo " " $echo "Checking to see how well your C compiler groks the void type..." $cat >void.c <<'EOCP' void main(){;} EOCP if $cc -c void.c >/dev/null 2>&1 ; then d_novoid="$undef" $echo "Yup, it does." else d_novoid="$define" $echo "Nope, it doesn't (boo hiss). I will substitute int." fi $rm void.* : see if rdchk exists set rdchk d_rdchk eval $inlibc : see if rename exists set rename d_rename eval $inlibc : see if sigblock exists set sigblock d_sigblock eval $inlibc : see if sighold exists set sighold d_sighold eval $inlibc : see if we need to define size_t if $contains 'size_t' /usr/include/string*.h >/dev/null 2>&1 ; then d_sizet="$undef" else d_sizet="$define" fi : see if strcasecmp exists set strcasecmp d_strccmp eval $inlibc : see which of string.h or strings.h is needed echo " " strings=`./findhdr string.h` if $test "$strings" && $test -r "$strings"; then echo "Using instead of ." >&4 val="$define" else val="$undef" strings=`./findhdr strings.h` if $test "$strings" && $test -r "$strings"; then echo "Using instead of ." >&4 else echo "No string header found -- You'll surely have problems." >&4 fi fi set i_string eval $setvar case "$i_string" in "$undef") strings=`./findhdr strings.h`;; *) strings=`./findhdr string.h`;; esac : index or strchr echo " " if set index val -f; eval $csym; $val; then if set strchr val -f d_strchr; eval $csym; $val; then if $contains strchr "$strings" >/dev/null 2>&1 ; then val="$define" echo "strchr() found." >&4 else val="$undef" echo "index() found." >&4 fi else val="$undef" echo "index() found." >&4 fi else if set strchr val -f d_strchr; eval $csym; $val; then val="$define" echo "strchr() found." >&4 else echo "No index() or strchr() found!" >&4 val="$undef" fi fi set d_strchr eval $setvar : see if strftime exists case "$strftimec" in '') ;; *) d_strftime="$undef";; esac set strftime d_strftime eval $inlibc case "$d_strftime" in "$undef") case "$strftimec" in ' ') dflt='none';; '') dflt="./strftime.c";; *) dflt="$strftimec";; esac $cat <&4 echo " " >&4 $cat >&4 $newslibexp/distributions echo " " >&4 fi echo "Use 'none' for any distributions you don't have." echo " " case "$locdist" in '') dflt="none";; *) dflt="$locdist";; esac rp="What is the distribution code for your local organization?" . ./myread locdist="$ans" case "$orgdist" in '') dflt="none";; *) dflt="$orgdist";; esac rp="What is the distribution code for your organization?" . ./myread orgdist="$ans" case "$citydist" in '') dflt="none";; *) dflt="$citydist";; esac rp="What is the distribution code for your city?" . ./myread citydist="$ans" case "$statedist" in '') dflt="none";; *) dflt="$statedist";; esac rp="What is the distribution code for your state/province?" . ./myread statedist="$ans" case "$multistatedist" in '') dflt="none";; *) dflt="$multistatedist";; esac rp="What is the distribution code for your multi-state region?" . ./myread multistatedist="$ans" case "$cntrydist" in '') dflt="none";; *) dflt="$cntrydist";; esac rp="What is the distribution code for your country?" . ./myread cntrydist="$ans" case "$contdist" in '') dflt="none";; *) dflt="$contdist";; esac rp="What is the distribution code for your continent?" . ./myread contdist="$ans" $cat <<'EOM' If you have any other distribution groups you will need to edit Pnews and newsetup to add them. EOM : determine default editor echo " " case "$defeditor" in '') case "$_vi" in */*) dflt="$_vi";; *) dflt=/usr/ucb/vi;; esac ;; *) dflt="$defeditor" ;; esac fn=f/ rp="What is the default editor on your system?" . ./getfile defeditor="$ans" : determine where private executables go case "$privlib" in '') dflt=/usr/lib/$package $test -d /usr/local/lib && dflt=/usr/local/lib/$package $test -d /local/lib && dflt=/local/lib/$package ;; *) dflt="$privlib" ;; esac $cat <&4 ;; esac : see if signal is declared as pointer to function returning int or void echo " " xxx=`./findhdr signal.h` $test "$xxx" && $cppstdin $cppminus $cppflags < $xxx >$$.tmp 2>/dev/null if $contains 'int.*\*[ ]*signal' $$.tmp >/dev/null 2>&1 ; then echo "You have int (*signal())() instead of void." >&4 val="$undef" elif $contains 'void.*\*[ ]*signal' $$.tmp >/dev/null 2>&1 ; then echo "You have void (*signal())() instead of int." >&4 val="$define" elif $contains 'extern[ ]*[(\*]*signal' $$.tmp >/dev/null 2>&1 ; then echo "You have int (*signal())() instead of void." >&4 val="$undef" else case "$d_voidsig" in '') echo "I can't determine whether signal handler returns void or int..." >&4 dflt=void rp="What type does your signal handler return?" . ./myread case "$ans" in v*) val="$define";; *) val="$undef";; esac;; *) echo "As you already told me, signal handler returns $signal_t." >&4;; esac fi set d_voidsig eval $setvar case "$d_voidsig" in "$define") signal_t="void";; *) signal_t="int";; esac $rm -f $$.tmp : check if they want threads by default case "$trn_init" in F*) dflt="n";; *) dflt="y";; esac $cat <<'EOM' Trn has two distinct operating modes (selected by the -x option): traditional rn and threaded rn. If you like, trn will check the first letter of the command name and default to threaded mode (-x) if it is a 't', or run as traditional rn (+x) if it isn't (this lets you install as both trn and rn, linked together). Otherwise, you can install trn to default to threaded rn (-x) no matter what its name is. EOM rp='Do you want trn to default to -x, regardless of name?' . ./myread case "$ans" in n*|f*) trn_init='FALSE' ans=' for trn (not rn)';; *) trn_init='TRUE' ans='';; esac : check if they want the selector used by default case "$trn_select" in F*) dflt="n";; *) dflt="y";; esac $cat <<'EOM' When the -X option is specified, trn will set the default command for starting a newsgroup to be the selector. EOM rp="Should the -X option be on by default$ans?" . ./myread case "$ans" in n*|f*) trn_select=FALSE;; *) trn_select=TRUE;; esac : see if this is a dirent system echo " " if xinc=`./findhdr dirent.h`; $test "$xinc"; then val="$define" echo " found." >&4 else val="$undef" if xinc=`./findhdr sys/dir.h`; $test "$xinc"; then echo " found." >&4 echo " " else xinc=`./findhdr sys/ndir.h` fi echo " NOT found." >&4 fi set i_dirent eval $setvar : see if ptem.h exists if $test -r /usr/include/sys/ptem.h ; then $echo "sys/ptem.h found." i_ptem="$define" else i_ptem="$undef" fi : get C preprocessor symbols handy echo " " echo $attrlist | $tr ' ' '\012' >Cppsym.know $cat <Cppsym $startsh case "\$1" in -l) list=true shift ;; esac unknown='' case "\$list\$#" in 1|2) for sym do if $contains "^\$1$" Cppsym.true >/dev/null 2>&1; then exit 0 elif $contains "^\$1$" Cppsym.know >/dev/null 2>&1; then : else unknown="\$unknown \$sym" fi done set X \$unknown shift ;; esac case \$# in 0) exit 1;; esac echo \$* | $tr ' ' '\012' | $sed -e 's/\(.*\)/\\ #ifdef \1\\ exit 0; _ _ _ _\1\\ \1\\ #endif\\ /' >Cppsym\$\$ echo "exit 1; _ _ _" >>Cppsym\$\$ $cppstdin $cppminus Cppsym2\$\$ case "\$list" in true) $awk 'NF > 5 {print substr(\$6,2,100)}' Cppsym.true $cat Cppsym.true : see if this is a termio system val="$undef" val2="$undef" val3="$undef" if $test `./findhdr termios.h`; then set tcsetattr i_termios eval $inlibc val3="$i_termios" fi echo " " case "$val3" in "$define") echo "You have POSIX termios.h... good!" >&4;; *) if Cppsym pyr; then case "`/bin/universe`" in ucb) if $test `./findhdr sgtty.h`; then val2="$define" echo " found." >&4 else echo "System is pyramid with BSD universe." echo " not found--you could have problems." >&4 fi;; *) if $test `./findhdr termio.h`; then val="$define" echo " found." >&4 else echo "System is pyramid with USG universe." echo " not found--you could have problems." >&4 fi;; esac elif usg; then if $test `./findhdr termio.h`; then echo " found." >&4 val="$define" elif $test `./findhdr sgtty.h`; then echo " found." >&4 val2="$define" else echo "Neither nor found--you could have problems." >&4 fi else if $test `./findhdr sgtty.h`; then echo " found." >&4 val2="$define" elif $test `./findhdr termio.h`; then echo " found." >&4 val="$define" else echo "Neither nor found--you could have problems." >&4 fi fi;; esac set i_termio; eval $setvar val=$val2; set i_sgtty; eval $setvar val=$val3; set i_termios; eval $setvar : define an alternate in-header-list? function inhdr='echo " "; td=$define; tu=$undef; yyy=$@; cont=true; xxf="echo \"<\$1> found.\" >&4"; case $# in 2) xxnf="echo \"<\$1> NOT found.\" >&4";; *) xxnf="echo \"<\$1> NOT found, ...\" >&4";; esac; case $# in 4) instead=instead;; *) instead="at last";; esac; while $test "$cont"; do xxx=`./findhdr $1` var=$2; eval "was=\$$2"; if $test "$xxx" && $test -r "$xxx"; then eval $xxf; eval "case \"\$$var\" in $undef) . whoa; esac"; eval "$var=\$td"; cont=""; else eval $xxnf; eval "case \"\$$var\" in $define) . whoa; esac"; eval "$var=\$tu"; fi; set $yyy; shift; shift; yyy=$@; case $# in 0) cont="";; 2) xxf="echo \"but I found <\$1> $instead.\" >&4"; xxnf="echo \"and I did not find <\$1> either.\" >&4";; *) xxf="echo \"but I found <\$1\> instead.\" >&4"; xxnf="echo \"there is no <\$1>, ...\" >&4";; esac; done; while $test "$yyy"; do set $yyy; var=$2; eval "was=\$$2"; eval "case \"\$$var\" in $define) . whoa; esac"; eval "$var=\$tu"; set $yyy; shift; shift; yyy=$@; done' : see if stdlib is available set stdlib.h i_stdlib eval $inhdr : see if this is an sysdir system set sys/dir.h i_sysdir eval $inhdr : see if ioctl defs are in sgtty, termio, sys/filio or sys/ioctl echo " " if $test `./findhdr sys/filio.h`; then val="$define" echo " found." >&4 else val="$undef" echo "no found." >&4 fi set i_sysfilio eval $setvar if $test `./findhdr sys/ioctl.h`; then val="$define" echo " found." >&4 else val="$undef" if $test $i_sysfilio = "$define"; then echo "no found." >&4 else $test $i_sgtty = "$define" && xxx="sgtty.h" $test $i_termio = "$define" && xxx="termio.h" $test $i_termios = "$define" && xxx="termios.h" echo "No found, assuming ioctl args are defined in <$xxx>." >&4 fi fi set i_sysioctl eval $setvar : see if this is an sysndir system set sys/ndir.h i_sysndir eval $inhdr : see if we should include time.h, sys/time.h, or both echo " " echo "Testing to see if we should include , or both." >&4 $echo $n "I'm now running the test program...$c" $cat >try.c <<'EOCP' #include #ifdef I_TIME #include #endif #ifdef I_SYSTIME #include #endif #ifdef I_SYSSELECT #include #endif main() { struct tm foo; #ifdef S_TIMEVAL struct timeval bar; #endif #ifdef S_TIMEZONE struct timezone tzp; #endif if (foo.tm_sec == foo.tm_sec) exit(0); #ifdef S_TIMEVAL if (bar.tv_sec == bar.tv_sec) exit(0); #endif exit(1); } EOCP flags='' for s_timezone in '-DS_TIMEZONE' ''; do sysselect='' for s_timeval in '-DS_TIMEVAL' ''; do for i_time in '' '-DI_TIME'; do for i_systime in '-DI_SYSTIME' ''; do case "$flags" in '') $echo $n ".$c" if $cc $ccflags \ $i_time $i_systime $sysselect $s_timeval $s_timezone \ try.c -o try >/dev/null 2>&1 ; then set X $i_time $i_systime $sysselect $s_timeval shift flags="$*" echo " " $echo $n "Succeeded with $flags$c" fi ;; esac done done done done echo " " case "$flags" in *I_TIME*) i_time="$define" echo "We'll include ." >&4;; *) i_time="$undef";; esac case "$flags" in *I_SYSTIME*) i_systime="$define" echo "We'll include ." >&4;; *) i_systime="$undef";; esac $rm -f try.c try : see if this is a unistd.h system set unistd.h i_unistd eval $inhdr : see if this is a vfork system set vfork.h i_vfork eval $inhdr : end of configuration questions echo " " echo "End of configuration questions." echo " " : back to where it started if test -d ../UU; then cd .. fi case "$srcdir" in ..) srcdir='.';; esac : configuration may be patched via a 'config.over' file if $test -f config.over; then echo " " dflt=y rp='I see a config.over file. Do you wish to load it?' . UU/myread case "$ans" in n*) echo "OK, I'll ignore it.";; *) . ./config.over echo "Configuration override changes have been loaded." ;; esac fi : create config.sh file echo " " echo "Creating config.sh..." >&4 $spitshell <config.sh $startsh # # This file was produced by running the Configure script. It holds all the # definitions figured out by Configure. Should you modify one of these values, # do not forget to propagate your changes by running "Configure -der". You may # instead choose to run each of the .SH files by yourself, or "Configure -S". # # Configuration time: $cf_time # Configured by: $cf_by # Target system: $myuname d_bsd='$d_bsd' d_eunice='$d_eunice' d_xenix='$d_xenix' eunicefix='$eunicefix' Mcc='$Mcc' awk='$awk' basename='$basename' bash='$bash' cat='$cat' cp='$cp' cpp='$cpp' csh='$csh' date='$date' diff='$diff' echo='$echo' ed='$ed' egrep='$egrep' expr='$expr' grep='$grep' inews='$inews' ispell='$ispell' ksh='$ksh' less='$less' lint='$lint' mail='$mail' metamail='$metamail' mhn='$mhn' mkdir='$mkdir' more='$more' munpack='$munpack' mv='$mv' nroff='$nroff' pg='$pg' rm='$rm' rmail='$rmail' sed='$sed' sendmail='$sendmail' smail='$smail' sort='$sort' spell='$spell' tail='$tail' test='$test' tr='$tr' uname='$uname' uniq='$uniq' uuname='$uuname' vi='$vi' vspell='$vspell' who='$who' hint='$hint' myuname='$myuname' srcdir='$srcdir' vincludes='$vincludes' vpath='$vpath' Id='$Id' Log='$Log' active='$active' acttimes='$acttimes' d_acttimes='$d_acttimes' myactive='$myactive' bin='$bin' binexp='$binexp' installbin='$installbin' cc='$cc' ccflags='$ccflags' cppflags='$cppflags' ldflags='$ldflags' lkflags='$lkflags' optimize='$optimize' cf_by='$cf_by' cf_time='$cf_time' contains='$contains' cpplast='$cpplast' cppminus='$cppminus' cpprun='$cpprun' cppstdin='$cppstdin' d_ftime='$d_ftime' aphostcmd='$aphostcmd' d_gethname='$d_gethname' d_phostcmd='$d_phostcmd' d_uname='$d_uname' d_getpwent='$d_getpwent' d_getcwd='$d_getcwd' d_getwd='$d_getwd' d_havetlib='$d_havetlib' termlib='$termlib' d_ignoreorg='$d_ignoreorg' d_internet='$d_internet' d_memcmp='$d_memcmp' d_memcpy='$d_memcpy' d_memset='$d_memset' d_mimeshow='$d_mimeshow' d_mimestore='$d_mimestore' mimeshow="$mimeshow" mimestore="$mimestore" d_newsadm='$d_newsadm' newsadmin='$newsadmin' d_nntp='$d_nntp' d_xdata='$d_xdata' d_genauth='$d_genauth' servername='$servername' d_nolnbuf='$d_nolnbuf' d_normsig='$d_normsig' jobslib='$jobslib' d_novoid='$d_novoid' void='$void' d_portable='$d_portable' d_rdchk='$d_rdchk' d_rename='$d_rename' d_sigblock='$d_sigblock' d_sighold='$d_sighold' d_sizet='$d_sizet' d_strccmp='$d_strccmp' d_strchr='$d_strchr' d_strftime='$d_strftime' strftimec='$strftimec' strftimeo='$strftimeo' d_libndir='$d_libndir' d_usendir='$d_usendir' libndir='$libndir' ndirc='$ndirc' ndiro='$ndiro' d_vfork='$d_vfork' d_voidsig='$d_voidsig' signal_t='$signal_t' defeditor='$defeditor' filexp='$filexp' d_dirnamlen='$d_dirnamlen' i_dirent='$i_dirent' i_ptem='$i_ptem' i_stdlib='$i_stdlib' i_string='$i_string' strings='$strings' i_sysdir='$i_sysdir' d_voidtty='$d_voidtty' i_bsdioctl='$i_bsdioctl' i_sysfilio='$i_sysfilio' i_sysioctl='$i_sysioctl' i_syssockio='$i_syssockio' i_sysndir='$i_sysndir' i_sgtty='$i_sgtty' i_termio='$i_termio' i_termios='$i_termios' i_systime='$i_systime' i_time='$i_time' i_unistd='$i_unistd' i_vfork='$i_vfork' ispell_options='$ispell_options' ispell_prg='$ispell_prg' libc='$libc' plibpth='$plibpth' xlibpth='$xlibpth' libs='$libs' citydist='$citydist' cntrydist='$cntrydist' contdist='$contdist' locdist='$locdist' multistatedist='$multistatedist' orgdist='$orgdist' statedist='$statedist' mailer='$mailer' mailfile='$mailfile' installmansrc='$installmansrc' manext='$manext' mansrc='$mansrc' mansrcexp='$mansrcexp' mboxchar='$mboxchar' c='$c' n='$n' d_berknames='$d_berknames' d_passnames='$d_passnames' d_usgnames='$d_usgnames' nametype='$nametype' inewsloc='$inewsloc' newslib='$newslib' newslibexp='$newslibexp' newsspool='$newsspool' orgname='$orgname' package='$package' spackage='$spackage' pager='$pager' phost='$phost' phostcmd='$phostcmd' prefshell='$prefshell' installprivlib='$installprivlib' privlib='$privlib' privlibexp='$privlibexp' rootid='$rootid' sharpbang='$sharpbang' shsharp='$shsharp' spitshell='$spitshell' startsh='$startsh' sysman='$sysman' d_usemt='$d_usemt' d_useov='$d_useov' overviewdir='$overviewdir' threaddir='$threaddir' trn_init='$trn_init' trn_select='$trn_select' nm_opt='$nm_opt' runnm='$runnm' usenm='$usenm' incpath='$incpath' mips='$mips' mips_type='$mips_type' usrinc='$usrinc' EOT : add special variables $test -f $srcdir/patchlevel.h && \ awk '/^#define/ {printf "%s=%s%s %s %s %s\n",$2,$3,$4,$5,$6,$7}' $srcdir/patchlevel.h >>config.sh echo "CONFIG=true" >>config.sh : Finish up by extracting the .SH files case "$alldone" in exit) $rm -rf UU echo "Done." exit 0 ;; cont) ;; '') echo " " dflt='' nostick=true echo "If you didn't make any mistakes, then just type a carriage return here." rp="If you need to edit config.sh, do it as a shell escape here:" . UU/myread nostick='' case "$ans" in '') ;; *) : in case they cannot read sh 1>&4 -c "$ans";; esac ;; esac : if this fails, just run all the .SH files by hand . ./config.sh echo " " exec 1>&4 . UU/extract echo " " echo 'Now type "make".' $rm -f kit*isdone ark*isdone $rm -rf UU : End of Configure trn-3.6/Pnews.10000664000000000000000000000775405625465773010273 0ustar .\" $Id: Pnews.1,v 4.4.3.1 1992/02/01 03:17:20 sob PATCH_3 sob $ .\" .\" This software is Copyright 1991 by Stan Barber. .\" .\" Permission is hereby granted to copy, reproduce, redistribute or otherwise .\" use this software as long as: there is no monetary profit gained .\" specifically from the use or reproduction of this software, it is not .\" sold, rented, traded or otherwise marketed, and this copyright notice is .\" included prominently in any copy made. .\" .\" The authors make no claims as to the fitness or correctness of this software .\" for any use whatsoever, and it is provided as is. Any use of this software .\" is at the user's own risk. .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH PNEWS 1 LOCAL .SH NAME Pnews - a program for posting news articles .SH SYNOPSIS .B Pnews newsgroup title .br or .br .B Pnews -h headerfile [oldarticle] .br or .br .B Pnews .SH DESCRIPTION Pnews is a friendly interface for posting news articles. It will ask several questions, then allow you to enter your article, and then post it using the inews(1) program. If you type h and a carriage return at any point, .I Pnews will tell you what it wants to know. .PP If you put a .I .signature file in your home directory, your inews program will usually append it to your message after you are done editing it. If you prefer to see your signature while you are editing, rename your \&.signature file to be .news_sig and it will be appended before you begin to edit. Note that .news_sig obeys the setting of DOTDIR, but .signature may not, since it is out of Pnews's control. If both .news_sig and .signature exist you'll get two signatures appended. .PP The -h form is used when invoked from .I trn or .IR rn . If your editor can edit multiple files, and you want the article to which you are replying to show up as an alternate file, define the environment variable NEWSPOSTER as \*(L"Pnews -h %h %A\*(R". You can also modify the the NEWSHEADER environment variable to change the header file that .I [t]rn passes to Pnews. .SH ENVIRONMENT .IP AUTHORCOPY 8 If defined, contains the name of a file to which the finished article will be appended. .Sp Default: article not saved .IP DOTDIR 8 Where to find your dot files, if they aren't in your home directory. This is primarily for accounts which are shared by more than one person. .Sp Default: $HOME .IP EDITOR 8 The editor you want to use, if VISUAL is undefined. .Sp Default: whatever your news administrator installed, usually vi. .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .IP LOGNAME 8 Your login name, if USER is undefined. .Sp Default: value of \*(L"whoami\*(R". .IP NAME 8 Your full name. .Sp Default: name from /etc/passwd, or ~/.fullname. .IP NEWSORG 8 Either the name of your organization, or the name of a file containing the name of your organization. (For use at organizations where the ORGANIZATION environmental variable is in use for other purposes). If both NEWSORG and ORGANIZATION are set, NEWSORG will override ORGANIZATION. .IP ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. .Sp Default: whatever your news administrator chose. .IP REPLYTO 8 The contents of a \*(L"Reply-To:\*(R" header line to insert into your message. .Sp Default: header line not inserted. .IP USER 8 Your login name. .Sp Default: $LOGNAME .IP VISUAL 8 The editor you want to use. .Sp Default: $EDITOR .SH FILES $DOTDIR/.article .br $DOTDIR/.news_sig .br ~/dead.article .SH SEE ALSO trn(1), rn(1), Rnmail(1), inews(1) .SH DIAGNOSTICS .SH BUGS Not the speediest program in the world, but maybe that's a blessing to the net. trn-3.6/INIT0000664000000000000000000000000705554557076007562 0ustar +Hfrom trn-3.6/sw.h0000664000000000000000000000157005420174122007657 0ustar /* $Id: sw.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifdef INNERSEARCH EXT int gline INIT(0); #endif void sw_init _((int,char**,char**)); void sw_file _((char**,bool_int)); void sw_list _((char*)); void decode_switch _((char*)); void pr_switches _((void)); void cwd_check _((void)); trn-3.6/dependencies0000664000000000000000000003662105663312256011446 0ustar addng.o: EXTERN.h addng.o: INTERN.h addng.o: addng.c addng.o: addng.h addng.o: common.h addng.o: config.h addng.o: final.h addng.o: intrp.h addng.o: last.h addng.o: ngdata.h addng.o: nntp.h addng.o: nntpclient.h addng.o: only.h addng.o: rcstuff.h addng.o: search.h addng.o: trn.h addng.o: util.h art.o: EXTERN.h art.o: INTERN.h art.o: art.c art.o: art.h art.o: artio.h art.o: artstate.h art.o: backpage.h art.o: bits.h art.o: cache.h art.o: charsubst.h art.o: common.h art.o: config.h art.o: final.h art.o: head.h art.o: help.h art.o: intrp.h art.o: ng.h art.o: ngdata.h art.o: ngstuff.h art.o: nntp.h art.o: nntpclient.h art.o: rcstuff.h art.o: rt-select.h art.o: rt-util.h art.o: rt-wumpus.h art.o: rthread.h art.o: search.h art.o: sw.h art.o: term.h art.o: trn.h art.o: util.h art.o: util2.h artio.o: EXTERN.h artio.o: INTERN.h artio.o: art.h artio.o: artio.c artio.o: artio.h artio.o: bits.h artio.o: cache.h artio.o: common.h artio.o: config.h artio.o: final.h artio.o: head.h artio.o: ngdata.h artio.o: nntp.h artio.o: nntpclient.h artio.o: rthread.h artsrch.o: EXTERN.h artsrch.o: INTERN.h artsrch.o: artio.h artsrch.o: artsrch.c artsrch.o: artsrch.h artsrch.o: bits.h artsrch.o: cache.h artsrch.o: common.h artsrch.o: config.h artsrch.o: final.h artsrch.o: head.h artsrch.o: intrp.h artsrch.o: kfile.h artsrch.o: ng.h artsrch.o: ngdata.h artsrch.o: ngstuff.h artsrch.o: nntp.h artsrch.o: nntpclient.h artsrch.o: rt-select.h artsrch.o: rthread.h artsrch.o: search.h artsrch.o: term.h artsrch.o: util.h autosub.o: EXTERN.h autosub.o: INTERN.h autosub.o: autosub.c autosub.o: autosub.h autosub.o: common.h autosub.o: config.h autosub.o: final.h autosub.o: ngsrch.h autosub.o: search.h autosub.o: util.h backpage.o: EXTERN.h backpage.o: INTERN.h backpage.o: backpage.c backpage.o: backpage.h backpage.o: common.h backpage.o: config.h backpage.o: final.h backpage.o: intrp.h bits.o: EXTERN.h bits.o: INTERN.h bits.o: artio.h bits.o: bits.c bits.o: bits.h bits.o: cache.h bits.o: common.h bits.o: config.h bits.o: final.h bits.o: head.h bits.o: intrp.h bits.o: ndir.h bits.o: ng.h bits.o: ngdata.h bits.o: nntp.h bits.o: nntpclient.h bits.o: rcln.h bits.o: rcstuff.h bits.o: rt-select.h bits.o: rt-util.h bits.o: rthread.h bits.o: term.h bits.o: trn.h bits.o: util.h bits.o: util2.h cache.o: EXTERN.h cache.o: INTERN.h cache.o: artsrch.h cache.o: bits.h cache.o: cache.c cache.o: cache.h cache.o: common.h cache.o: config.h cache.o: final.h cache.o: hash.h cache.o: head.h cache.o: intrp.h cache.o: ng.h cache.o: ngdata.h cache.o: nntp.h cache.o: nntpclient.h cache.o: rcstuff.h cache.o: rt-ov.h cache.o: rt-page.h cache.o: rt-process.h cache.o: rt-select.h cache.o: rt-util.h cache.o: rthread.h cache.o: search.h cache.o: term.h cache.o: trn.h cache.o: util.h cache.o: util2.h charsubst.o: EXTERN.h charsubst.o: INTERN.h charsubst.o: artstate.h charsubst.o: charsubst.c charsubst.o: charsubst.h charsubst.o: common.h charsubst.o: config.h charsubst.o: search.h decode.o: EXTERN.h decode.o: INTERN.h decode.o: common.h decode.o: config.h decode.o: decode.c decode.o: decode.h edit_dist.o: EXTERN.h edit_dist.o: common.h edit_dist.o: config.h edit_dist.o: edit_dist.c edit_dist.o: util.h final.o: EXTERN.h final.o: INTERN.h final.o: artio.h final.o: bits.h final.o: cache.h final.o: common.h final.o: config.h final.o: final.c final.o: final.h final.o: init.h final.o: intrp.h final.o: last.h final.o: ng.h final.o: ngdata.h final.o: nntp.h final.o: nntpclient.h final.o: rcstuff.h final.o: term.h final.o: util.h hash.o: EXTERN.h hash.o: INTERN.h hash.o: common.h hash.o: config.h hash.o: final.h hash.o: hash.c hash.o: hash.h hash.o: util.h head.o: EXTERN.h head.o: INTERN.h head.o: artio.h head.o: cache.h head.o: common.h head.o: config.h head.o: final.h head.o: hash.h head.o: head.c head.o: head.h head.o: ng.h head.o: ngdata.h head.o: nntp.h head.o: nntpclient.h head.o: rt-process.h head.o: rt-util.h head.o: rthread.h head.o: util.h help.o: EXTERN.h help.o: INTERN.h help.o: common.h help.o: config.h help.o: help.c help.o: help.h help.o: term.h help.o: trn.h init.o: EXTERN.h init.o: INTERN.h init.o: addng.h init.o: art.h init.o: artio.h init.o: artsrch.h init.o: backpage.h init.o: bits.h init.o: cache.h init.o: common.h init.o: config.h init.o: decode.h init.o: final.h init.o: head.h init.o: help.h init.o: init.c init.o: init.h init.o: intrp.h init.o: kfile.h init.o: last.h init.o: ng.h init.o: ngdata.h init.o: ngsrch.h init.o: ngstuff.h init.o: nntp.h init.o: nntpclient.h init.o: only.h init.o: rcln.h init.o: rcstuff.h init.o: respond.h init.o: rthread.h init.o: search.h init.o: sw.h init.o: term.h init.o: trn.h init.o: util.h intrp.o: EXTERN.h intrp.o: INTERN.h intrp.o: artio.h intrp.o: artsrch.h intrp.o: bits.h intrp.o: cache.h intrp.o: common.h intrp.o: config.h intrp.o: final.h intrp.o: head.h intrp.o: init.h intrp.o: intrp.c intrp.o: intrp.h intrp.o: ng.h intrp.o: ngdata.h intrp.o: nntp.h intrp.o: nntpclient.h intrp.o: rcstuff.h intrp.o: respond.h intrp.o: rt-select.h intrp.o: rt-util.h intrp.o: rthread.h intrp.o: search.h intrp.o: term.h intrp.o: trn.h intrp.o: util.h intrp.o: util2.h kfile.o: EXTERN.h kfile.o: INTERN.h kfile.o: artsrch.h kfile.o: bits.h kfile.o: cache.h kfile.o: common.h kfile.o: config.h kfile.o: hash.h kfile.o: intrp.h kfile.o: kfile.c kfile.o: kfile.h kfile.o: ng.h kfile.o: ngdata.h kfile.o: ngstuff.h kfile.o: rcstuff.h kfile.o: rt-process.h kfile.o: rt-select.h kfile.o: rthread.h kfile.o: search.h kfile.o: term.h kfile.o: trn.h kfile.o: util.h last.o: EXTERN.h last.o: INTERN.h last.o: common.h last.o: config.h last.o: intrp.h last.o: last.c last.o: last.h last.o: trn.h last.o: util.h ndir.o: EXTERN.h ndir.o: INTERN.h ndir.o: common.h ndir.o: config.h ndir.o: ndir.c ndir.o: ndir.h ng.o: EXTERN.h ng.o: INTERN.h ng.o: art.h ng.o: artio.h ng.o: artsrch.h ng.o: artstate.h ng.o: backpage.h ng.o: bits.h ng.o: cache.h ng.o: charsubst.h ng.o: common.h ng.o: config.h ng.o: decode.h ng.o: final.h ng.o: head.h ng.o: help.h ng.o: intrp.h ng.o: kfile.h ng.o: last.h ng.o: ng.c ng.o: ng.h ng.o: ngdata.h ng.o: ngstuff.h ng.o: nntp.h ng.o: nntpclient.h ng.o: rcln.h ng.o: rcstuff.h ng.o: respond.h ng.o: rt-select.h ng.o: rt-wumpus.h ng.o: rthread.h ng.o: search.h ng.o: sw.h ng.o: term.h ng.o: trn.h ng.o: util.h ngdata.o: EXTERN.h ngdata.o: INTERN.h ngdata.o: bits.h ngdata.o: cache.h ngdata.o: common.h ngdata.o: config.h ngdata.o: final.h ngdata.o: head.h ngdata.o: intrp.h ngdata.o: kfile.h ngdata.o: ndir.h ngdata.o: ng.h ngdata.o: ngdata.c ngdata.o: ngdata.h ngdata.o: nntp.h ngdata.o: nntpclient.h ngdata.o: rcln.h ngdata.o: rcstuff.h ngdata.o: rt-select.h ngdata.o: rthread.h ngdata.o: term.h ngdata.o: trn.h ngdata.o: util.h nghash.o: EXTERN.h nghash.o: common.h nghash.o: config.h nghash.o: final.h nghash.o: hash.h nghash.o: intrp.h nghash.o: ndir.h nghash.o: ngdata.h nghash.o: nghash.c nghash.o: nntp.h nghash.o: nntpclient.h nghash.o: rcln.h nghash.o: rcstuff.h nghash.o: term.h nghash.o: trn.h nghash.o: util.h ngsrch.o: EXTERN.h ngsrch.o: INTERN.h ngsrch.o: common.h ngsrch.o: config.h ngsrch.o: final.h ngsrch.o: ngsrch.c ngsrch.o: ngsrch.h ngsrch.o: rcln.h ngsrch.o: rcstuff.h ngsrch.o: search.h ngsrch.o: term.h ngsrch.o: trn.h ngsrch.o: util.h ngstuff.o: EXTERN.h ngstuff.o: INTERN.h ngstuff.o: bits.h ngstuff.o: cache.h ngstuff.o: common.h ngstuff.o: config.h ngstuff.o: decode.h ngstuff.o: final.h ngstuff.o: head.h ngstuff.o: intrp.h ngstuff.o: kfile.h ngstuff.o: ng.h ngstuff.o: ngdata.h ngstuff.o: ngstuff.c ngstuff.o: ngstuff.h ngstuff.o: rcstuff.h ngstuff.o: respond.h ngstuff.o: rt-select.h ngstuff.o: rt-wumpus.h ngstuff.o: rthread.h ngstuff.o: sw.h ngstuff.o: term.h ngstuff.o: trn.h ngstuff.o: util.h ngstuff.o: util2.h nntp.o: EXTERN.h nntp.o: artio.h nntp.o: bits.h nntp.o: cache.h nntp.o: common.h nntp.o: config.h nntp.o: final.h nntp.o: head.h nntp.o: init.h nntp.o: ngdata.h nntp.o: nntp.c nntp.o: nntp.h nntp.o: nntpclient.h nntp.o: rcln.h nntp.o: term.h nntp.o: trn.h nntp.o: util.h nntpauth.o: EXTERN.h nntpauth.o: INTERN.h nntpauth.o: common.h nntpauth.o: config.h nntpauth.o: nntp.h nntpauth.o: nntpauth.c nntpauth.o: nntpauth.h nntpauth.o: nntpclient.h nntpauth.o: util2.h nntpclient.o: EXTERN.h nntpclient.o: INTERN.h nntpclient.o: common.h nntpclient.o: config.h nntpclient.o: nntpclient.c nntpclient.o: nntpclient.h nntpinit.o: EXTERN.h nntpinit.o: common.h nntpinit.o: config.h nntpinit.o: nntpclient.h nntpinit.o: nntpinit.c nntplist.o: EXTERN.h nntplist.o: common.h nntplist.o: config.h nntplist.o: nntpclient.h nntplist.o: nntplist.c only.o: EXTERN.h only.o: INTERN.h only.o: common.h only.o: config.h only.o: final.h only.o: ngsrch.h only.o: only.c only.o: only.h only.o: search.h only.o: util.h parsedate.o: config.h parsedate.o: parsedate.y rcln.o: EXTERN.h rcln.o: INTERN.h rcln.o: common.h rcln.o: config.h rcln.o: ngdata.h rcln.o: rcln.c rcln.o: rcln.h rcln.o: rcstuff.h rcln.o: util.h rcln.o: util2.h rcstuff.o: EXTERN.h rcstuff.o: INTERN.h rcstuff.o: autosub.h rcstuff.o: bits.h rcstuff.o: cache.h rcstuff.o: common.h rcstuff.o: config.h rcstuff.o: final.h rcstuff.o: hash.h rcstuff.o: intrp.h rcstuff.o: ngdata.h rcstuff.o: nntp.h rcstuff.o: nntpclient.h rcstuff.o: only.h rcstuff.o: rcln.h rcstuff.o: rcstuff.c rcstuff.o: rcstuff.h rcstuff.o: search.h rcstuff.o: term.h rcstuff.o: trn.h rcstuff.o: util.h respond.o: EXTERN.h respond.o: INTERN.h respond.o: artio.h respond.o: cache.h respond.o: charsubst.h respond.o: common.h respond.o: config.h respond.o: decode.h respond.o: final.h respond.o: head.h respond.o: intrp.h respond.o: ng.h respond.o: ngdata.h respond.o: nntp.h respond.o: nntpclient.h respond.o: respond.c respond.o: respond.h respond.o: term.h respond.o: trn.h respond.o: util.h respond.o: util2.h rt-mt.o: EXTERN.h rt-mt.o: INTERN.h rt-mt.o: bits.h rt-mt.o: cache.h rt-mt.o: common.h rt-mt.o: config.h rt-mt.o: hash.h rt-mt.o: intrp.h rt-mt.o: ng.h rt-mt.o: ngdata.h rt-mt.o: nntp.h rt-mt.o: nntpclient.h rt-mt.o: rcln.h rt-mt.o: rt-mt.c rt-mt.o: rt-mt.h rt-mt.o: rt-process.h rt-mt.o: rthread.h rt-mt.o: trn.h rt-mt.o: util.h rt-ov.o: EXTERN.h rt-ov.o: INTERN.h rt-ov.o: bits.h rt-ov.o: cache.h rt-ov.o: common.h rt-ov.o: config.h rt-ov.o: final.h rt-ov.o: hash.h rt-ov.o: head.h rt-ov.o: ng.h rt-ov.o: ngdata.h rt-ov.o: nntp.h rt-ov.o: nntpclient.h rt-ov.o: overview.h rt-ov.o: rt-ov.c rt-ov.o: rt-ov.h rt-ov.o: rt-process.h rt-ov.o: rt-util.h rt-ov.o: rthread.h rt-ov.o: term.h rt-ov.o: trn.h rt-ov.o: util.h rt-page.o: EXTERN.h rt-page.o: INTERN.h rt-page.o: cache.h rt-page.o: common.h rt-page.o: config.h rt-page.o: ngdata.h rt-page.o: rt-page.c rt-page.o: rt-page.h rt-page.o: rt-select.h rt-page.o: rt-util.h rt-page.o: rthread.h rt-page.o: term.h rt-page.o: trn.h rt-page.o: util.h rt-process.o: EXTERN.h rt-process.o: INTERN.h rt-process.o: bits.h rt-process.o: cache.h rt-process.o: common.h rt-process.o: config.h rt-process.o: final.h rt-process.o: hash.h rt-process.o: intrp.h rt-process.o: kfile.h rt-process.o: ng.h rt-process.o: ngdata.h rt-process.o: rcln.h rt-process.o: rt-process.c rt-process.o: rt-process.h rt-process.o: rt-select.h rt-process.o: rthread.h rt-process.o: trn.h rt-process.o: util.h rt-select.o: EXTERN.h rt-select.o: INTERN.h rt-select.o: artsrch.h rt-select.o: bits.h rt-select.o: cache.h rt-select.o: common.h rt-select.o: config.h rt-select.o: final.h rt-select.o: help.h rt-select.o: kfile.h rt-select.o: ng.h rt-select.o: ngdata.h rt-select.o: ngstuff.h rt-select.o: rt-page.h rt-select.o: rt-select.c rt-select.o: rt-select.h rt-select.o: rt-util.h rt-select.o: rthread.h rt-select.o: search.h rt-select.o: term.h rt-select.o: trn.h rt-select.o: util.h rt-util.o: EXTERN.h rt-util.o: INTERN.h rt-util.o: artio.h rt-util.o: cache.h rt-util.o: common.h rt-util.o: config.h rt-util.o: ngdata.h rt-util.o: nntp.h rt-util.o: nntpclient.h rt-util.o: rt-select.h rt-util.o: rt-util.c rt-util.o: rt-util.h rt-util.o: rthread.h rt-util.o: term.h rt-wumpus.o: EXTERN.h rt-wumpus.o: INTERN.h rt-wumpus.o: artio.h rt-wumpus.o: backpage.h rt-wumpus.o: cache.h rt-wumpus.o: common.h rt-wumpus.o: config.h rt-wumpus.o: final.h rt-wumpus.o: head.h rt-wumpus.o: ng.h rt-wumpus.o: ngdata.h rt-wumpus.o: rt-select.h rt-wumpus.o: rt-wumpus.c rt-wumpus.o: rt-wumpus.h rt-wumpus.o: rthread.h rt-wumpus.o: term.h rt-wumpus.o: util.h rthread.o: EXTERN.h rthread.o: INTERN.h rthread.o: artstate.h rthread.o: bits.h rthread.o: cache.h rthread.o: common.h rthread.o: config.h rthread.o: final.h rthread.o: hash.h rthread.o: head.h rthread.o: intrp.h rthread.o: kfile.h rthread.o: ng.h rthread.o: ngdata.h rthread.o: nntp.h rthread.o: nntpclient.h rthread.o: rcln.h rthread.o: rcstuff.h rthread.o: rt-mt.h rthread.o: rt-ov.h rthread.o: rt-page.h rthread.o: rt-process.h rthread.o: rt-select.h rthread.o: rt-util.h rthread.o: rt-wumpus.h rthread.o: rthread.c rthread.o: rthread.h rthread.o: search.h rthread.o: trn.h rthread.o: util.h search.o: EXTERN.h search.o: INTERN.h search.o: common.h search.o: config.h search.o: search.c search.o: search.h search.o: util.h strftime.o: EXTERN.h strftime.o: common.h strftime.o: config.h strftime.o: strftime.c sw.o: EXTERN.h sw.o: INTERN.h sw.o: artstate.h sw.o: cache.h sw.o: charsubst.h sw.o: common.h sw.o: config.h sw.o: head.h sw.o: intrp.h sw.o: ng.h sw.o: ngdata.h sw.o: only.h sw.o: rt-page.h sw.o: rt-util.h sw.o: search.h sw.o: sw.c sw.o: sw.h sw.o: term.h sw.o: trn.h sw.o: util.h sw.o: util2.h term.o: EXTERN.h term.o: INTERN.h term.o: cache.h term.o: common.h term.o: config.h term.o: final.h term.o: help.h term.o: intrp.h term.o: term.c term.o: term.h term.o: util.h term.o: util2.h test.o: test.c trn-artchk.o: EXTERN.h trn-artchk.o: common.h trn-artchk.o: config.h trn-artchk.o: nntpclient.h trn-artchk.o: trn-artchk.c trn.o: EXTERN.h trn.o: INTERN.h trn.o: addng.h trn.o: cache.h trn.o: common.h trn.o: config.h trn.o: final.h trn.o: help.h trn.o: init.h trn.o: intrp.h trn.o: kfile.h trn.o: last.h trn.o: ng.h trn.o: ngdata.h trn.o: ngsrch.h trn.o: ngstuff.h trn.o: nntp.h trn.o: nntpclient.h trn.o: only.h trn.o: patchlevel.h trn.o: rcln.h trn.o: rcstuff.h trn.o: search.h trn.o: sw.h trn.o: term.h trn.o: trn.c trn.o: trn.h trn.o: util.h trn.o: util2.h unipatch.o: unipatch.c unship.o: EXTERN.h unship.o: common.h unship.o: config.h unship.o: decode.h unship.o: respond.h unship.o: unship.c util.o: EXTERN.h util.o: INTERN.h util.o: common.h util.o: config.h util.o: final.h util.o: nntp.h util.o: nntpauth.h util.o: nntpclient.h util.o: term.h util.o: util.c util.o: util.h util2.o: EXTERN.h util2.o: INTERN.h util2.o: common.h util2.o: config.h util2.o: nntp.h util2.o: nntpauth.h util2.o: nntpclient.h util2.o: util.h util2.o: util2.c util2.o: util2.h uudecode.o: EXTERN.h uudecode.o: common.h uudecode.o: config.h uudecode.o: decode.h uudecode.o: respond.h uudecode.o: uudecode.c Makefile: Makefile.SH config.sh ; /bin/sh $(srcdir)/Makefile.SH Pnews: Pnews.SH config.sh ; /bin/sh $(srcdir)/Pnews.SH Policy.sh: Policy.sh.SH config.sh ; /bin/sh $(srcdir)/Policy.sh.SH Rnmail: Rnmail.SH config.sh ; /bin/sh $(srcdir)/Rnmail.SH Speller: Speller.SH config.sh ; /bin/sh $(srcdir)/Speller.SH config.h: config.h.SH config.sh ; /bin/sh $(srcdir)/config.h.SH filexp: filexp.SH config.sh ; /bin/sh $(srcdir)/filexp.SH makedepend: makedepend.SH config.sh ; /bin/sh $(srcdir)/makedepend.SH makedir: makedir.SH config.sh ; /bin/sh $(srcdir)/makedir.SH mbox.saver: mbox.saver.SH config.sh ; /bin/sh $(srcdir)/mbox.saver.SH newsetup: newsetup.SH config.sh ; /bin/sh $(srcdir)/newsetup.SH newsgroups: newsgroups.SH config.sh ; /bin/sh $(srcdir)/newsgroups.SH newsnews: newsnews.SH config.sh ; /bin/sh $(srcdir)/newsnews.SH norm.saver: norm.saver.SH config.sh ; /bin/sh $(srcdir)/norm.saver.SH # WARNING: Put nothing here or make depend will gobble it up! trn-3.6/ngdata.h0000664000000000000000000000305105432542056010470 0ustar /* $Id: ngdata.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT FILE *actfp INIT(Nullfp); /* the active file */ EXT bool writesoft INIT(FALSE); /* rewrite the soft pointer file? */ EXT int softtries INIT(0), softmisses INIT(0); EXT ART_NUM absfirst INIT(0); /* 1st real article in current newsgroup */ EXT ART_NUM firstart INIT(0); /* minimum unread article number in newsgroup */ EXT ART_NUM lastart INIT(0); /* maximum article number in newsgroup */ #ifdef USE_NNTP EXT char active_name[MAXFILENAME]; EXT time_t lastactfetch INIT(0); #endif EXT ART_NUM *abs1st INIT(NULL); /* 1st real article in newsgroup */ EXT char *moderated; EXT char *redirected; EXT bool ThreadedGroup; EXT long activeitems; /* number of enties in active file */ void ngdata_init _((void)); bool access_ng _((void)); void grow_ng _((ART_NUM)); void ng_skip _((void)); ART_NUM getngsize _((NG_NUM)); void getngmissing _((void)); ACT_POS findact _((char*,char*,int,long)); void ngdatahash_init _((void)); trn-3.6/Pnews.SH0000664000000000000000000004432411727656505010432 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting Pnews (with variable substitutions)" $spitshell >Pnews <>Pnews <<'!NO!SUBS!' orgname=${NEWSORG-$orgname} !NO!SUBS! ;; *) $spitshell >>Pnews <<'!NO!SUBS!' orgname=${NEWSORG-${ORGANIZATION-$orgname}} !NO!SUBS! ;; esac $spitshell >>Pnews <<'!NO!SUBS!' homedir=${HOME-$LOGDIR} dotdir=${DOTDIR-$homedir} tmpart=$dotdir/.article speller=$rnlib/Speller # tmp="${TMPDIR-/tmp}" # uses tempfile(1) instead newsgroups=${NEWSGROUPS-$lib/newsgroups} active=${NEWSACTIVE-$active} case "$active" in none) active=`tempfile -p Pnact` #"$tmp/Pnact.$$";; esac news_sig=${NEWSSIGNATURE-$dotdir/.news_sig} case "$NETSPEED" in fast) if $test ! -f "$newsgroups"; then newsgroups=`tempfile -p Pnng` # "$tmp/Pnng.$$"; rmlist="$newsgroups"; cmdlist="$nntplist -o $newsgroups newsgroups ;"; fi if $test "x$active" = xnone || $test ! -f "$active"; then active=`tempfile -p Pnact` # "$tmp/Pnact.$$"; rmlist="$rmlist $active"; cmdlist="$cmdlist $nntplist -o $active active"; fi ;; esac if $test -f $dotdir/.pnewsexpert; then expertise=expert else $cat <<'EOM' I see you've never used this version of Pnews before. I will give you extra help this first time through, but then you must remember what you learned. If you don't understand any question, type h and a CR (carriage return) for help. If you've never posted an article to the net before, it is HIGHLY recommended that you read the netiquette document found in news.announce.newusers so that you'll know to avoid the commonest blunders. To do that, interrupt Pnews, get to the top-level prompt of [t]rn, and use the command "g news.announce.newusers" to go to that group. EOM expertise=beginner fi case $cntry in can) Stpr=Province ; stpr=province ;; *) Stpr=State ; stpr=state ;; esac case $multistate in pnw) multistpr="Pacific NorthWest" ;; *) multistpr="Multi-State Area" ;; esac headerfile="" case $# in 0) ;; *) case $1 in -h) headerfile="$2" shift shift case $# in 0) oldart="" ;; *) oldart="$1" shift ;; esac ;; esac ;; esac case $headerfile in '') . $rnlib/Pnews.header ;; *) $cat < $headerfile > $tmpart ;; esac rescue="$sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.article ;$rm -f $rmlist ; $echo Article appended to ${HOME-$LOGDIR}/dead.article ;chmod -f o-rwx ${HOME-$LOGDIR}/dead.article ${HOME-$LOGDIR}/.article; exit" if $test -n "$rmlist" ; then trap "$rm -f $rmlist" 0 fi trap "trap : 1; $rescue" 1 trap "$rescue" 2 $echo "" # extract the newsgroups list and distribution hdr_newsgroups=`$sed -n -e '/^Newsgroups:/{' -e 's///' -e 's/,/ /g' -e p -e q -e '}' $tmpart` hdr_distribution=`$sed -n -e '/^Distribution:/{' -e 's///' -e p -e q -e '}' $tmpart` # check for "poster" magic cookie. Allow erroneous user@site too. flag=0 for ng in $hdr_newsgroups ; do case "$ng" in poster) flag=1 ;; *@*) flag=1 ;; *) ;; esac done case $flag in 1) $cat <<'EOM' The original author has requested that messages be sent back via mail rather than posting to news. Do you want to jump out of this and EOM $echo $n "mail your reply instead? [yn] $c" read ans case $ans in n*) tweak=`tempfile -p Pntwk` #"$tmp/Pntwk.$$" $sed '1,/^[ ]*$/{/^Newsgroups:/d; s/^X-ORIGINAL-NEWSGROUPS:/Newsgroups:/;}' $tmpart >$tweak $cp $tweak $tmpart $rm -f $tweak ;; *) exit ;; esac $echo " " $echo "The Cc: line should have the poster's requested return address." ;; esac # play recorded message if $test -s ${lib}/recording ; then for ng in $hdr_newsgroups ; do _rec1=${lib}/`$sed -n "/^$ng/s/^.* //p" ${lib}/recording` _tmp=`$echo $ng |$sed "s/\..*//"` _rec2=${lib}/`$cat -s ${lib}/recording|$grep ${_tmp}.all|$sed "s/^.* //"` if $test -f ${_rec1} ; then $cat -s ${_rec1} fi if $test -f ${_rec2} ; then $cat -s ${_rec2} fi done fi # determine the distribution of this message set X $hdr_distribution shift if $test $# -gt 0 ; then dist=$1.whatever else set X $hdr_newsgroups shift if $test $# -gt 0 ; then dist=$1.whatever else dist=misc.whatever fi fi case $dist in *.*) ;; *) dist=$dist.whatever ;; esac case "$FAST_PNEWS" in y*) ;; *) # tell them what we think they are doing... !DIST! case $dist in world.*|comp.*|news.*|sci.*|rec.*|misc.*|soc.*|talk.*|alt.*|'') $cat <<'EOM' This program posts news to thousands of machines throughout the entire civilized world. Your message will cost the net hundreds if not thousands of dollars to send everywhere. Please be sure you know what you are doing. EOM ;; vmsnet.*) $echo 'This program posts news to many machines.' ;; bit.*) $echo 'This program posts news to many machines on BITNET.' ;; ddn.*) $echo 'This program posts news to many machines throughout the internet.' ;; $cont.*) $echo 'This program posts news to many machines throughout the continent.' ;; $cntry.*) $echo 'This program posts news to many machines throughout the country.' ;; $multistate.*) $echo "This program posts news to many machines throughout the ${multistpr}." ;; $state.*) $echo "This program posts news to many machines throughout the ${stpr}." ;; $city.*) $echo 'This program posts news to many machines throughout the city.' ;; $org.*) $echo 'This program posts news to machines throughout the organization.' ;; $loc.*) $echo 'This program posts news to machines throughout the local organization.' ;; *.*) $echo 'This program may post news to many machines.' ;; to.*) $echo 'This program may post news to a particular machine.' ;; *) $echo 'This program posts news to everyone on the machine.' ;; esac ans="" while $test "X$ans" = X ; do $echo $n "Are you absolutely sure that you want to do this? [ny] $c" read ans case $ans in y*) ;; f*) ;; h*) $cat <<'EOH' Type n or CR to exit, y to post. EOH ans="" ;; *) exit ;; esac done ;; esac # run nntplist in the background, if necessary if $test -n "$cmdlist"; then ( eval $cmdlist ) >/dev/null 2>&1 & fi case "$FAST_PNEWS" in y*) file='' $echo "" >> $tmpart state=edit ;; *) file=h;; esac while $test "X$file" = Xh ; do $echo "" $echo $n "Prepared file to include [none]: $c" read file || exit 0 case $file in h) $cat <<'EOH' If you have already produced the body of your article, type the filename for it here. If you just want to proceed directly to the editor, type a RETURN. In any event, you will be allowed to edit as many times as you want before you send off the article. EOH ;; '') $echo "" >> $tmpart state=edit ;; *) $cat $file >>$tmpart state=check ;; esac done $echo "" if $test -r $news_sig; then $echo "-- " >> $tmpart $sed 4q $news_sig >> $tmpart fi while : ; do case $state in edit) case $expertise in beginner) $cat $dotdir/.pnewsexpert $cat <<'EOMessage' A temporary file has been created for you to edit. Be sure to leave at least one blank line between the header and the body of your message. (And until a certain bug is fixed all over the net, don't start the body of your message with any indentation, or it may get eaten.) Within the header may be fields that you don't understand. If you don't understand a field (or even if you do), you can simply leave it blank, and it will go away when the article is posted. Type return to get the default editor, or type the name of your favorite editor. EOMessage ;; esac case "${VISUAL-${EDITOR-}}" in '') tmp=h ;; *) tmp='' ;; esac while $test "X$tmp" = Xh ; do $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c" read tmp || state=rescue # exits on EOF case $tmp in h) $cat <<'EOH' Type a return to get the default editor, or type the name of the editor you prefer. The default editor depends on the VISUAL and EDITOR environment variables. EOH ;; '') ;; *) VISUAL=$tmp export VISUAL ;; esac done trap : 2 ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart trap "$rescue" 2 state=check ;; check) # wait for possible background nntplist $test -n "$cmdlist" && wait # warn about long lines, malformed headers, misspelled newsgroups ($artcheck $tmpart 79 $newsgroups $active) 2>/dev/null state=ask ;; ask) $echo "" $echo $n "Check spelling, Send, Abort, Edit, or List? $c" read ans || state=rescue # exits on EOF case "$ans" in [aA]*) state=rescue ;; [eE]*) set $ans case $# in 2) VISUAL="$2" ;; esac state=edit ;; [lL]*) $pager $tmpart state=ask ;; [cC]*|[sS][pP]*) $speller $tmpart state=ask ;; [sS]*) state=send ;; [hH]*) $cat <<'EOH' Type c to check the article's spelling, s to send the article, a to abort and append the article to dead.article, e to edit the article again, or l to list the article with your pager. To invoke an alternate editor, type 'e editor'. EOH esac ;; send) set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'` shift case $# in 2) state=cleanup #posted_to="$2" headerstrip='1,/^[ ]*$/{/^[A-Z][-A-Za-z0-9]*:[ ]*$/d; /^X-ORIGINAL-NEWSGROUPS:/d; /^[Cc][Cc]:/d; /^Distribution: world/d;}' if $sed "$headerstrip" $tmpart | $inews -h ; then : null else state=ask fi cc=`$sed -n '1,/^[ ]*$/{/^[Cc][Cc]:[ ][^ ]/p;}' $tmpart| $sed 's/^[Cc][Cc]:[ ][ ]*//'` if $test "X$cc" != X ; then case "$mailer" in *recmail) set X ;; *sendmail) set X -t ;; *) set X `echo $cc | $sed 's/,/ /g'` ;; esac shift (#$echo "X-Also-Posted-To: $posted_to" case "$mailer" in *recmail|*sendmail) $echo To: $cc ;; esac $sed "$headerstrip" $tmpart if $test -f $homedir/.signature; then $echo "-- " $cat $homedir/.signature fi ) | $mailer $@ fi ;; *) $echo "" $echo "Malformed Newsgroups line." $echo "" $sleep 1 state=edit ;; esac ;; rescue) if $test -s $tmpart; then $cat $tmpart >> ${HOME-$LOGDIR}/dead.article $echo "Article appended to ${HOME-$LOGDIR}/dead.article" $echo "A copy may be temporarily found in $tmpart" chmod -f o-rwx ${HOME-$LOGDIR}/dead.article $tmpart else $echo "Null article discarded." fi exit ;; cleanup) case "${AUTHORCOPY-none}" in none) ;; *) set X ${USER-${LOGNAME-`$who am i`}} unknown shift $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $AUTHORCOPY "From $1 `LANG= date`" if $test $? -eq 0 ; then $echo "Article appended to $AUTHORCOPY" else $echo "Cannot append to $AUTHORCOPY" fi ;; esac exit ;; esac done !NO!SUBS! $eunicefix Pnews chmod 755 Pnews $spitshell >Pnews.header <<'!NO!SUBS!' case $# in 0) ng=h while $test "X$ng" = Xh ; do $echo "" $echo $n "Newsgroup(s): $c" read ng || exit 0 case $ng in h) $cat <<'EOH' Type the name of one or more newsgroups to which you wish to post an article. If you want to post to multiple newsgroups, it is better to do them all at once than to post to each newsgroup individually, which defeats the news reading programs' strategies of eliminating duplicates. Separate multiple newsgroup names with commas. EOH ;; esac done ;; *) ng=$1 shift ;; esac case $ng in *\ *) ng=`$echo "$ng" | $sed 's/[, ] */,/g'` ;; esac case $ng in ddn.*) defdist=inet dist=h ;; *.*) defdist='' dist=h ;; *) defdist='' dist='' ;; esac while $test "X$dist" = Xh ; do if $test -f $lib/distributions; then $echo " " $echo "Your local distribution prefixes are:" $cat $lib/distributions $echo " " else $egrep -v '[ ]none$' < (not "world") EOM fi $echo $n "Distribution ($defdist): $c" read dist || exit 0 case $dist in '') dist="$defdist" ;; esac case "$dist" in h) $cat <<'EOH' The Distribution line may be used to limit the distribution of an article to some subset of the systems that would receive the article based only on the Newsgroups line. For example, if you want to sell your car in talk.auto, and you live in New Jersey, you might want to put "nj" on the Distribution line to avoid advertising in California, which has enough problems of its own. The actual area designators to use depend on where you are, of course. EOH ;; world*|comp*|news*|sci*|rec*|misc*|soc*|talk*|alt*) dist='' ;; ''|$loc|$org|$city|$state|$multistate|$cntry|$cont|$defdist) ;; *) if $test -f $lib/distributions && \ $egrep "^$dist[ ]" $lib/distributions >$tmpart && \ $test -s $tmpart; then : null else $echo "Unrecognized distribution prefix--type h for help, CR to use anyway." defdist=$dist dist=h fi ;; esac done follow="" # LCP 16-Oct-91 Subject line is required. Make it a little more # difficult to omit. Added "while : ; do", ... "done", and "if" # at end of while loop. while : ; do case $# in 0) title=h while $test "X$title" = Xh ; do $echo "" $echo $n "Title/Subject: $c" read title || exit 0 case $title in h) $cat <<'EOH' Type the title for your article. Please make it as informative as possible (within reason) so that people who aren't interested won't have to read the article to find out they aren't interested. This includes marking movie spoilers as (spoiler), and rotated jokes as (rot 13). EOH ;; esac done ;; *) title="$*" # LCP 16-Oct-91 Added "set" and "shift". Must insure $# is 0 # in case the title is all white space and we make another # pass thru this loop. set X shift ;; esac if expr "X$title" : "^X[ ]*$" > /dev/null 2>&1 then $cat <<'EOH' Articles without a "Subject:" line will not be accepted by the News system. Please give a Title/Subject line for your article. EOH else break fi done # now build a file with a header for them to edit set X ${USER-${LOGNAME-`$who am i`}} shift logname=$1 case $logname in *!*) logname=`expr "$logname" : '!\(.*\)$'` ;; esac case ${NAME-$nametype} in bsd) if $test "X$ypmatch" != X; then fullname=`$ypmatch $logname passwd 2>/dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/"` elif $test "X$nidump" != X; then fullname=`$nidump passwd / | $sed -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"` fi if $test "X$fullname" = X; then fullname=`$sed /dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q"` fi if $test "X$fullname" = X; then fullname=`$sed $tmpart < Pnews.h.new ;; *) $sed < Pnews.header -e '/^#NORMAL/s/^#NORMAL//' > Pnews.h.new ;; esac $mv Pnews.h.new Pnews.header $eunicefix Pnews.header trn-3.6/samples/0000775000000000000000000000000005654303565010534 5ustar trn-3.6/samples/subscriptions0000664000000000000000000000011505650602723013355 0ustar general news.announce.important news.announce.newusers news.software.readers trn-3.6/rt-util.c0000664000000000000000000003244705655576361010655 0ustar /* $Id: rt-util.c,v 3.0 1992/12/14 00:14:12 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "cache.h" #include "ngdata.h" #include "artio.h" #include "rthread.h" #include "rt-select.h" #include "term.h" #include "nntp.h" #include "INTERN.h" #include "rt-util.h" /* Name-munging routines written by Ross Ridge. ** Enhanced by Wayne Davison. */ /* Extract the full-name part of an email address, returning NULL if not ** found. */ char * extract_name(name) char *name; { char *s; char *lparen, *rparen; char *langle; while (isspace(*name)) { name++; } lparen = index(name, '('); rparen = rindex(name, ')'); langle = index(name, '<'); if (!lparen && !langle) { return NULL; } else if (langle && (!lparen || !rparen || lparen > langle || rparen < langle)) { if (langle == name) { return NULL; } *langle = '\0'; } else { name = lparen; *name++ = '\0'; while (isspace(*name)) { name++; } if (name == rparen) { return NULL; } if (rparen != NULL) { *rparen = '\0'; } } if (*name == '"') { name++; while (isspace(*name)) { name++; } if ((s = rindex(name, '"')) != NULL) { *s = '\0'; } } return name; } /* If necessary, compress a net user's full name by playing games with ** initials and the middle name(s). If we start with "Ross Douglas Ridge" ** we try "Ross D Ridge", "Ross Ridge", "R D Ridge" and finally "R Ridge" ** before simply truncating the thing. We also turn "R. Douglas Ridge" ** into "Douglas Ridge" and "Ross Ridge D.D.S." into "Ross Ridge" as a ** first step of the compaction, if needed. */ char * compress_name(name, max) char *name; int max; { register char *s, *last, *mid, *d; register int len, namelen, midlen; int notlast; try_again: /* First remove white space from both ends. */ while (isspace(*name)) { name++; } if ((len = strlen(name)) == 0) { return name; } s = name + len - 1; while (isspace(*s)) { s--; } s[1] = '\0'; if (s - name + 1 <= max) { return name; } /* Look for characters that likely mean the end of the name ** and the start of some hopefully uninteresting additional info. ** Spliting at a comma is somewhat questionalble, but since ** "Ross Ridge, The Great HTMU" comes up much more often than ** "Ridge, Ross" and since "R HTMU" is worse than "Ridge" we do ** it anyways. */ for (d = name + 1; *d; d++) { if (*d == ',' || *d == ';' || *d == '(' || *d == '@' || (*d == '-' && (d[1] == '-' || d[1] == ' '))) { *d-- = '\0'; s = d; break; } } /* Find the last name */ do { notlast = 0; while (isspace(*s)) { s--; } s[1] = '\0'; len = s - name + 1; if (len <= max) { return name; } /* If the last name is an abbreviation it's not the one we want. */ if (*s == '.') notlast = 1; while (!isspace(*s)) { if (s == name) { /* only one name */ name[max] = '\0'; return name; } if (isdigit(*s)) { /* probably a phone number */ notlast = 1; /* so chuck it */ } s--; } } while (notlast); last = s-- + 1; /* Look for a middle name */ while (isspace(*s)) { /* get rid of any extra space */ len--; s--; } mid = name; while (!isspace(*mid)) { mid++; } namelen = mid - name + 1; if (mid == s+1) { /* no middle name */ mid = 0; midlen = 0; } else { *mid++ = '\0'; while (isspace(*mid)) { len--; mid++; } midlen = s - mid + 2; /* If first name is an initial and middle isn't and it all fits ** without the first initial, drop it. */ if (len > max && mid != s) { if (len - namelen <= max && ((mid[1] != '.' && (!name[1] || (name[1] == '.' && !name[2]))) || (*mid == '"' && *s == '"'))) { len -= namelen; name = mid; namelen = midlen; mid = 0; } else if (*mid == '"' && *s == '"') { if (midlen > max) { name = mid+1; *s = '\0'; goto try_again; } len = midlen; last = mid; namelen = 0; mid = 0; } } } s[1] = '\0'; if (mid && len > max) { /* Turn middle names into intials */ len -= s - mid + 2; d = s = mid; while (*s) { if (isalpha(*s)) { if (d != mid) { *d++ = ' '; } *d++ = *s++; } while (*s && !isspace(*s)) { s++; } while (isspace(*s)) { s++; } } if (d != mid) { *d = '\0'; midlen = d - mid + 1; len += midlen; } else { mid = 0; } } if (len > max) { /* If the first name fits without the middle initials, drop them */ if (mid && len - midlen <= max) { len -= midlen; mid = 0; } else if (namelen > 0) { /* Turn the first name into an initial */ len -= namelen - 2; name[1] = '\0'; namelen = 2; if (len > max) { /* Dump the middle initials (if present) */ if (mid) { len -= midlen; mid = 0; } if (len > max) { /* Finally just truncate the last name */ last[max - 2] = '\0'; } } } else namelen = 0; } /* Paste the names back together */ d = name + namelen; if (namelen) d[-1] = ' '; if (mid) { strcpy(d, mid); d += midlen; d[-1] = ' '; } strcpy(d, last); return name; } /* Compress an email address, trying to keep as much of the local part of ** the addresses as possible. The order of precence is @ ! %, but ** @ % ! may be better... */ static char * compress_address(name, max) char *name; int max; { char *s, *at, *bang, *hack, *start; int len; /* Remove white space from both ends. */ while (isspace(*name)) { name++; } if ((len = strlen(name)) == 0) { return name; } s = name + len - 1; while (isspace(*s)) { s--; } s[1] = '\0'; if (*name == '<') { name++; if (*s == '>') { *s-- = '\0'; } } if ((len = s - name + 1) <= max) { return name; } at = bang = hack = NULL; for (s = name + 1; *s; s++) { /* If there's whitespace in the middle then it's probably not ** really an email address. */ if (isspace(*s)) { name[max] = '\0'; return name; } switch (*s) { case '@': if (at == NULL) { at = s; } break; case '!': if (at == NULL) { bang = s; hack = NULL; } break; case '%': if (at == NULL && hack == NULL) { hack = s; } break; } } if (at == NULL) { at = name + len; } if (hack != NULL) { if (bang != NULL) { if (at - bang - 1 >= max) { start = bang + 1; } else if (at - name >= max) { start = at - max; } else { start = name; } } else { start = name; } } else if (bang != NULL) { if (at - name >= max) { start = at - max; } else { start = name; } } else { start = name; } if (len - (start - name) > max) { start[max] = '\0'; } return start; } /* Fit the author name in chars. Uses the comment portion if present ** and pads with spaces. */ char * compress_from(ap, size) ARTICLE *ap; int size; { char *s, *t; int len; for (t = cmd_buf, s = ap && ap->from? ap->from : nullstr; *s; ) { if ((unsigned char)*s < ' ') *t++ = ' ', s++; else *t++ = *s++; } *t = '\0'; if ((s = extract_name(cmd_buf)) != NULL) s = compress_name(s, size); else s = compress_address(cmd_buf, size); len = strlen(s); if (!len) { strcpy(s,"NO NAME"); len = 7; } while (len < size) s[len++] = ' '; s[size] = '\0'; return s; } #define EQ(x,y) ((isupper(x) ? tolower(x) : (x)) == (y)) /* Parse the subject to skip past any "Re[:^]"s at the start. */ char * get_subject_start(str) register char *str; { while (*str && (unsigned char)*str <= ' ') str++; while (EQ(str[0], 'r') && EQ(str[1], 'e')) { /* check for Re: */ register char *cp = str + 2; if (*cp == '^') { /* allow Re^2: */ while (*++cp <= '9' && *cp >= '0') ; } if (*cp != ':') break; while (*++cp == ' ') ; str = cp; } return str; } /* Output a subject in chars. Does intelligent trimming that tries to ** save the last two words on the line, excluding "(was: blah)" if needed. */ char * compress_subj(ap, max) ARTICLE *ap; int max; { register char *cp; register int len; ARTICLE *first; if (!ap) return ""; /* Put a preceeding '>' on subjects that are replies to other articles */ cp = buf; first = (ThreadedGroup? ap->subj->thread : ap->subj->articles); if (ap != first || (ap->flags & AF_HAS_RE) || !(!(ap->flags&AF_READ) ^ sel_rereading)) *cp++ = '>'; strcpy(cp, ap->subj->str + 4); /* Remove "(was: oldsubject)", because we already know the old subjects. ** Also match "(Re: oldsubject)". Allow possible spaces after the ('s. */ for (cp = buf; (cp = index(cp+1, '(')) != Nullch;) { while (*++cp == ' ') ; if (EQ(cp[0], 'w') && EQ(cp[1], 'a') && EQ(cp[2], 's') && (cp[3] == ':' || cp[3] == ' ')) { *--cp = '\0'; break; } if (EQ(cp[0], 'r') && EQ(cp[1], 'e') && ((cp[2]==':' && cp[3]==' ') || (cp[2]=='^' && cp[4]==':'))) { *--cp = '\0'; break; } } len = strlen(buf); if (!unbroken_subjects && len > max) { char *last_word; /* Try to include the last two words on the line while trimming */ if ((last_word = rindex(buf, ' ')) != Nullch) { char *next_to_last; *last_word = '\0'; if ((next_to_last = rindex(buf, ' ')) != Nullch) { if (next_to_last-buf >= len - max + 3 + 10-1) cp = next_to_last; else cp = last_word; } else cp = last_word; *last_word = ' '; if (cp-buf >= len - max + 3 + 10-1) { sprintf(buf + max - (len-(cp-buf)+3), "...%s", cp + 1); len = max; } } } if (len > max) buf[max] = '\0'; return buf; } #ifndef HAS_STRCASECMP static unsigned char casemap[256] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }; int strcasecmp(s1, s2) register char *s1, *s2; { do { if (casemap[(unsigned)*s1++] != casemap[(unsigned)*s2]) return casemap[(unsigned)s1[-1]] - casemap[(unsigned)*s2]; } while (*s2++ != '\0'); return 0; } int strncasecmp(s1, s2, len) register char *s1, *s2; register int len; { while (len--) { if (casemap[(unsigned)*s1++] != casemap[(unsigned)*s2]) return casemap[(unsigned)s1[-1]] - casemap[(unsigned)*s2]; if (*s2++ == '\0') break; } return 0; } #endif /* Modified version of a spinner originally found in Clifford Adams' strn. */ static char spinchars[] = {'|','/','-','\\'}; static int spin_place; /* represents place in spinchars array */ static int spin_count; /* counter for when to spin */ static int spin_level INIT(0); /* used to allow non-interfering nested spins */ static int spin_mode; static ART_NUM spin_art; static ART_POS spin_tell; void setspin(mode) int mode; { switch (mode) { case SPIN_FOREGROUND: case SPIN_BACKGROUND: if (!spin_level++) { if ((spin_art = openart) != 0 && artfp) spin_tell = tellart(); spin_count = 1; /* not 0 to prevent immediate spin display */ spin_place = 1; /* start with slash... */ } spin_mode = mode; break; case SPIN_POP: if (--spin_level > 0) break; /* FALL THROUGH */ case SPIN_OFF: spin_level = 0; if (spin_place > 1) { /* we have spun at least once */ putchar(spin_char); /* get rid of spin character */ backspace(); fflush(stdout); spin_place = 0; } if (spin_art) { artopen(spin_art); if (artfp) seekart(spin_tell); /* do not screw up the pager */ spin_art = 0; } spin_mode = SPIN_OFF; break; } } void spin(count) int count; /* modulus for the spin... */ { if (!spin_level || (!bkgnd_spinner && spin_mode == SPIN_BACKGROUND)) return; if (!(spin_count++%count)) { if (spin_mode == SPIN_FOREGROUND) putchar('.'); else { putchar(spinchars[spin_place++%4]); backspace(); } fflush(stdout); } } bool inbackground() { return spin_mode == SPIN_BACKGROUND; } trn-3.6/artio.c0000664000000000000000000000474705645405230010356 0ustar /* $Id: artio.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "cache.h" #include "rthread.h" #include "head.h" #include "nntp.h" #include "art.h" #include "bits.h" #include "final.h" #include "ngdata.h" #include "INTERN.h" #include "artio.h" void artio_init() { ; } /* open an article, unless it's already open */ FILE * artopen(artnum) ART_NUM artnum; { #ifndef USE_NNTP char artname[MAXFILENAME]; /* filename of current article */ #endif ARTICLE *ap = find_article(artnum); if (!ap || !artnum || (ap->flags & (AF_MISSING|AF_FAKE)) == AF_MISSING) { errno = ENOENT; return Nullfp; } if (openart == artnum) { /* this article is already open? */ seekart(0L); /* just get to the beginning */ return artfp; /* and say we succeeded */ } if (artfp != Nullfp) { /* it was somebody else? */ fclose(artfp); /* put them out of their misery */ openart = 0; /* and remember them no more */ } retry_open: #ifdef USE_NNTP artfp = nntp_body(artnum); #else sprintf(artname,"%ld",(long)artnum); artfp = fopen(artname,"r"); #endif if (!artfp) { #ifdef ETIMEDOUT if (errno == ETIMEDOUT) goto retry_open; #endif if (errno == EINTR) goto retry_open; uncache_article(ap,FALSE); } else { #ifdef LINKART char tmpbuf[256]; char *s; if (!fstat(fileno(artfp),&filestat) && filestat.st_size < sizeof tmpbuf) { fgets(tmpbuf,sizeof tmpbuf,artfp); if (*tmpbuf == '/') { /* is a "link" to another article */ fclose(artfp); if (s=index(tmpbuf,'\n')) *s = '\0'; if (!(artfp = fopen(tmpbuf,"r"))) { uncache_article(ap,FALSE); } else { if (*linkartname) free(linkartname); linkartname = savestr(tmpbuf); } } else seekart(0L); /* get back to the beginning */ } #endif openart = artnum; /* remember what we did here */ } return artfp; /* and return either fp or NULL */ } trn-3.6/uudecode.c0000664000000000000000000002260305551731003011020 0ustar /* $Id: uudecode.c,v 4.4.3.1 1992/02/01 03:09:32 $ * * Decode one or more uuencoded articles back to binary form. * Trn version created by Wayne Davison. * Formerly the nn version by Kim Storm. * From the Berkeley original, modified by MSD, RDR, JPHD & WLS. */ /* * This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "respond.h" #include "decode.h" #define MAXCHAR 256 #define NORMLEN 64 /* allows for 84 encoded chars per line */ #define SEQMAX 'z' #define SEQMIN 'a' static char seqc; static int first, secnd, check, numl; static char blank; static int chtbl[MAXCHAR], cdlen[NORMLEN + 3]; static int state; static bool Xflag; static int expecting_part; static int decode_line _((char *)); static void inittbls _((void)); static void gettable _((FILE *)); #define NO_ADVANCE 0x10 #define FIND_BEGIN 0x01 #define AFTER_ERROR_FIND_BEGIN 0x02 #define DECODE_TEXT 0x03 #define SKIP_TRAILING (0x04 | NO_ADVANCE) #define SKIP_LEADING 0x05 #define FOUND_END (0x06 | NO_ADVANCE) #define DECODE_ERROR (0x07 | NO_ADVANCE) #define OTHER_ERROR (0x08 | NO_ADVANCE) #define NEW_BEGIN (0x09 | NO_ADVANCE) void uud_start() { Xflag = FALSE; expecting_part = 0; seqc = SEQMAX; check = 1; first = 1; secnd = 0; state = FIND_BEGIN; } int uudecode(in) FILE *in; { int mode, onedone, lens; char buff[LBUFLEN]; numl = onedone = 0; if (state == FIND_BEGIN) inittbls(); /* * search for header or translation table line. */ while ((state & NO_ADVANCE) || fgets(buff, sizeof buff, in) != Nullch) { numl++; switch (state) { case NEW_BEGIN: if (decode_fp != Nullfp) { if (expecting_part) { register int got_part = 0; if (strnEQ(buff + 6, "part ", 5)) { register char *bp; for (bp = buff + 11; islower(*bp); bp++) got_part = got_part * 26 + *bp - 'a'; } if (expecting_part == got_part) { state = DECODE_TEXT; break; } printf("Expecting part %d; got part %d.\n", expecting_part + 1, got_part + 1); if (got_part) { state = SKIP_LEADING; return -1; } } decode_end(); sleep(2); Xflag = FALSE; expecting_part = 0; } state = FIND_BEGIN; /* fall thru */ case FIND_BEGIN: case AFTER_ERROR_FIND_BEGIN: if (strnEQ(buff, "table", 5)) { gettable(in); continue; } if (strnEQ(buff, "begin ", 6) || strnEQ(buff, "Xbegin ", 7)) { lens = strlen(buff)-1; if (buff[lens] == '\n') buff[lens] = '\0'; if(sscanf(buff+6,"%o%s", &mode, decode_fname) != 2) { register char *bp = buff + 6; if (*bp == ' ') bp++; if (strnEQ(bp, "part ", 5)) { register int got_part = 0; for (bp = bp + 5; islower(*bp); bp++) got_part = got_part * 26 + *bp - 'a'; printf("Expecting part 1; got part %d.\n", got_part + 1); return -1; } continue; } Xflag = (*buff == 'X'); sprintf(decode_dest, "%s/%s", extractdest, decode_fname); if ((decode_fp = fopen(decode_dest, FOPEN_WB)) == Nullfp) { printf("Cannot create file: %s\n", decode_dest); goto err; } chmod(decode_dest, mode); printf("Decoding: %s\n", decode_fname); state = DECODE_TEXT; } continue; case SKIP_LEADING: state = decode_line(buff); continue; case DECODE_TEXT: state = decode_line(buff); onedone = 1; continue; case FOUND_END: fclose(decode_fp); decode_fp = Nullfp; Xflag = FALSE; expecting_part = 0; state = FIND_BEGIN; printf("Done.\n"); continue; case SKIP_TRAILING: printf("(Continued)\n"); state = SKIP_LEADING; return 0; case DECODE_ERROR: state = SKIP_TRAILING; continue; case OTHER_ERROR: fclose(decode_fp); decode_fp = Nullfp; Xflag = FALSE; expecting_part = 0; state = AFTER_ERROR_FIND_BEGIN; goto err; } } if (onedone) { if (state == DECODE_TEXT) { printf("(Continued)\n"); state = SKIP_LEADING; } return 0; } if (state == AFTER_ERROR_FIND_BEGIN) return -1; printf("Couldn't find anything to decode.\n"); err: sleep(2); return -1; } /* * decode one line and write it out using decode_fp */ static int decode_line(buff) char *buff; { char outl[LBUFLEN]; register char *bp, *ut; register int *trtbl = chtbl; register int n; register int blen; /* binary length (from decoded file) */ register int rlen; /* calculated input line length */ register int len; /* actual input line length */ register int dash; /* number of '-'s encountered on a line */ /* If it's too high, we reject the line */ # define REJECT(buf,rlen,len) \ ((*buf == 'M' && len > rlen + 5) \ || (*buf != 'M' && len != rlen && len != rlen+1) \ || (strnEQ(buf, "BEGIN", 5)) \ || (strnEQ(buf, "END", 3))) if (Xflag) { if (*buff == 'X') buff++; else *buff = 'x'; /* force a mis-parse of a non-x'ed line */ } len = strlen(buff); if (--len <= 0) return state; buff[len] = '\0'; /* * Get the binary line length. */ if ((blen = trtbl[buff[0]]) < 0) { if (state == SKIP_LEADING) { if (strnEQ(buff, "begin ", 6)) return NEW_BEGIN; return SKIP_LEADING; } /* * end of uuencoded file ? */ if (strnEQ(buff, "end", 3)) return FOUND_END; /* * end of current file ? : get next one. */ if (strnEQ(buff, "include ", 8)) { for (bp = buff + 8; *bp; bp++) { if (bp[0] == '.' && bp[1] == 'u') { expecting_part = (bp[2] - 'a') * 26 + bp[3] - 'a'; break; } } } /* * trailing garbage */ return SKIP_TRAILING; } rlen = cdlen[blen]; if (state == SKIP_LEADING && REJECT(buff,rlen,len)) return SKIP_LEADING; /* * Is it the empty line before the end line ? */ if (blen == 0) return state; if (REJECT(buff,rlen,len)) return SKIP_TRAILING; /* * Pad with blanks. */ for (bp = buff + len, n = rlen - len; --n >= 0; ) *bp++ = blank; /* * Verify */ for (n = rlen, bp = buff, dash = 0; --n >= 0; bp++) { if (trtbl[*bp] < 0) { if (state == SKIP_LEADING) return SKIP_LEADING; return DECODE_ERROR; } if (*bp == '-') dash++; } if (dash * 100 / rlen > 33) /* more than 1/3 dashes? */ if (state == SKIP_LEADING) return SKIP_LEADING; /* -> reject */ else return SKIP_TRAILING; /* * Check for uuencodes that append a 'z' to each line.... */ if (check) if (secnd) { secnd = 0; if (buff[rlen] == SEQMAX) check = 0; } else if (first) { first = 0; secnd = 1; if (buff[rlen] != SEQMAX) check = 0; } /* * There we check. */ if (check) { if (buff[rlen] != seqc) { if (state == SKIP_LEADING) return SKIP_LEADING; return DECODE_ERROR; } if (--seqc < SEQMIN) seqc = SEQMAX; } /* * output a group of 3 bytes (4 input characters). * the input chars are pointed to by p, they are to * be output to file f. blen is used to tell us not to * output all of them at the end of the file. */ ut = outl; n = blen; bp = &buff[1]; while (--n >= 0) { *(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4; if (n > 0) { *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2); n--; } if (n > 0) { *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]]; n--; } bp += 4; } if ((int)fwrite(outl, 1, blen, decode_fp) <= 0) { printf("Error on writing decoded file\n"); return OTHER_ERROR; } return DECODE_TEXT; } /* * Install the table in memory for later use. */ static void inittbls() { register int i, j; /* * Set up the default translation table. */ for (i = 0; i < ' '; i++) chtbl[i] = -1; for (i = ' ', j = 0; i < ' ' + 64; i++, j++) chtbl[i] = j; for (i = ' ' + 64; i < MAXCHAR; i++) chtbl[i] = -1; chtbl['`'] = chtbl[' ']; /* common mutation */ chtbl['~'] = chtbl['^']; /* another common mutation */ blank = ' '; /* * set up the line length table, to avoid computing lotsa * and / ... */ cdlen[0] = 1; for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4) cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j)); } static void gettable(in) FILE *in; { char buff[LBUFLEN]; register int c, n = 0; register char *cpt; for (c = 0; c < MAXCHAR; c++) chtbl[c] = -1; for (;;) { if (fgets(buff, sizeof buff, in) == Nullch) { printf("EOF while in translation table.\n"); return; } numl++; if (strnEQ(buff, "begin", 5)) { printf("Incomplete translation table.\n"); return; } cpt = buff + strlen(buff) - 1; *cpt = ' '; while (*cpt == ' ') { *cpt = 0; cpt--; } cpt = buff; while (c = *cpt) { if (chtbl[c] != -1) { printf("Duplicate char in translation table.\n"); return; } if (n == 0) blank = c; chtbl[c] = n++; if (n >= 64) return; cpt++; } } } trn-3.6/artsrch.c0000664000000000000000000002452405657552727010722 0ustar /* $Id: artsrch.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "term.h" #include "util.h" #include "intrp.h" #include "cache.h" #include "bits.h" #include "kfile.h" #include "head.h" #include "final.h" #include "nntp.h" #include "ng.h" #include "ngdata.h" #include "ngstuff.h" #include "artio.h" #include "rthread.h" #include "rt-select.h" #include "INTERN.h" #include "artsrch.h" void artsrch_init() { #ifdef ARTSEARCH init_compex(&sub_compex); init_compex(&art_compex); #endif } /* search for an article containing some pattern */ #ifdef ARTSEARCH int art_search(patbuf,patbufsiz,get_cmd) char *patbuf; /* if patbuf != buf, get_cmd must */ int patbufsiz; int get_cmd; /* be set to FALSE!!! */ { char *pattern; /* unparsed pattern */ register char cmdchr = *patbuf; /* what kind of search? */ register char *s; bool backward = cmdchr == '?' || cmdchr == Ctl('p'); /* direction of search */ COMPEX *compex; /* which compiled expression */ char *cmdlst = Nullch; /* list of commands to do */ int normal_return = SRCH_NOTFOUND; /* assume no commands */ bool saltaway = FALSE; /* store in KILL file? */ char howmuch; /* search scope: subj/from/Hdr/head/art */ char *srchhdr; /* header to search if Hdr scope */ bool topstart = 0; bool doread; /* search read articles? */ bool foldcase = TRUE; /* fold upper and lower case? */ int ignorethru = 0; /* should we ignore the thru line? */ ART_NUM srchfirst; int_count = 0; if (cmdchr == '/' || cmdchr == '?') { /* normal search? */ if (get_cmd && buf == patbuf) if (!finish_command(FALSE)) /* get rest of command */ return SRCH_ABORT; compex = &art_compex; if (patbuf[1]) { howmuch = ARTSCOPE_SUBJECT; srchhdr = Nullch; doread = FALSE; } else { howmuch = art_howmuch; srchhdr = art_srchhdr; doread = art_doread; } s = cpytill(buf,patbuf+1,cmdchr);/* ok to cpy buf+1 to buf */ pattern = buf; if (*pattern) { if (*lastpat) free(lastpat); lastpat = savestr(pattern); } if (*s) { /* modifiers or commands? */ for (s++; *s && index("KabrcfhtBHIN",*s); s++) { switch (*s) { case 'f': /* scan the From line */ howmuch = ARTSCOPE_FROM; break; case 'H': /* scan a specific header */ howmuch = ARTSCOPE_ONEHDR; srchhdr = s + 1; if (!(s = index(srchhdr, ':'))) { s = buf + strlen(buf); *s++ = ':'; *s = '\0'; } else s++; srchhdr = savestr(srchhdr); goto loop_break; case 'h': /* scan header */ howmuch = ARTSCOPE_HEAD; break; case 'b': /* scan body sans signature */ howmuch = ARTSCOPE_BODY_NOSIG; break; case 'B': /* scan body */ howmuch = ARTSCOPE_BODY; break; case 'a': /* scan article */ howmuch = ARTSCOPE_ARTICLE; break; case 't': /* start from the top */ topstart = TRUE; break; case 'r': /* scan read articles */ doread = TRUE; break; case 'K': /* put into KILL file */ saltaway = TRUE; break; case 'c': /* make search case sensitive */ foldcase = FALSE; break; case 'I': /* ignore the killfile thru line */ ignorethru = 1; break; case 'N': /* override ignore if -k was used */ ignorethru = -1; break; } } loop_break:; } while (isspace(*s) || *s == ':') s++; if (*s) { #ifdef OLD_RN_WAY if (*s == 'm' || *s == 'M') #else if (*s == 'm') #endif doread = TRUE; if (*s == 'k') /* grandfather clause */ *s = 'j'; cmdlst = savestr(s); normal_return = SRCH_DONE; } art_howmuch = howmuch; if (art_srchhdr != srchhdr) { if (art_srchhdr) free(art_srchhdr); art_srchhdr = srchhdr; } art_doread = doread; if (srchahead) srchahead = -1; } else { register char *h; howmuch = ARTSCOPE_SUBJECT; /* just search subjects */ srchhdr = Nullch; doread = (cmdchr == Ctl('p')); if (cmdchr == Ctl('n')) normal_return = SRCH_SUBJDONE; compex = &sub_compex; pattern = patbuf+1; strcpy(pattern,": *"); h = pattern + strlen(pattern); interp(h,patbufsiz - (h-patbuf),"%\\s"); /* fetch current subject */ if (cmdchr == 'k' || cmdchr == 'K' || cmdchr == ',' || cmdchr == '+' || cmdchr == '.') { if (cmdchr != 'k') saltaway = TRUE; normal_return = SRCH_DONE; if (cmdchr == '+') { cmdlst = savestr("+"); ignorethru = 1; } else if (cmdchr == '.') { cmdlst = savestr("."); ignorethru = 1; } else { if (cmdchr == ',') cmdlst = savestr(","); mark_as_read(); /* this article has this subject */ } if (!*h) { #ifdef VERBOSE IF(verbose) fputs("\nCannot process a null subject.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNull subject.\n",stdout) FLUSH; #endif return SRCH_ABORT; } #ifdef VERBOSE else if (verbose) if (cmdchr != '+' && cmdchr != '.') printf("\nMarking subject \"%s\" as read.\n",h) FLUSH; else printf("\nSelecting subject \"%s\".\n",h) FLUSH; #endif } else if (!srchahead) srchahead = -1; { /* compensate for notesfiles */ register int i; for (i = 24; *h && i--; h++) if (*h == '\\') h++; *h = '\0'; } #ifdef DEBUG if (debug) { printf("\npattern = %s\n",pattern) FLUSH; } #endif } if ((s = compile(compex,pattern,TRUE,foldcase)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; return SRCH_ABORT; } #ifdef KILLFILES if (saltaway) { char saltbuf[LBUFLEN], *f; s = saltbuf; f = pattern; *s++ = '/'; while (*f) { if (*f == '/') *s++ = '\\'; *s++ = *f++; } *s++ = '/'; if (doread) *s++ = 'r'; if (ignorethru) *s++ = (ignorethru == 1 ? 'I' : 'N'); if (howmuch != ARTSCOPE_SUBJECT) { *s++ = scopestr[howmuch]; if (howmuch == ARTSCOPE_ONEHDR) { safecpy(s,srchhdr,LBUFLEN-(s-saltbuf)); s = index(s,':'); if (!s) s = saltbuf+LBUFLEN-2; } } *s++ = ':'; if (!cmdlst) cmdlst = savestr("j"); safecpy(s,cmdlst,LBUFLEN-(s-saltbuf)); kf_append(saltbuf); } #endif if (cmdlst && index(cmdlst,'=')) normal_return = SRCH_ERROR; /* listing subjects is an error? */ if (get_cmd) { fputs("\nSearching...\n",stdout) FLUSH; /* give them something to read */ } if (mode == 't') { if (!cmdlst) if (sel_mode == SM_ARTICLE)/* set the selector's default command */ cmdlst = savestr("+"); else cmdlst = savestr("++"); if (sel_rereading) doread = TRUE; normal_return = SRCH_DONE; } if (ignorethru == 0 && kill_thru_kludge && cmdlst && (*cmdlst == '+' || *cmdlst == '.')) ignorethru = 1; srchfirst = doread? absfirst : (mode != 'k' || ignorethru > 0)? firstart : killfirst; if (topstart || art == 0) { art = lastart+1; topstart = FALSE; } if (backward) { if (cmdlst && art <= lastart) art++; /* include current article */ } else { if (art > lastart) art = srchfirst-1; else if (cmdlst && art >= absfirst) art--; /* include current article */ } if (srchahead > 0) { if (!backward) art = srchahead - 1; srchahead = -1; } assert(!cmdlst || *cmdlst); perform_cnt = 0; for (;;) { /* check if we're out of articles */ if (backward? (--art < srchfirst) : (++art > lastart)) { if (cmdlst) free(cmdlst); return normal_return; } if (int_count) { int_count = 0; if (cmdlst) free(cmdlst); return SRCH_INTR; } artp = article_ptr(art); if (doread || !(artp->flags & AF_READ)) { if (wanted(compex,art,howmuch)) { /* does the shoe fit? */ if (cmdlst) { if (perform(cmdlst,TRUE)) { free(cmdlst); return SRCH_INTR; } } else return SRCH_FOUND; } else if (!cmdlst && ! (art%50)) { printf("...%ld",(long)art); fflush(stdout); } } } } /* determine if article fits pattern */ /* returns TRUE if it exists and fits pattern, FALSE otherwise */ bool wanted(compex, artnum, scope) COMPEX *compex; ART_NUM artnum; char_int scope; { ARTICLE *ap = find_article(artnum); if (!ap || (ap->flags & AF_MISSING)) return FALSE; switch (scope) { case ARTSCOPE_SUBJECT: strcpy(buf,"Subject: "); strncpy(buf+9,fetchsubj(artnum,FALSE),256); #ifdef DEBUG if (debug & DEB_SEARCH_AHEAD) printf("%s\n",buf) FLUSH; #endif break; case ARTSCOPE_FROM: strcpy(buf, "From: "); strncpy(buf+6,fetchfrom(artnum,FALSE),256); break; case ARTSCOPE_ONEHDR: { int header_num; char *s; assert(art_srchhdr != Nullch); s = index(art_srchhdr,':'); header_num = set_line_type(art_srchhdr, s); if (header_num == SOME_LINE) return FALSE; /* FIX ME */ untrim_cache = TRUE; strcpy(buf, art_srchhdr); sprintf(buf + (s-art_srchhdr), ": %s", prefetchlines(artnum,header_num,FALSE)); untrim_cache = FALSE; break; } default: if (scope != ARTSCOPE_BODY && scope != ARTSCOPE_BODY_NOSIG) { if (!parseheader(artnum)) return FALSE; /* see if it's in the header */ if (execute(compex,headbuf) != Nullch) /* does it match? */ return TRUE; /* say, "Eureka!" */ if (scope < ARTSCOPE_ARTICLE) return FALSE; } if (!artopen(artnum)) /* ensure we have the body */ return FALSE; seekart(htype[PAST_HEADER].ht_minpos); /* loop through each line of the article */ while (readart(buf,LBUFLEN) != Nullch) { if (scope == ARTSCOPE_BODY_NOSIG && *buf == '-' && buf[1] == '-' && (buf[2] == '\n' || (buf[2] == ' ' && buf[3] == '\n'))) { #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif break; } if (execute(compex,buf) != Nullch) {/* does it match? */ #ifdef USE_NNTP nntp_finishbody(FB_SILENT); #endif return TRUE; /* say, "Eureka!" */ } } return FALSE; /* out of article, so no match */ } return execute(compex,buf) != Nullch; } #endif trn-3.6/ngdata.c0000664000000000000000000001505505644354550010477 0ustar /* $Id: ngdata.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "rcstuff.h" #include "trn.h" #include "cache.h" #include "bits.h" #include "head.h" #include "rthread.h" #include "rt-select.h" #include "ng.h" #include "intrp.h" #include "kfile.h" #include "final.h" #include "rcln.h" #include "term.h" #include "util.h" #include "nntp.h" #include "ndir.h" #include "INTERN.h" #include "ngdata.h" /* open the active file */ void ngdata_init() { #ifdef USE_NNTP switch (nntp_list("active", "control", 7)) { case 1: do { nntp_gets(ser_line, sizeof ser_line); } while (!NNTP_LIST_END(ser_line)); /* FALL THROUGH */ case 0: /*actfp = NULL;*/ fuzzyGet = 0; break; case -1: #endif ngdatahash_init(); if (fseek(actfp,0L,0) == -1) { /* just get to the beginning */ printf("Error seeking in active file.\n") FLUSH; finalize(1); } #ifdef USE_NNTP break; } #endif } bool access_ng() { #ifdef USE_NNTP ART_NUM old_first = abs1st[ng]; if (!nntp_group(ngname,ng)) { toread[ng] = TR_BOGUS; return FALSE; } if ((lastart = getngsize(ng)) < 0) /* this cannot happen (laugh here) */ return FALSE; absfirst = abs1st[ng]; lastart = ngmax[ng]; if (absfirst > old_first) checkexpired(ng,absfirst); #else /* !USE_NNTP */ if (eaccess(ngdir,5)) { /* directory read protected? */ if (eaccess(ngdir,0)) { # ifdef VERBOSE IF(verbose) printf("\nNewsgroup %s does not have a spool directory!\n", ngname) FLUSH; ELSE # endif # ifdef TERSE printf("\nNo spool for %s!\n",ngname) FLUSH; # endif } else { # ifdef VERBOSE IF(verbose) printf("\nNewsgroup %s is not currently accessible.\n", ngname) FLUSH; ELSE # endif # ifdef TERSE printf("\n%s not readable.\n",ngname) FLUSH; # endif } toread[ng] = TR_NONE; /* make this newsgroup temporarily invisible */ return FALSE; } /* chdir to newsgroup subdirectory */ if (chdir(ngdir)) { printf(nocd,ngdir) FLUSH; return FALSE; } if ((lastart = getngsize(ng)) < 0) /* this cannot happen (laugh here) */ return FALSE; absfirst = abs1st[ng]; setfoundbits(); /* might reset absfirst */ #endif /* !USE_NNTP */ dmcount = 0; in_ng = TRUE; /* tell the world we are here */ build_cache(); return TRUE; } void grow_ng(newlast) ART_NUM newlast; { ART_NUM tmpfirst; forcegrow = FALSE; if (newlast > lastart) { ART_NUM tmpart = art; toread[ng] += (ART_UNREAD)(newlast-lastart); grow_cache(newlast); tmpfirst = lastart+1; lastart = newlast; thread_grow(); #ifdef KILLFILES #ifdef VERBOSE IF(verbose) sprintf(buf, "%ld more article%s arrived -- processing memorized commands...\n\n", (long)(lastart - tmpfirst + 1), (lastart > tmpfirst ? "s have" : " has" ) ); ELSE /* my, my, how clever we are */ #endif #ifdef TERSE strcpy(buf, "More news -- auto-processing...\n\n"); #endif if (has_normal_kills) { bool forcelast_save = forcelast; ARTICLE *artp_save = artp; kill_unwanted(tmpfirst,buf,TRUE); artp = artp_save; forcelast = forcelast_save; } #endif art = tmpart; } } void ng_skip() { #ifndef USE_NNTP /* never read it & cannot find it? */ if (errno != ENOENT) { /* has it not been deleted? */ clear(); # ifdef VERBOSE IF(verbose) printf("\n(Article %ld exists but is unreadable.)\n",(long)art) FLUSH; ELSE # endif # ifdef TERSE printf("\n(%ld unreadable.)\n",(long)art) FLUSH; # endif if (novice_delays) { pad(just_a_sec); sleep(2); } } inc_art(selected_only,FALSE); /* try next article */ #else /* USE_NNTP */ ART_NUM artnum; clear(); # ifdef VERBOSE IF(verbose) fputs("Skipping unavailable article\n",stdout); ELSE # endif /* VERBOSE */ # ifdef TERSE fputs("Skipping\n",stdout); # endif /* TERSE */ if (novice_delays) { pad(just_a_sec/3); sleep(1); } art++; artp++; do { /* tries to grab PREFETCH_SIZE XHDRS, flagging missing articles */ (void) fetchsubj(art, FALSE); artnum = art+PREFETCH_SIZE-1; if (artnum > lastart) artnum = lastart; while (art <= artnum) { if (!(artp->flags & AF_MISSING)) return; art++; artp++; } } while (art <= lastart); #endif /* USE_NNTP */ } /* find the maximum article number of a newsgroup */ ART_NUM getngsize(num) register NG_NUM num; { register int len; register char *nam; char tmpbuf[LBUFLEN]; ART_POS oldsoft; long last, first; char ch; nam = rcline[num]; len = rcnums[num] - 1; softtries++; #ifdef DEBUG if (debug & DEB_SOFT_POINTERS) printf("Softptr = %ld\n",(long)softptr[num]) FLUSH; #endif oldsoft = softptr[num]; #ifndef USE_NNTP fseek(actfp,100000L,1); /* hopefully this forces a reread */ #endif if ((softptr[num] = findact(tmpbuf, nam, len, (long)oldsoft)) >= 0) { if (softptr[num] != oldsoft) { softmisses++; writesoft = TRUE; } } else { softptr[num] = 0; if (RCCHAR(rcchar[num]) == ':') rcchar[num] = NEGCHAR; return TR_BOGUS; } #ifdef DEBUG if (debug & DEB_SOFT_POINTERS) { printf("Should be %ld\n",(long)softptr[num]) FLUSH; } #endif #ifdef ANCIENT_NEWS sscanf(tmpbuf+len+1, "%ld %c", &last, &ch); first = 1; #else sscanf(tmpbuf+len+1, "%ld %ld %c", &last, &first, &ch); #endif if (!abs1st[num]) abs1st[num] = (ART_NUM)first; if (!in_ng) { if (redirected) { if (redirected != nullstr) free(redirected); redirected = Nullch; } switch (ch) { case 'n': moderated = getval("NOPOSTRING"," (no posting)"); break; case 'm': moderated = getval("MODSTRING", " (moderated)"); break; case 'x': redirected = nullstr; moderated = " (DISABLED)"; break; case '=': len = strlen(tmpbuf); if (tmpbuf[len-1] == '\n') tmpbuf[len-1] = '\0'; redirected = savestr(rindex(tmpbuf, '=') + 1); moderated = " (REDIRECTED)"; break; default: moderated = nullstr; break; } } if (last < ngmax[num]) return ngmax[num]; return ngmax[num] = (ART_NUM)last; } trn-3.6/parsedate.y0000664000000000000000000005141505403460701011224 0ustar %{ /* $Revision: 1.12 $ ** ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990. ** Further revised (removed obsolete constructs and cleaned up timezone ** names) in August, 1991, by Rich. Paul Eggert ** helped in September, 1992. ** ** This grammar has six shift/reduce conflicts. ** ** This code is in the public domain and has no copyright. */ /* SUPPRESS 530 *//* Empty body for statement */ /* SUPPRESS 593 on yyerrlab *//* Label was not used */ /* SUPPRESS 593 on yynewstate *//* Label was not used */ /* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */ #include #include #include #include "config.h" #include #define yyparse date_parse #define yylex date_lex #define yyerror date_error /* See the LeapYears table in Convert. */ #define EPOCH 1970 #define END_OF_TIME 2038 /* Constants for general time calculations. */ #define DST_OFFSET 1 #define SECSPERDAY (24L * 60L * 60L) /* Readability for TABLE stuff. */ #define HOUR(x) (x * 60) #define LPAREN '(' #define RPAREN ')' #define IS7BIT(x) ((unsigned int)(x) < 0200) #define SIZEOF(array) ((int)(sizeof array / sizeof array[0])) #define ENDOF(array) (&array[SIZEOF(array)]) /* ** An entry in the lexical lookup table. */ typedef struct _TABLE { char *name; int type; time_t value; } TABLE; /* ** Daylight-savings mode: on, off, or not yet known. */ typedef enum _DSTMODE { DSTon, DSToff, DSTmaybe } DSTMODE; /* ** Meridian: am, pm, or 24-hour style. */ typedef enum _MERIDIAN { MERam, MERpm, MER24 } MERIDIAN; /* ** Global variables. We could get rid of most of them by using a yacc ** union, but this is more efficient. (This routine predates the ** yacc %union construct.) */ static char *yyInput; static DSTMODE yyDSTmode; static int yyHaveDate; static int yyHaveRel; static int yyHaveTime; static time_t yyTimezone; static time_t yyDay; static time_t yyHour; static time_t yyMinutes; static time_t yyMonth; static time_t yySeconds; static time_t yyYear; static MERIDIAN yyMeridian; static time_t yyRelMonth; static time_t yyRelSeconds; extern struct tm *localtime(); static void date_error(); %} %union { time_t Number; enum _MERIDIAN Meridian; } %token tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER %token tUNUMBER tZONE %type tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT %type tSNUMBER tUNUMBER tZONE numzone zone %type tMERIDIAN o_merid %% spec : /* NULL */ | spec item ; item : time { yyHaveTime++; #ifdef lint /* I am compulsive about lint natterings... */ if (yyHaveTime == -1) { YYERROR; } #endif /* lint */ } | time zone { yyHaveTime++; yyTimezone = $2; } | date { yyHaveDate++; } | rel { yyHaveRel = 1; } ; time : tUNUMBER o_merid { if ($1 < 100) { yyHour = $1; yyMinutes = 0; } else { yyHour = $1 / 100; yyMinutes = $1 % 100; } yySeconds = 0; yyMeridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = 0; yyMeridian = $4; } | tUNUMBER ':' tUNUMBER numzone { yyHour = $1; yyMinutes = $3; yyTimezone = $4; yyMeridian = MER24; yyDSTmode = DSToff; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = $6; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyTimezone = $6; yyMeridian = MER24; yyDSTmode = DSToff; } ; zone : tZONE { $$ = $1; yyDSTmode = DSToff; } | tDAYZONE { $$ = $1; yyDSTmode = DSTon; } | tZONE numzone { /* Only allow "GMT+300" and "GMT-0800" */ if ($1 != 0) { YYABORT; } $$ = $2; yyDSTmode = DSToff; } | numzone { $$ = $1; yyDSTmode = DSToff; } ; numzone : tSNUMBER { int i; /* Unix and GMT and numeric timezones -- a little confusing. */ if ($1 < 0) { /* Don't work with negative modulus. */ $1 = -$1; if ($1 > 9999 || (i = $1 % 100) >= 60) { YYABORT; } $$ = ($1 / 100) * 60 + i; } else { if ($1 > 9999 || (i = $1 % 100) >= 60) { YYABORT; } $$ = -(($1 / 100) * 60 + i); } } ; date : tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { if ($1 > 100) { yyYear = $1; yyMonth = $3; yyDay = $5; } else { yyMonth = $1; yyDay = $3; yyYear = $5; } } | tMONTH tUNUMBER { yyMonth = $1; yyDay = $2; } | tMONTH tUNUMBER ',' tUNUMBER { yyMonth = $1; yyDay = $2; yyYear = $4; } | tUNUMBER tMONTH { yyDay = $1; yyMonth = $2; } | tUNUMBER tMONTH tUNUMBER { yyDay = $1; yyMonth = $2; yyYear = $3; } | tDAY ',' tUNUMBER tMONTH tUNUMBER { yyDay = $3; yyMonth = $4; yyYear = $5; } ; rel : tSNUMBER tSEC_UNIT { yyRelSeconds += $1 * $2; } | tUNUMBER tSEC_UNIT { yyRelSeconds += $1 * $2; } | tSNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tUNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } ; o_merid : /* NULL */ { $$ = MER24; } | tMERIDIAN { $$ = $1; } ; %% /* Month and day table. */ static TABLE MonthDayTable[] = { { "january", tMONTH, 1 }, { "february", tMONTH, 2 }, { "march", tMONTH, 3 }, { "april", tMONTH, 4 }, { "may", tMONTH, 5 }, { "june", tMONTH, 6 }, { "july", tMONTH, 7 }, { "august", tMONTH, 8 }, { "september", tMONTH, 9 }, { "october", tMONTH, 10 }, { "november", tMONTH, 11 }, { "december", tMONTH, 12 }, /* The value of the day isn't used... */ { "sunday", tDAY, 0 }, { "monday", tDAY, 0 }, { "tuesday", tDAY, 0 }, { "wednesday", tDAY, 0 }, { "thursday", tDAY, 0 }, { "friday", tDAY, 0 }, { "saturday", tDAY, 0 }, }; /* Time units table. */ static TABLE UnitsTable[] = { { "year", tMONTH_UNIT, 12 }, { "month", tMONTH_UNIT, 1 }, { "week", tSEC_UNIT, 7L * 24 * 60 * 60 }, { "day", tSEC_UNIT, 1L * 24 * 60 * 60 }, { "hour", tSEC_UNIT, 60 * 60 }, { "minute", tSEC_UNIT, 60 }, { "min", tSEC_UNIT, 60 }, { "second", tSEC_UNIT, 1 }, { "sec", tSEC_UNIT, 1 }, }; /* Timezone table. */ static TABLE TimezoneTable[] = { { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ { "ut", tZONE, HOUR( 0) }, /* Universal */ { "utc", tZONE, HOUR( 0) }, /* Universal Coordinated */ { "cut", tZONE, HOUR( 0) }, /* Coordinated Universal */ { "z", tZONE, HOUR( 0) }, /* Greenwich Mean */ { "wet", tZONE, HOUR( 0) }, /* Western European */ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ { "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */ { "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ { "cst", tZONE, HOUR( 6) }, /* Central Standard */ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ { "akst", tZONE, HOUR( 9) }, /* Alaska Standard */ { "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ { "hast", tZONE, HOUR(10) }, /* Hawaii-Aleutian Standard */ { "hadt", tDAYZONE, HOUR(10) }, /* Hawaii-Aleutian Daylight */ { "ces", tDAYZONE, -HOUR(1) }, /* Central European Summer */ { "cest", tDAYZONE, -HOUR(1) }, /* Central European Summer */ { "mez", tZONE, -HOUR(1) }, /* Middle European */ { "mezt", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ { "cet", tZONE, -HOUR(1) }, /* Central European */ { "met", tZONE, -HOUR(1) }, /* Middle European */ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe */ { "msk", tZONE, -HOUR(3) }, /* Moscow Winter */ { "msd", tDAYZONE, -HOUR(3) }, /* Moscow Summer */ { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */ { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */ { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */ { "cct", tZONE, -HOUR(8) }, /* China Coast */ { "jst", tZONE, -HOUR(9) }, /* Japan Standard */ { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ { "kdt", tZONE, -HOUR(9) }, /* Korean Daylight */ { "cast", tZONE, -(HOUR(9)+30) }, /* Central Australian Standard */ { "cadt", tDAYZONE, -(HOUR(9)+30) }, /* Central Australian Daylight */ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ /* For completeness we include the following entries. */ #if 0 /* Duplicate names. Either they conflict with a zone listed above * (which is either more likely to be seen or just been in circulation * longer), or they conflict with another zone in this section and * we could not reasonably choose one over the other. */ { "fst", tZONE, HOUR( 2) }, /* Fernando De Noronha Standard */ { "fdt", tDAYZONE, HOUR( 2) }, /* Fernando De Noronha Daylight */ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ { "est", tZONE, HOUR( 3) }, /* Eastern Standard (Brazil) */ { "edt", tDAYZONE, HOUR( 3) }, /* Eastern Daylight (Brazil) */ { "wst", tZONE, HOUR( 4) }, /* Western Standard (Brazil) */ { "wdt", tDAYZONE, HOUR( 4) }, /* Western Daylight (Brazil) */ { "cst", tZONE, HOUR( 5) }, /* Chile Standard */ { "cdt", tDAYZONE, HOUR( 5) }, /* Chile Daylight */ { "ast", tZONE, HOUR( 5) }, /* Acre Standard */ { "adt", tDAYZONE, HOUR( 5) }, /* Acre Daylight */ { "cst", tZONE, HOUR( 5) }, /* Cuba Standard */ { "cdt", tDAYZONE, HOUR( 5) }, /* Cuba Daylight */ { "est", tZONE, HOUR( 6) }, /* Easter Island Standard */ { "edt", tDAYZONE, HOUR( 6) }, /* Easter Island Daylight */ { "sst", tZONE, HOUR(11) }, /* Samoa Standard */ { "ist", tZONE, -HOUR(2) }, /* Israel Standard */ { "idt", tDAYZONE, -HOUR(2) }, /* Israel Daylight */ { "idt", tDAYZONE, -(HOUR(3)+30) }, /* Iran Daylight */ { "ist", tZONE, -(HOUR(3)+30) }, /* Iran Standard */ { "cst", tZONE, -HOUR(8) }, /* China Standard */ { "cdt", tDAYZONE, -HOUR(8) }, /* China Daylight */ { "sst", tZONE, -HOUR(8) }, /* Singapore Standard */ /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ { "wat", tZONE, -HOUR(1) }, /* West Africa */ { "at", tZONE, HOUR( 2) }, /* Azores */ { "gst", tZONE, -HOUR(10) }, /* Guam Standard */ { "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ { "fwt", tZONE, -HOUR(1) }, /* French Winter */ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ { "bt", tZONE, -HOUR(3) }, /* Baghdad */ { "it", tZONE, -(HOUR(3)+30) }, /* Iran */ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ { "ist", tZONE, -(HOUR(5)+30) }, /* Indian Standard */ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ { "nst", tZONE, -HOUR(7) }, /* North Sumatra */ { "sst", tZONE, -HOUR(7) }, /* South Sumatra */ { "jt", tZONE, -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ { "cat", tZONE, HOUR(10) }, /* -- expired 1967 */ { "nt", tZONE, HOUR(11) }, /* -- expired 1967 */ { "ahst", tZONE, HOUR(10) }, /* -- expired 1983 */ { "hdt", tDAYZONE, HOUR(10) }, /* -- expired 1986 */ #endif /* 0 */ }; /* ARGSUSED */ static void date_error(s) char *s; { /* NOTREACHED */ } static time_t ToSeconds(Hours, Minutes, Seconds, Meridian) time_t Hours; time_t Minutes; time_t Seconds; MERIDIAN Meridian; { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61) return -1; if (Meridian == MER24) { if (Hours < 0 || Hours > 23) return -1; } else { if (Hours < 1 || Hours > 12) return -1; if (Hours == 12) Hours = 0; if (Meridian == MERpm) Hours += 12; } return (Hours * 60L + Minutes) * 60L + Seconds; } static time_t Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, dst) time_t Month; time_t Day; time_t Year; time_t Hours; time_t Minutes; time_t Seconds; MERIDIAN Meridian; DSTMODE dst; { static int DaysNormal[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static int DaysLeap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static int LeapYears[] = { 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036 }; register int *yp; register int *mp; register time_t Julian; register int i; time_t tod; if (Year < 0) Year = -Year; if (Year < 100) Year += 1900; if (Year < EPOCH) Year += 100; for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++) if (Year == *yp) { mp = DaysLeap; break; } if (Year < EPOCH || Year > END_OF_TIME || Month < 1 || Month > 12 /* NOSTRICT *//* conversion from long may lose accuracy */ || Day < 1 || Day > mp[(int)Month]) return -1; Julian = Day - 1 + (Year - EPOCH) * 365; for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++) if (Year <= *yp) break; for (i = 1; i < Month; i++) Julian += *++mp; Julian *= SECSPERDAY; Julian += yyTimezone * 60L; if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) return -1; Julian += tod; tod = Julian; if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst)) Julian -= DST_OFFSET * 60L * 60L; return Julian; } static time_t DSTcorrect(Start, Future) time_t Start; time_t Future; { time_t StartDay; time_t FutureDay; StartDay = (localtime(&Start)->tm_hour + 1) % 24; FutureDay = (localtime(&Future)->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60L * 60L; } static time_t RelativeMonth(Start, RelMonth) time_t Start; time_t RelMonth; { struct tm *tm; time_t Month; time_t Year; tm = localtime(&Start); Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; return DSTcorrect(Start, Convert(Month, (time_t)tm->tm_mday, Year, (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, MER24, DSTmaybe)); } static int LookupWord(buff, length) char *buff; register int length; { register char *p; register char *q; register TABLE *tp; register int c; p = buff; c = p[0]; /* See if we have an abbreviation for a month. */ if (length == 3 || (length == 4 && p[3] == '.')) for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) { q = tp->name; if (c == q[0] && p[1] == q[1] && p[2] == q[2]) { yylval.Number = tp->value; return tp->type; } } else for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Try for a timezone. */ for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) if (c == tp->name[0] && p[1] == tp->name[1] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Try the units table. */ for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Strip off any plural and try the units table again. */ if (--length > 0 && p[length] == 's') { p[length] = '\0'; for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) if (c == tp->name[0] && strcmp(p, tp->name) == 0) { p[length] = 's'; yylval.Number = tp->value; return tp->type; } p[length] = 's'; } length++; /* Drop out any periods. */ for (p = buff, q = (char*)buff; *q; q++) if (*q != '.') *p++ = *q; *p = '\0'; /* Try the meridians. */ if (buff[1] == 'm' && buff[2] == '\0') { if (buff[0] == 'a') { yylval.Meridian = MERam; return tMERIDIAN; } if (buff[0] == 'p') { yylval.Meridian = MERpm; return tMERIDIAN; } } /* If we saw any periods, try the timezones again. */ if (p - buff != length) { c = buff[0]; for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) if (c == tp->name[0] && p[1] == tp->name[1] && strcmp(p, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } /* Unknown word -- assume GMT timezone. */ yylval.Number = 0; return tZONE; } int date_lex() { register char c; register char *p; char buff[20]; register int sign; register int i; register int nesting; for ( ; ; ) { /* Get first character after the whitespace. */ for ( ; ; ) { while (isspace(*yyInput)) yyInput++; c = *yyInput; /* Ignore RFC 822 comments, typically time zone names. */ if (c != LPAREN) break; for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; ) if (c == LPAREN) nesting++; else if (!IS7BIT(c) || c == '\0' || c == '\r' || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c)))) /* Lexical error: bad comment. */ return '?'; yyInput++; } /* A number? */ if (isdigit(c) || c == '-' || c == '+') { if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; yyInput++; if (!isdigit(*yyInput)) /* Skip the plus or minus sign. */ continue; } else sign = 0; for (i = 0; (c = *yyInput++) != '\0' && isdigit(c); ) i = 10 * i + c - '0'; yyInput--; yylval.Number = sign < 0 ? -i : i; return sign ? tSNUMBER : tUNUMBER; } /* A word? */ if (isalpha(c)) { for (p = buff; (c = *yyInput++) == '.' || isalpha(c); ) if (p < &buff[sizeof buff - 1]) *p++ = isupper(c) ? tolower(c) : c; *p = '\0'; yyInput--; return LookupWord(buff, p - buff); } return *yyInput++; } } time_t parsedate(p) char *p; { extern int date_parse(); time_t Start; yyInput = p; yyYear = 0; yyMonth = 0; yyDay = 0; yyTimezone = 0; yyDSTmode = DSTmaybe; yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; yyRelSeconds = 0; yyRelMonth = 0; yyHaveDate = 0; yyHaveRel = 0; yyHaveTime = 0; if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1) return -1; if (yyHaveDate || yyHaveTime) { Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, yyMeridian, yyDSTmode); if (Start < 0) return -1; } else return -1; Start += yyRelSeconds; if (yyRelMonth) Start += RelativeMonth(Start, yyRelMonth); /* Have to do *something* with a legitimate -1 so it's distinguishable * from the error return value. (Alternately could set errno on error.) */ return Start == -1 ? 0 : Start; } #ifdef TEST #if YYDEBUG extern int yydebug; #endif /* YYDEBUG */ /* ARGSUSED */ int main(ac, av) int ac; char *av[]; { char buff[128]; time_t d; #if YYDEBUG yydebug = 1; #endif /* YYDEBUG */ (void)printf("Enter date, or blank line to exit.\n\t> "); for ( ; ; ) { (void)printf("\t> "); (void)fflush(stdout); if (gets(buff) == NULL || buff[0] == '\n') break; #if YYDEBUG if (strcmp(buff, "yydebug") == 0) { yydebug = !yydebug; printf("yydebug = %s\n", yydebug ? "on" : "off"); continue; } #endif /* YYDEBUG */ d = parsedate(buff, (TIMEINFO *)NULL); if (d == -1) (void)printf("Bad format - couldn't convert.\n"); else (void)printf("%s", ctime(&d)); } exit(0); /* NOTREACHED */ } #endif /* TEST */ trn-3.6/final.h0000664000000000000000000000261405476543462010341 0ustar /* $Id: final.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ /* cleanup status for fast exits */ EXT bool panic INIT(FALSE); /* we got hung up or something-- */ /* so leave tty alone */ EXT bool rc_changed INIT(FALSE); /* need we rewrite .newsrc? */ EXT bool doing_ng INIT(FALSE); /* do we need to reconstitute */ /* current rc line? */ EXT char int_count INIT(0); /* how many interrupts we've had */ EXT bool clear_on_stop INIT(FALSE); /* set when handling the stop signal */ /* would leave the screen a mess */ /* signal catching routines */ Signal_t int_catcher _((int)); Signal_t sig_catcher _((int)); #ifdef SIGTSTP Signal_t stop_catcher _((int)); #endif void final_init _((void)); void finalize _((int)) #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR >= 5) __attribute__((noreturn)) #endif ; trn-3.6/ndir.c0000664000000000000000000000450105545455743010175 0ustar /* $Id: ndir.c,v 3.0 1991/09/09 20:23:31 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "INTERN.h" #include "ndir.h" #ifdef EMULATE_NDIR /* * support for Berkeley directory reading routine on a V7 file system */ /* * open a directory. */ DIR * opendir(name) char *name; { register DIR *dirp; register int fd; char *malloc(); if ((fd = open(name, 0)) == -1) return NULL; if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { close (fd); return NULL; } dirp->dd_fd = fd; dirp->dd_loc = 0; return dirp; } /* * read an old style directory entry and present it as a new one */ #ifndef pyr #define ODIRSIZ 14 struct olddirect { ino_t od_ino; char od_name[ODIRSIZ]; }; #else /* an Pyramid in the ATT universe */ #define ODIRSIZ 248 struct olddirect { long od_ino; short od_fill1, od_fill2; char od_name[ODIRSIZ]; }; #endif /* * get next entry in a directory. */ Direntry_t * readdir(dirp) register DIR *dirp; { register struct olddirect *dp; static Direntry_t dir; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) return NULL; } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc); dirp->dd_loc += sizeof(struct olddirect); if (dp->od_ino == 0) continue; dir.d_ino = dp->od_ino; strncpy(dir.d_name, dp->od_name, ODIRSIZ); dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */ dir.d_namlen = strlen(dir.d_name); dir.d_reclen = DIRSIZ(&dir); return (&dir); } } /* * close a directory. */ void closedir(dirp) register DIR *dirp; { close(dirp->dd_fd); dirp->dd_fd = -1; dirp->dd_loc = 0; free(dirp); } #endif /* EMULATE_NDIR */ trn-3.6/MANIFEST0000664000000000000000000001637405664226626010237 0ustar Configure A shell script that installs everything system dependent. EXTERN.h When included, makes other includes not belong to me. HACKERSGUIDE A brief guide to the contorted innards of [t]rn. HINTS.TRN Some helpful hints to get the most out of using trn. INIT Sample system-wide switch file. INSTALL Installation instructions. INTERN.h When included, makes other includes belong to me. MANIFEST This list of files. Makefile.SH Shell script to generate the makefile. NEW List of new features with trn 3.0. Pnews.1 Manual page for Pnews. Pnews.SH A news posting shell script that knows about -h. Policy.sh.SH Script to save the local policy answers. README Start by reading this file. Rnmail.1 Manual page for Rnmail. Rnmail.SH A mailer that knows about -h. Speller.SH An ispell wrapper that understands news articles. addng.c Routines for scanning the active file for new newsgroups. addng.h Public info regarding addng.c. art.c Routines to display an article. art.h Public info regarding art.c. artio.c Reserved for the article abstract type, someday. artio.h Public info regarding artio.c. artsrch.c Routines for searching among articles. artsrch.h Public info regarding artsrch.c. artstate.h Info on the current state of the article. autosub.c Routines to allow automatic subscription management autosub.h Public info regarding autosub.c backpage.c Routines for paging backwards in articles. backpage.h Public info regarding backpage.c. bits.c Bitmap management functions. bits.h Public info regarding bits.c. cache.c Routines to handle caching of articles. cache.h Public info regarding cache.c. charsubst.c Routines for converting between various character sets. charsubst.h Public info regarding charsubst.c. common.h Global info. config.h.SH Shell script to create config.h decode.c Routines common to the binary decoders decode.h Public info for the binary decoders. dependencies A pre-made list of the source's dependencies. edit_dist.c Routines to find the edit distance between two strings. filexp.SH The filename expansion script. final.c Finalization (exit) routines. final.h Public info regarding final.c. hash.c Hashing routines. hash.h Public/private info for hashing routines. head.c Header parsing routines. head.h Public info regarding head.c. help.c Help routines. help.h Public info regarding help.c. init.c Initialization (startup) routines. init.h Public info regarding init.c. intrp.c Filename expansion and % interpretation routines. intrp.h Public info regarding intrp.c. kfile.c KILL file routines. kfile.h Public info regarding kfile.c. last.c Routines for handling the .rnlast file. last.h Public info regarding last.c. makedepend.SH Shell script to generate make dependencies. makedir.SH Shell script to make nested subdirectories. mbox.saver.SH Shell script to save an article to a mailbox. ndir.c BSD 4.2 directory routine emulation. ndir.h Public directory info. newsetup.1 Manual page for newsetup. newsetup.SH Shell script to create a .newsrc file. newsgroups.1 Manual page for newsgroups. newsgroups.SH Shell script to list unsubscribed newsgroups. newsnews.SH A motd-like file that trn may print at startup. ng.c Routines to display a newsgroup. ng.h Public info regarding ng.c. ngdata.c General data fetching routines for a newsgroup. ngdata.h Public info regarding ngdata.c. nghash.c Some hashing routines to speed up active file handling. ngsrch.c Routines to search among newsgroups. ngsrch.h Public info regarding ngsrch.c. ngstuff.c Support routines for ng.c. ngstuff.h Public info regarding ng.c. nntp.c Routines for accessing NNTP commands. nntp.h Public info regarding the NNTP routines. nntpauth.c Code to handle NNTP authentication. nntpauth.h Public info for the authentication routines. nntpclient.c Custom version of the nntp client library. nntpclient.h Public info for the nntp client library. nntpinit.c Routines to implement server_init(). nntplist.c Used by shell scripts to list server resources. (NNTP only) norm.saver.SH Shell script to save an article to a normal file. only.c Routines to perform newsgroup restriction. only.h Public info regarding only.c. overview.h Defines for customizing the overview handling. parsedate.y A yacc script for date parsing. patchlevel.h Indicates current patch level. rcln.c Routines to mung a .newsrc line. rcln.h Public info regarding rcln.c. rcstuff.c Routines to mung the .newsrc file. rcstuff.h Public info regarding rcstuff.c. respond.c Various routines for doing things with articles. respond.h Public info regarding respond.c. rt-mt.c Support for the mthread's .thread file format. rt-mt.h Public/private info for rt-mt.c. rt-ov.c Support for .overview files. rt-ov.h Public info for rt-ov.c. rt-page.c Routines to manipulate pages in the selector. rt-page.h Public info for rt-page.c. rt-process.c The thread-processing code. rt-process.h Public info for rt-process.c. rt-select.c The thread/subject/article selector. rt-select.h Public/private info for rt-select.c. rt-util.c Utility routines for reading threads. rt-util.h Public info for rt-util.c. rt-wumpus.c Routines to draw the character-oriented tree display. rt-wumpus.h Public info for rt-wumpus.c. rthread.c The basic routines added to init and read threads. rthread.h Public info for rthread.c. search.c Regular expression processing ala emacs. search.h Public info regarding search.c. strftime.c Source for strftime.c, if needed. sw.c Switch processing routines. sw.h Public info regarding switch.c. term.c Terminal interface routines. term.h Public info regarding term.c. trn.1 Manual pages for trn -- PLEASE READ. trn.c The main program. trn.h Public info for trn.c. trn-artchk.c An article-checking program called from Pnews. unipatch.c A unified diff filter for use with old versions of patch. unship.c Decodes ship files. util.c Utility routines. util.h Public info regarding util.c. util2.c Utility routines common to trn and nntplist. util2.h Public info regarding util2.c. uudecode.c Decodes uuencoded files. samples/subscriptions A sample subscriptions file for the default .newsrc. support/trnkill A script to run all trn's kill files in the background. trn-3.6/nntplist.10000664000000000000000000000276307264355530011033 0ustar '\" t .\" @(#)nntplist.1 .\" .\" Original version of this manpage: .\" Peter Maydell (pmaydell@chiark.greenend.org.uk), 09/1999 .\" .TH NNTPLIST 1 "September 1999" .SH NAME nntplist \- obtain newsgroup list and other information from an NNTP server .SH SYNOPSIS .B nntplist .RB [ \-x .IR WildSpec ] .RB [ \-o .IR OutputFile ] .RI [ type ] .br .SH DESCRIPTION .LP .B nntplist provides a convenient interface to the 'LIST' command of an NNTP server, allowing the downloading of information such as lists of newsgroups. It is in fact mostly intended to be used internally by the .B newsgroups(1) program, which is in turn used by the .B trn(1) newsreader. .SH OPTIONS .TP .BI \-x\ WildSpec This option allows a wildcard specification to be passed to the server to limit the amount of information produced. .TP .BI \-o\ OutputFile This option specifies an output file into which the data from the server is saved. If this option is not given, the default is to write the data to standard output. .TP .I type This specifies the type of information to be obtained from the server. It can be any of the LIST command arguments your server accepts. If .I type is not specified it defaults to ACTIVE. .SH EXAMPLES .TP .B nntplist -x 'alt.*' This lists all the alt. newsgroups your newsserver currently handles (ie those in its active file). .SH AUTHOR Stan Barber was the original author of nntplist. Peter Maydell wrote this manual page. .SH SEE ALSO .BR newsgroups (1), .BR trn (1) trn-3.6/artsrch.h0000664000000000000000000000333105657552730010712 0ustar /* $Id: artsrch.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifndef NBRA #include "search.h" #endif #ifdef ARTSEARCH #define SRCH_ABORT 0 #define SRCH_INTR 1 #define SRCH_FOUND 2 #define SRCH_NOTFOUND 3 #define SRCH_DONE 4 #define SRCH_SUBJDONE 5 #define SRCH_ERROR 6 #endif EXT char *lastpat INIT(nullstr); /* last search pattern */ #ifdef ARTSEARCH EXT COMPEX sub_compex; /* last compiled subject search */ EXT COMPEX art_compex; /* last compiled normal search */ # ifdef CONDSUB EXT COMPEX *bra_compex INIT(&(art_compex)); /* current compex with brackets */ # endif #define ARTSCOPE_SUBJECT 0 #define ARTSCOPE_FROM 1 #define ARTSCOPE_ONEHDR 2 #define ARTSCOPE_HEAD 3 #define ARTSCOPE_BODY_NOSIG 4 #define ARTSCOPE_BODY 5 #define ARTSCOPE_ARTICLE 6 EXT char scopestr[] INIT("sfHha"); EXT char art_howmuch; /* search scope */ EXT char *art_srchhdr; /* specific header to search or NULL */ EXT bool art_doread; /* search read articles? */ #endif void artsrch_init _((void)); #ifdef ARTSEARCH int art_search _((char*, int, int)); bool wanted _((COMPEX*, ART_NUM, char_int)); /* return TRUE if current article matches pattern */ #endif trn-3.6/autosub.c0000664000000000000000000000354005420174057010711 0ustar /* $Id: autosub.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "ngsrch.h" #include "util.h" #include "final.h" #include "INTERN.h" #include "autosub.h" /* Consider the newsgroup specified, and return: */ /* : if we should autosubscribe to it */ /* ! if we should autounsubscribe to it */ /* \0 if we should ask the user. */ int auto_subscribe(ng) char *ng; { char *s; if((s = getval("AUTOSUBSCRIBE", (char *)NULL)) && matchlist(s, ng)) return ':'; if((s = getval("AUTOUNSUBSCRIBE", (char *)NULL)) && matchlist(s, ng)) return '!'; return 0; } bool matchlist(patlist, s) char *patlist; char *s; { COMPEX ilcompex; char *p; char *err; bool result; bool tmpresult; result = FALSE; init_compex(&ilcompex); while(patlist && *patlist) { if(*patlist == '!') { patlist++; tmpresult = FALSE; } else tmpresult = TRUE; if(p = index(patlist, ',')) *p = '\0'; /* compile regular expression */ err = ng_comp(&ilcompex,patlist,TRUE,TRUE); if(p) *p++ = ','; if(err != Nullch) { printf("\n%s\n", err) FLUSH; finalize(1); } if (execute(&ilcompex,s) != Nullch) result = tmpresult; patlist = p; } free_compex(&ilcompex); return result; } trn-3.6/last.c0000664000000000000000000000433405476767065010216 0ustar /* $Id: last.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "trn.h" #include "util.h" #include "intrp.h" #include "INTERN.h" #include "last.h" char *lastname = Nullch; /* path name of .rnlast file */ void last_init(tcbuf) char *tcbuf; { lastname = savestr(filexp(LASTNAME)); if ((tmpfp = fopen(lastname,"r")) != Nullfp && fgets(tcbuf,1024,tmpfp) != Nullch) { tcbuf[strlen(tcbuf)-1] = '\0'; lastngname = savestr(tcbuf); fgets(tcbuf,1024,tmpfp); lasttime = atol(tcbuf); fgets(tcbuf,1024,tmpfp); lastactsiz = atol(tcbuf); if (fgets(tcbuf,1024,tmpfp) != Nullch) lastnewtime = atol(tcbuf); else lastnewtime = lasttime; #if 1 /* If the time is in the future, rewind by thirty days */ if (lastnewtime > time(Null(time_t*))) lastnewtime = time(Null(time_t*)) - 30L*24*60*60; #endif /* If the time wasn't set, just give them 2 days worth of groups */ if (!lastnewtime) lastnewtime = time(Null(time_t*)) - 2L*24*60*60; if (fgets(tcbuf,1024,tmpfp) != Nullch) lastnewsize = atol(tcbuf); else lastnewsize = 0; fclose(tmpfp); } else { lastngname = nullstr; lasttime = 0; lastactsiz = 0; lastnewsize = 0; /* Use two days ago as an initial value for finding new groups. */ lastnewtime = time(Null(time_t*)) - 2L*24*60*60; } } /* put out certain values for next run of rn */ void writelast() { if ((tmpfp = fopen(lastname,"w")) != Nullfp) { fprintf(tmpfp,"%s\n%ld\n%ld\n%ld\n%ld\n", (ngname==Nullch?nullstr:ngname),(long)lasttime,(long)lastactsiz, (long)lastnewtime,(long)lastnewsize); fclose(tmpfp); } else printf(cantcreate,lastname) FLUSH; } trn-3.6/config.sh0000775000000000000000000000765011124143664010673 0ustar #!/bin/sh # # This file was produced by running the Configure script. It holds all the # definitions figured out by Configure. Should you modify one of these values, # do not forget to propagate your changes by running "Configure -der". You may # instead choose to run each of the .SH files by yourself, or "Configure -S". # # Configuration time: Fri Nov 17 19:50:28 GMT 2000 # Configured by: mcv21 # Target system: linux pick 2.2.14 #1 wed jan 12 13:49:58 gmt 2000 i686 unknown d_bsd='undef' d_eunice='undef' d_xenix='undef' eunicefix=':' Mcc='Mcc' awk='awk' basename='basename' bash='bash' cat='cat' cp='cp' cpp='cpp' csh='csh' date='date' diff='diff' echo='echo' ed='ed' egrep='egrep' expr='expr' grep='grep' inews='inews' ispell='ispell' ksh='ksh' less='less' lint='' mail='mail' metamail='metamail' mhn='mhn' mkdir='mkdir' more='more' munpack='munpack' mv='mv' nroff='nroff' pg='pg' rm='rm' rmail='rmail' sed='sed' sendmail='sendmail' smail='smail' sort='sort' spell='spell' tail='tail' test='test' tr='tr' uname='uname' uniq='uniq' uuname='uuname' vi='vi' vspell='' who='' hint='previous' myuname='linux pick 2.2.14 #1 wed jan 12 13:49:58 gmt 2000 i686 unknown ' srcdir='.' vincludes='' vpath='.' Id='$Id' Log='$Log' active='none' acttimes='nntp' d_acttimes='define' myactive='.falseactive' bin='/usr/lib/trn' binexp='/usr/lib/trn' installbin='/usr/lib/trn' cc='gcc' ccflags='' cppflags='' ldflags='-s' lkflags='' optimize='-O3' cf_by='mcv21' cf_time='Fri Nov 17 19:50:28 GMT 2000' contains='grep' cpplast='-' cppminus='-' cpprun='gcc -E' cppstdin='gcc -E' d_ftime='define' aphostcmd='' d_gethname='undef' d_phostcmd='undef' d_uname='undef' d_getpwent='define' d_getcwd='undef' d_getwd='define' d_havetlib='define' termlib='-lcurses' d_ignoreorg='undef' d_internet='define' d_memcmp='define' d_memcpy='define' d_memset='define' d_mimeshow='undef' d_mimestore='undef' mimeshow="none" mimestore="none" d_newsadm='define' newsadmin='news' d_nntp='define' d_xdata='define' d_genauth='define' servername='/etc/news/server' d_nolnbuf='undef' d_normsig='define' jobslib='' d_novoid='undef' void='' d_portable='define' d_rdchk='undef' d_rename='define' d_sigblock='define' d_sighold='define' d_sizet='undef' d_strccmp='define' d_strchr='define' d_strftime='define' strftimec='' strftimeo='' d_libndir='undef' d_usendir='undef' libndir='' ndirc='' ndiro='' d_vfork='define' d_voidsig='define' signal_t='void' defeditor='/usr/bin/editor' filexp='/usr/share/trn/filexp' d_dirnamlen='' i_dirent='define' i_ptem='undef' i_stdlib='define' i_string='define' strings='/usr/include/string.h' i_sysdir='define' d_voidtty='' i_bsdioctl='' i_sysfilio='undef' i_sysioctl='define' i_syssockio='' i_sysndir='undef' i_sgtty='undef' i_termio='undef' i_termios='define' i_systime='undef' i_time='define' i_unistd='define' i_vfork='undef' ispell_options='' ispell_prg='/usr/bin/ispell' libc='/usr/lib/libc.a' plibpth='/usr/ccs/lib /usr/lib /usr/ucblib /usr/local/lib' xlibpth='/usr/lib/386 /lib/386' libs=' ' citydist='none' cntrydist='none' contdist='none' locdist='none' multistatedist='none' orgdist='none' statedist='none' mailer='/usr/sbin/sendmail' mailfile='/var/spool/mail/%L' installmansrc='/usr/share/man/man1' manext='1' mansrc='/usr/share/man/man1' mansrcexp='/usr/share/man/man1' mboxchar='F' c='' n='-n' d_berknames='define' d_passnames='define' d_usgnames='undef' nametype='bsd' inewsloc='inews' newslib='/tmp' newslibexp='/tmp' newsspool='/tmp' orgname='/etc/news/organization' package='trn' spackage='Trn' pager='/usr/bin/pager' phost='/etc/news/whoami' phostcmd='' prefshell='/bin/sh' installprivlib='/usr/share/trn' privlib='/usr/share/trn' privlibexp='/usr/share/trn' rootid='0' sharpbang='#!' shsharp='true' spitshell='cat' startsh='#!/bin/sh' sysman='/usr/man/man1' d_usemt='define' d_useov='define' overviewdir='/tmp' threaddir='/tmp' trn_init='FALSE' trn_select='TRUE' nm_opt='' runnm='true' usenm='true' incpath='' mips='' mips_type='' usrinc='/usr/include' PATCHLEVEL="3.6 (20 Nov 1994)" CONFIG=true trn-3.6/patchlevel.h0000664000000000000000000000005005664230137011356 0ustar #define PATCHLEVEL " 3.6 (20 Nov 1994)" trn-3.6/rt-process.h0000664000000000000000000000074405552412177011344 0ustar /* $Id: rt-process.h,v 3.0 1992/12/14 00:14:15 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ int msgid_cmp _((char*,int,HASHDATUM)); ARTICLE *allocate_article _((ART_NUM)); bool valid_article _((ARTICLE*)); ARTICLE *get_article _((char*)); void thread_article _((ARTICLE*)); void merge_threads _((SUBJECT*, SUBJECT*)); trn-3.6/nntpauth.c0000664000000000000000000000356705663312263011103 0ustar /* $Id: nntpauth.c,v 3.6 1994/10/28 11:11:11 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "nntp.h" #include "util2.h" #include "INTERN.h" #include "nntpauth.h" #ifdef USE_NNTP char nntp_handle_auth_err(strict) bool_int strict; { #ifdef USE_GENAUTH extern char *loginName; char last_command_save[NNTP_STRLEN]; char *s, *cmd = "XAUTHINFO GENERIC" + (CompliantServer? 0 : 1); char ch; /* save previous command */ strcpy(last_command_save, last_command); if (s = getenv("NNTPAUTH")) sprintf(ser_line, "%s %s", cmd, s); else sprintf(ser_line, "%s any %s", cmd, loginName); /* issue authentication request */ nntp_command(ser_line); ch = (s? nntp_auth(s) : nntp_check(strict)); if (ch == NNTP_CLASS_FATAL) { char tmpbuf[LBUFLEN]; sprintf(tmpbuf,"\n%s\n",ser_line); nntp_error(tmpbuf); finalize(1); } nntp_command(last_command_save); return nntp_check(strict); #else /* !USE_GENAUTH */ if (strict) { char tmpbuf[LBUFLEN]; sprintf(tmpbuf,"\n%s\n",ser_line); nntp_error(tmpbuf); finalize(1); } return *ser_line; #endif /* !USE_GENAUTH */ } #ifdef USE_GENAUTH char nntp_auth(authc) char *authc; { char *s; if (cookiefd == -1 && (s = getenv("NNTP_AUTH_FDS")) != Nullch) { sscanf(s, "%*d.%*d.%d", &cookiefd); } if (cookiefd == -1) { FILE *f = tmpfile(); if (f) cookiefd = fileno(f); } sprintf(ser_line,"%d.%d.%d",fileno(ser_rd_fp),fileno(ser_wr_fp),cookiefd); export("NNTP_AUTH_FDS", ser_line); if (system(authc)) strcpy(ser_line, "502 Authentication failed"); else strcpy(ser_line, "281 Ok"); return *ser_line; } #endif /* USE_GENAUTH */ #endif /* USE_NNTP */ trn-3.6/ng.h0000664000000000000000000000346605420174100007634 0ustar /* $Id: ng.h,v 3.0 1991/09/09 20:23:31 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT ART_NUM art INIT(0); /* current or prospective article # */ EXT ART_NUM recent_art; /* previous article # for '-' command */ EXT ART_NUM curr_art; /* current article # */ EXT ARTICLE *recent_artp INIT(0);/* article_ptr equivilents */ EXT ARTICLE *curr_artp INIT(0); EXT ARTICLE *artp INIT(0); /* the article ptr we use when art is 0 */ EXT int checkcount INIT(0); /* how many articles have we read */ /* in the current newsgroup since */ /* the last checkpoint? */ EXT int docheckwhen INIT(20); /* how often to do checkpoint */ #ifdef MAILCALL EXT int mailcount INIT(0); /* check for mail when 0 mod 10 */ #endif EXT char *mailcall INIT(nullstr); EXT bool forcelast INIT(FALSE); /* ought we show "End of newsgroup"? */ EXT bool forcegrow INIT(FALSE); /* do we want to recalculate size */ /* of newsgroup, e.g. after posting? */ #define NG_ERROR -1 #define NG_NORM 0 #define NG_ASK 1 #define NG_MINUS 2 #define NG_SELPRIOR 3 #define NG_SELNEXT 4 void ng_init _((void)); int do_newsgroup _((char*)); int art_switch _((void)); #ifdef MAILCALL void setmail _((bool_int)); #endif void setdfltcmd _((void)); char ask_catchup _((void)); char ask_memorize _((char_int)); trn-3.6/search.h0000664000000000000000000000325705420174121010476 0ustar /* $Id: search.h,v 3.0 1991/09/09 20:27:37 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifndef NBRA #define NBRA 10 /* the maximum number of meta-brackets in an RE -- \( \) */ #define NALTS 10 /* the maximum number of \|'s */ typedef struct { char *expbuf; /* The compiled search string */ int eblen; /* Length of above buffer */ char *alternatives[NALTS]; /* The list of \| seperated alternatives */ char *braslist[NBRA]; /* RE meta-bracket start list */ char *braelist[NBRA]; /* RE meta-bracket end list */ char *brastr; /* saved match string after execute() */ char nbra; /* The number of meta-brackets int the most recenlty compiled RE */ bool do_folding; /* fold upper and lower case? */ } COMPEX; void search_init _((void)); void init_compex _((COMPEX*)); void free_compex _((COMPEX*)); char *getbracket _((COMPEX*,int)); void case_fold _((int)); char *compile _((COMPEX*,char*,int,int)); char *grow_eb _((COMPEX*,char*,char**)); char *execute _((COMPEX*,char*)); bool advance _((COMPEX*,char*,char*)); bool backref _((COMPEX*,int,char*)); bool cclass _((char*,int,int)); #endif trn-3.6/nntpauth.h0000664000000000000000000000056105654303431011074 0ustar /* $Id: nntpclient.h,v 3.0 1992/12/14 00:14:55 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #if defined(USE_NNTP) && defined(USE_GENAUTH) char nntp_auth _((char*)); EXT int cookiefd INIT(-1); #endif trn-3.6/rt-select.c0000664000000000000000000006313605663312265011144 0ustar /* $Id: rt-select.c,v 3.0 1992/12/14 00:14:12 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "trn.h" #include "term.h" #include "final.h" #include "util.h" #include "help.h" #include "cache.h" #include "bits.h" #include "artsrch.h" #include "ng.h" #include "ngdata.h" #include "ngstuff.h" #include "kfile.h" #include "rthread.h" #include "rt-page.h" #include "rt-util.h" #include "INTERN.h" #include "rt-select.h" /* When display mode is 'l', each author gets a separate line; when 's', no ** authors are displayed. */ char *display_mode = select_order; char sel_disp_char[] = { ' ', '+', '-', '*' }; static char sel_ret; static bool empty_ok; static bool disp_status_line; static bool clean_screen; /* CAA hacks: xterm mouse support */ static char *sel_mouse_save; void sel_go_line(line) int line; { int i; for (i = 0; i < sel_item_cnt; i++) { if (sel_items[i].line > line) break; } if (i > 0) i--; sel_item_index = i; } void sel_do_mouse(button,x,y) int button; /* 0: button1 1: button2 2: button3 3: release */ int x,y; { switch (button) { case 0: case 1: if (!y) sel_mouse_save = "<"; else if (y >= sel_last_line) sel_mouse_save = (button == 0)? " " : ">"; else { sel_go_line(y); if (button == 0) sel_mouse_save = "."; } break; case 2: /* move forward or backwards a page: * if cursor in top half: backwards * if cursor in bottom half: forwards */ if (y<(LINES/2)) sel_mouse_save = "<"; else sel_mouse_save = ">"; break; case 3: /* do range stuff here later? */ break; } } void sel_mouse() { int x,y; int button; sel_mouse_save = Nullch; read_tty(buf,1); button = (int)(buf[0]); read_tty(buf,1); x = (int)(buf[0])-33; read_tty(buf,1); y = (int)(buf[0])-33; sel_do_mouse(button&3,x,y); /* get the button-up event */ while (1) { getcmd(buf); if (*buf == 3) /* got the button-up */ break; /* otherwise just eat any other keystrokes */ } /* interpret the button-up event */ read_tty(buf,1); button = (int)(buf[0]); read_tty(buf,1); x = (int)(buf[0])-33; read_tty(buf,1); y = (int)(buf[0])-33; sel_do_mouse(button&3,x,y); if (sel_mouse_save) pushstring(sel_mouse_save,0); } /* Display a menu of threads/subjects/articles for the user to choose from. ** If "cmd" is '+' we display all the unread items and allow the user to mark ** them as selected and perform various commands upon them. If "cmd" is 'U' ** the list consists of previously-read items for the user to mark as unread. */ char do_selector(cmd) char_int cmd; { register int j; int got_dash; int ch, action; char page_char, end_char; char promptbuf[80]; char oldmode = mode; char *in_select; mode = 't'; art = lastart+1; sel_rereading = (cmd == 'U'); clear_on_stop = TRUE; empty_ok = FALSE; set_sel_mode(cmd); xmouse_on(); if (!cache_range(sel_rereading? absfirst : firstart, lastart)) { sel_ret = '+'; goto sel_exit; } start_selector: /* Setup for selecting articles to read or set unread */ if (sel_rereading) { page_char = '>'; end_char = 'Z'; sel_page_app = Null(ARTICLE**); sel_page_sp = Nullsubj; sel_mask = AF_DELSEL; } else { page_char = page_select; end_char = end_select; if (curr_artp) { sel_last_ap = curr_artp; sel_last_sp = curr_artp->subj; } sel_mask = AF_SEL; } selected_only = TRUE; count_subjects(cmd ? CS_UNSEL_STORE : CS_NORM); /* If nothing to display, we're done. */ if (!article_count && !empty_ok) { empty_complaint(); sel_ret = '+'; goto sel_exit; } init_pages(FILL_LAST_PAGE); sel_item_index = 0; *promptbuf = '\0'; disp_status_line = FALSE; if (added_articles) { register long i = added_articles, j; register ARTICLE *ap = article_ptr(lastart - i + 1); for (j = 0; j < added_articles; j++, ap++) { if (ap->flags & AF_READ) i--; } if (i == added_articles) sprintf(promptbuf, "** %ld new article%s arrived ** ", (long)added_articles, added_articles == 1? nullstr : "s"); else sprintf(promptbuf, "** %ld of %ld new articles unread ** ", i, (long)added_articles); disp_status_line = TRUE; } added_articles = 0; if (cmd && selected_count) { sprintf(promptbuf+strlen(promptbuf), "%ld article%s selected.", (long)selected_count, selected_count == 1? " is" : "s are"); disp_status_line = TRUE; } cmd = 0; display_selector: /* Present a page of items to the user */ display_page(); /* Check if there is really anything left to display. */ if (!sel_item_cnt && !empty_ok) { empty_complaint(); sel_ret = 'q'; goto sel_exit; } empty_ok = FALSE; if (sel_item_index >= sel_item_cnt) sel_item_index = 0; if (disp_status_line) { printf("\n%s", promptbuf); if (can_home) { carriage_return(); goto_line(sel_last_line+1, sel_last_line); } else putchar('\n'); } reask_selector: /* Prompt the user */ #ifdef MAILCALL setmail(FALSE); #endif if (sel_at_end) sprintf(cmd_buf, "%s [%c%c] --", (!sel_prior_arts? "All" : "Bot"), end_char, page_char); else sprintf(cmd_buf, "%s%ld%% [%c%c] --", (!sel_prior_arts? "Top " : nullstr), (long)((sel_prior_arts+sel_page_arts)*100 / sel_total_arts), page_char, end_char); interp(buf, sizeof buf, mailcall); sprintf(promptbuf, "%s-- %s %s (%s%s order) -- %s", buf, sel_exclusive? "SELECTED" : "Select", sel_mode_string, sel_direction<0? "reverse " : nullstr, sel_sort_string, cmd_buf); #ifdef CLEAREOL if (erase_screen && can_home_clear) clear_rest(); #endif standout(); fputs(promptbuf, stdout); un_standout(); if (can_home) carriage_return(); sel_line = sel_last_line; position_selector: got_dash = 0; if (can_home) goto_line(sel_line, sel_items[sel_item_index].line); sel_line = sel_items[sel_item_index].line; reinp_selector: /* Grab some commands from the user */ fflush(stdout); eat_typeahead(); spin_char = sel_chars[sel_item_index]; cache_until_key(); spin_char = ' '; #ifdef CONDSUB getcmd(buf); if (*buf == ' ') setdef(buf, sel_at_end? &end_char : &page_char); ch = *buf; #else getcmd(cmd_buf); /* If no conditionals, don't allow macros */ buf[0] = ch = *cmd_buf; buf[1] = FINISHCMD; #endif if (errno) ch = Ctl('l'); if (disp_status_line) { if (can_home) { goto_line(sel_line, sel_last_line+1); erase_eol(); sel_line = sel_last_line+1; } disp_status_line = FALSE; } /* CAA: hack for mouse support */ if (ch == Ctl('c')) { sel_mouse(); goto position_selector; } if (ch == '-') { got_dash = 1; if (!can_home) putchar('-'), fflush(stdout); goto reinp_selector; } in_select = index(sel_chars, ch); if (in_select) { j = in_select - sel_chars; if (j >= sel_item_cnt) { dingaling(); goto position_selector; } else if (got_dash) ; else if (sel_items[j].sel == 1) action = (sel_rereading ? 'k' : '-'); else action = '+'; } else if (ch == '*' && sel_mode == SM_ARTICLE) { register ARTICLE *ap = (ARTICLE*)sel_items[sel_item_index].ptr; if (sel_items[sel_item_index].sel) deselect_subject(ap->subj); else select_subject(ap->subj, 0); update_page(); goto position_selector; } else if (ch == 'y' || ch == '.' || ch == '*') { j = sel_item_index; if (sel_items[j].sel == 1) action = (sel_rereading ? 'k' : '-'); else action = '+'; } else if (ch == 'k' || ch == 'j' || ch == ',') { j = sel_item_index; action = 'k'; } else if (ch == 'm' || ch == '\\') { j = sel_item_index; action = '-'; } else if (ch == 'M') { j = sel_item_index; action = 'M'; } else if (ch == '@') { sel_item_index = 0; j = sel_item_cnt-1; got_dash = 1; action = '@'; } else if (ch == '[' || ch == 'p') { if (--sel_item_index < 0) sel_item_index = sel_item_cnt ? sel_item_cnt-1 : 0; goto position_selector; } else if (ch == ']' || ch == 'n') { if (++sel_item_index >= sel_item_cnt) sel_item_index = 0; goto position_selector; } else { sel_ret = ch; switch (sel_command(ch)) { case DS_POS: if (!clean_screen) goto display_selector; goto position_selector; case DS_ASK: if (!clean_screen) goto display_selector; goto reask_selector; case DS_DISPLAY: ds_display: if (disp_status_line) strcpy(promptbuf, buf); goto display_selector; case DS_UPDATE: if (!clean_screen) goto ds_display; if (disp_status_line) { printf("\n%s",buf); if (can_home) { carriage_return(); up_line(); erase_eol(); } } update_page(); if (can_home) { goto_line(sel_line, sel_last_line); sel_line = sel_last_line; } goto reask_selector; case DS_RESTART: goto start_selector; case DS_QUIT: sel_cleanup(); if (!output_chase_phrase) putchar('\n') FLUSH; goto sel_exit; case DS_STATUS: disp_status_line = TRUE; if (!clean_screen) { strcpy(promptbuf, buf); goto display_selector; } if (can_home) { goto_line(sel_line, sel_last_line+1); sel_line = sel_last_line+1; } else putchar('\n'); fputs(buf, stdout); if (can_home) carriage_return(); else putchar('\n'); goto position_selector; } } /* The user manipulated one of the letters -- handle it. */ if (!got_dash) sel_item_index = j; else { if (j < sel_item_index) { ch = sel_item_index-1; sel_item_index = j; j = ch; } } if (++j == sel_item_cnt) j = 0; do { register int sel = sel_items[sel_item_index].sel; register SUBJECT *sp = (SUBJECT*)sel_items[sel_item_index].ptr; if (can_home) { goto_line(sel_line, sel_items[sel_item_index].line); sel_line = sel_items[sel_item_index].line; } if (action == '@') { if (sel == 2) ch = (sel_rereading ? '+' : ' '); else if (sel_rereading) ch = 'k'; else if (sel == 1) ch = '-'; else ch = '+'; } else ch = action; switch (ch) { case '+': if (sel_mode == SM_THREAD) select_thread(sp->thread, 0); else if (sel_mode == SM_SUBJECT) select_subject(sp, 0); else select_article((ARTICLE*)sp, 0); output_sel(1); break; case '-': case 'k': case 'M': { bool sel_reread_save = sel_rereading; if (ch == 'M') { if (sel_mode == SM_ARTICLE) delay_unmark((ARTICLE*)sp); else { register ARTICLE *ap; if (sel_mode == SM_THREAD) { for (ap = first_art(sp); ap; ap = next_art(ap)) if (!(ap->flags & AF_READ) ^ sel_rereading) delay_unmark(ap); } else { for (ap = sp->articles; ap; ap = ap->subj_next) if (!(ap->flags & AF_READ) ^ sel_rereading) delay_unmark(ap); } } } if (ch == '-') sel_rereading = FALSE; else sel_rereading = TRUE; if (sel_mode == SM_THREAD) deselect_thread(sp->thread); else if (sel_mode == SM_SUBJECT) deselect_subject(sp); else deselect_article((ARTICLE*)sp); sel_rereading = sel_reread_save; output_sel(ch == '-'? 0 : 2); break; } } fflush(stdout); if (++sel_item_index == sel_item_cnt) sel_item_index = 0; if (can_home) carriage_return(); } while (sel_item_index != j); goto position_selector; sel_exit: if (sel_rereading) { sel_rereading = 0; sel_mask = AF_SEL; } if (sel_mode != SM_ARTICLE || sel_sort == SS_GROUPS || sel_sort == SS_SUBJECT) { if (artptr_list) { free((char*)artptr_list); artptr_list = sel_page_app = Null(ARTICLE**); sort_subjects(); } artptr = Null(ARTICLE**); #ifdef ARTSEARCH if (!ThreadedGroup) srchahead = -1; #endif } #ifdef ARTSEARCH else srchahead = 0; #endif selected_only = (selected_count != 0); if (sel_ret != '#') count_subjects(sel_ret == '+'? CS_RESELECT : CS_UNSELECT); clear_on_stop = FALSE; mode = oldmode; if (sel_ret == '+') { art = curr_art; artp = curr_artp; } else top_article(); xmouse_off(); return sel_ret; } static void sel_cleanup() { if (sel_rereading) { /* Turn selections into unread selected articles. Let ** count_subjects() fix the counts after we're through. */ register SUBJECT *sp; sel_last_ap = Nullart; sel_last_sp = Nullsubj; for (sp = first_subject; sp; sp = sp->next) unkill_subject(sp); } else { if (sel_mode == SM_ARTICLE) { register ARTICLE *ap; register ART_NUM an; for (an=absfirst, ap=article_ptr(an); an<=lastart; an++, ap++) { if (ap->flags & AF_DEL) { ap->flags &= ~AF_DEL; set_read(ap); } } } else { register SUBJECT *sp; for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_DEL) { sp->flags &= ~SF_DEL; if (sel_mode == SM_THREAD) kill_thread(sp->thread, KF_UNSELECTED); else kill_subject(sp, KF_UNSELECTED); } } } } } static int sel_command(ch) char_int ch; { if (can_home) goto_line(sel_line, sel_last_line); sel_line = sel_last_line; clean_screen = TRUE; do_command: output_chase_phrase = TRUE; switch (ch) { case '>': sel_item_index = 0; if (next_page()) return DS_DISPLAY; return DS_POS; case '<': sel_item_index = 0; if (prev_page()) return DS_DISPLAY; return DS_POS; case '^': case Ctl('r'): sel_item_index = 0; if (first_page()) return DS_DISPLAY; return DS_POS; case '$': sel_item_index = 0; if (last_page()) return DS_DISPLAY; return DS_POS; case Ctl('l'): return DS_DISPLAY; case Ctl('^'): erase_eol(); /* erase the prompt */ #ifdef MAILCALL setmail(TRUE); /* force a mail check */ #endif return DS_ASK; case '#': { register SUBJECT *sp; for (sp = first_subject; sp; sp = sp->next) sp->flags &= ~SF_VISIT; selected_count = 0; sp = (SUBJECT*)sel_items[sel_item_index].ptr; switch (sel_mode) { case SM_THREAD: deselect_thread(sp->thread); select_thread(sp->thread, 0); break; case SM_SUBJECT: deselect_subject(sp); select_subject(sp, 0); break; case SM_ARTICLE: deselect_article((ARTICLE*)sp); select_article((ARTICLE*)sp, 0); break; } return DS_QUIT; } case '\r': case '\n': if (!selected_count) { if (sel_rereading || sel_items[sel_item_index].sel != 2) { register SUBJECT *sp = (SUBJECT*)sel_items[sel_item_index].ptr; switch (sel_mode) { case SM_THREAD: select_thread(sp->thread, 0); break; case SM_SUBJECT: select_subject(sp, 0); break; case SM_ARTICLE: select_article((ARTICLE*)sp, 0); break; } } } return DS_QUIT; case 'Z': case '\t': return DS_QUIT; case 'q': case 'Q': return DS_QUIT; case Ctl('Q'): case '\033': case '+': sel_ret = '+'; return DS_QUIT; case 'N': case 'P': return DS_QUIT; case 'L': if (!*++display_mode) display_mode = select_order; return DS_DISPLAY; case 'Y': if (!dmcount) { sprintf(buf,"No marked articles to yank back."); return DS_STATUS; } yankback(); sel_line++; if (!sel_rereading) sel_cleanup(); disp_status_line = TRUE; count_subjects(CS_NORM); sel_page_sp = Nullsubj; sel_page_app = Null(ARTICLE**); init_pages(PRESERVE_PAGE); return DS_DISPLAY; case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_sp = Nullsubj; sel_page_app = Null(ARTICLE**); if (!cache_range(sel_rereading? absfirst : firstart, lastart)) sel_rereading = !sel_rereading; empty_ok = TRUE; return DS_RESTART; case '=': if (!sel_rereading) sel_cleanup(); if (sel_mode == SM_ARTICLE) { set_selector(sel_threadmode, sel_threadsort); sel_page_sp = sel_page_app[0]->subj; } else { set_selector(SM_ARTICLE, sel_artsort); sel_page_app = 0; } count_subjects(CS_NORM); sel_item_index = 0; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'S': if (!sel_rereading) sel_cleanup(); erase_eol(); /* erase the prompt */ reask_output: in_char("Selector mode: Threads, Subjects, Articles?", 'o', "tsa"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) fputs("\n\ Type t or SP to display/select thread groups (threads the group, if needed).\n\ Type s to display/select subject groups.\n\ Type a to display/select individual articles.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\ t or SP selects thread groups (threads the group too).\n\ s selects subject groups.\n\ a selects individual articles.\n\ q does nothing.\n\n\ ",stdout) FLUSH; #endif clean_screen = FALSE; goto reask_output; } else if (*buf == 'q') { if (can_home) { carriage_return(); erase_eol(); } return DS_ASK; } set_sel_mode(*buf); count_subjects(CS_NORM); init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'O': if (!sel_rereading) sel_cleanup(); erase_eol(); /* erase the prompt */ reask_sort: if (sel_mode == SM_ARTICLE) in_char("Order by Date, Subject, Author, Number, subject-date Groups?", 'q', "dsangDSANG"); else in_char("Order by Date, Subject, or Count?", 'q', "dscDSC"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) { fputs("\n\ Type d or SP to order the displayed items by date.\n\ Type s to order the items by subject.\n\ ",stdout) FLUSH; if (sel_mode == SM_ARTICLE) fputs("\ Type a to order the items by author.\n\ Type n to order the items by number.\n\ Type g to order the items in subject-groups by date.\n\ ",stdout) FLUSH; else fputs("\ Type c to order the items by article-count.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ d or SP sorts by date.\n\ s sorts by subject.\n\ ",stdout) FLUSH; if (sel_mode == SM_ARTICLE) fputs("\ a sorts by author.\n\ g sorts in subject-groups by date.\n\ ",stdout) FLUSH; else fputs("\ c sorts by article-count.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; } else if (*buf == 'q') { if (can_home) { carriage_return(); erase_eol(); } return DS_ASK; } set_sel_sort(*buf); count_subjects(CS_NORM); sel_page_sp = Nullsubj; sel_page_app = Null(ARTICLE**); init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'R': if (!sel_rereading) sel_cleanup(); sel_direction *= -1; count_subjects(CS_NORM); sel_page_sp = Nullsubj; sel_page_app = Null(ARTICLE**); init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; count_subjects(CS_NORM); sel_page_sp = Nullsubj; sel_page_app = Null(ARTICLE**); init_pages(FILL_LAST_PAGE); empty_ok = TRUE; sel_item_index = 0; return DS_DISPLAY; case 'X': case 'D': case 'J': if (!sel_rereading) { if (sel_mode == SM_ARTICLE) { register ARTICLE *ap, **app, **limit; limit = artptr_list + artptr_list_size; if (ch == 'D') app = sel_page_app; else app = artptr_list; for (;;) { ap = *app; if ((!(ap->flags & AF_SEL) ^ (ch == 'J')) || (ap->flags & AF_DEL)) if (!sel_exclusive || (ap->flags & AF_INCLUDED)) set_read(ap); app++; if (app >= limit || (ch == 'D' && app == sel_next_app)) break; } } else { register SUBJECT *sp; if (ch == 'D') sp = sel_page_sp; else sp = first_subject; for (;;) { if (((!(sp->flags & SF_SEL) ^ (ch == 'J')) && sp->misc) || (sp->flags & SF_DEL)) { if (!sel_exclusive || (sp->flags & SF_INCLUDED)) kill_subject(sp, ch=='J'? KF_ALL : KF_UNSELECTED); } sp = sp->next; if (!sp || (ch == 'D' && sp == sel_next_sp)) break; } } count_subjects(CS_UNSELECT); if (article_count && (ch == 'J' || (ch == 'D' && !selected_count))) { init_pages(FILL_LAST_PAGE); sel_item_index = 0; return DS_DISPLAY; } if (artptr_list && article_count) sort_articles(); return DS_QUIT; } else if (ch == 'J') { register SUBJECT *sp; for (sp = first_subject; sp; sp = sp->next) deselect_subject(sp); selected_subj_cnt = selected_count = 0; return DS_DISPLAY; } return DS_QUIT; case 'T': if (!ThreadedGroup) { sprintf(buf,"Group is not threaded."); return DS_STATUS; } /* FALL THROUGH */ case 'A': erase_eol(); /* erase the prompt */ if (sel_mode == SM_ARTICLE) artp = (ARTICLE*)sel_items[sel_item_index].ptr; else { register SUBJECT *sp = (SUBJECT*)sel_items[sel_item_index].ptr; if (sel_mode == SM_THREAD) { while (!sp->misc) sp = sp->thread_link; } artp = sp->articles; } art = article_num(artp); /* This call executes the action too */ switch (ask_memorize(ch)) { case 'j': case ',': count_subjects(sel_rereading ? CS_NORM : CS_UNSELECT); init_pages(PRESERVE_PAGE); sprintf(buf,"Kill memorized."); disp_status_line = TRUE; return DS_DISPLAY; case '.': sprintf(buf,"Selection memorized."); disp_status_line = TRUE; return DS_DISPLAY; case '+': sprintf(buf,"Selection memorized."); disp_status_line = TRUE; return DS_UPDATE; case 'c': case 'C': sprintf(buf,"Auto-commands cleared."); disp_status_line = TRUE; return DS_DISPLAY; case 'q': return DS_DISPLAY; case 'Q': break; } if (can_home) { carriage_return(); erase_eol(); } return DS_ASK; case Ctl('k'): xmouse_off(); edit_kfile(); xmouse_on(); return DS_DISPLAY; case ':': if (sel_mode == SM_ARTICLE) artp = (ARTICLE*)sel_items[sel_item_index].ptr; else { register SUBJECT *sp = (SUBJECT*)sel_items[sel_item_index].ptr; if (sel_mode == SM_THREAD) { while (!sp->misc) sp = sp->thread_link; } artp = sp->articles; } art = article_num(artp); /* FALL THROUGH */ case '/': case '&': case '!': erase_eol(); /* erase the prompt */ if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) return DS_ASK; goto extend_done; } if (ch == '&' || ch == '!') { xmouse_off(); one_command = TRUE; perform(buf, FALSE); one_command = FALSE; putchar('\n') FLUSH; clean_screen = FALSE; xmouse_on(); } else { int sel_art_save = selected_count; if (ch == ':') { clean_screen = (thread_perform() == 2) && clean_screen; if (!sel_rereading) { register SUBJECT *sp; for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_DEL) { sp->flags = 0; if (sel_mode == SM_THREAD) kill_thread(sp->thread, KF_UNSELECTED); else kill_subject(sp, KF_UNSELECTED); } } } } else { /* Force the search to begin at absfirst or firstart, ** depending upon whether they specified the 'r' option. */ art = lastart+1; page_line = 1; switch (art_search(buf, sizeof buf, FALSE)) { case SRCH_ERROR: case SRCH_ABORT: case SRCH_INTR: fputs("\nInterrupted\n", stdout) FLUSH; break; case SRCH_DONE: case SRCH_SUBJDONE: fputs("Done\n", stdout) FLUSH; break; case SRCH_NOTFOUND: fputs("\nNot found.\n", stdout) FLUSH; break; case SRCH_FOUND: break; } clean_screen = FALSE; } /* Recount, in case something has changed. */ count_subjects(sel_rereading ? CS_NORM : CS_UNSELECT); init_pages(PRESERVE_PAGE); sel_item_index = 0; sel_art_save -= selected_count; if (sel_art_save) { putchar('\n'); if (sel_art_save < 0) { fputs("S", stdout); sel_art_save *= -1; } else fputs("Des", stdout); printf("elected %d article%s.", sel_art_save, sel_art_save == 1 ? nullstr : "s"); clean_screen = FALSE; } if (!clean_screen) putchar('\n') FLUSH; }/* if !& else :/ */ if (clean_screen) { carriage_return(); up_line(); erase_eol(); return DS_ASK; } extend_done: if ((ch = pause_getcmd())) { got_cmd: if (ch > 0) { /* try to optimize the screen update for some commands. */ if (!index(sel_chars, ch) && (index("<+>^$!?&:;/hDEJLNOPqQRSUXYZ\n\r\t\033", ch) || ch == Ctl('k'))) { buf[0] = sel_ret = ch; buf[1] = FINISHCMD; goto do_command; } pushchar(ch | 0200); } } return DS_DISPLAY; case 'c': erase_eol(); /* erase the prompt */ if ((ch = ask_catchup()) == 'y' || ch == 'u') { count_subjects(CS_UNSELECT); if (ch != 'u' && article_count) { sel_page_sp = Nullsubj; sel_page_app = Null(ARTICLE**); init_pages(FILL_LAST_PAGE); return DS_DISPLAY; } sel_ret = 'Z'; return DS_QUIT; } if (ch != 'N') return DS_DISPLAY; if (can_home) { carriage_return(); erase_eol(); } return DS_ASK; case 'h': case '?': putchar('\n'); if ((ch = help_select()) || (ch = pause_getcmd())) goto got_cmd; return DS_DISPLAY; default: sprintf(buf,"Type ? for help."); settle_down(); if (clean_screen) return DS_STATUS; printf("\n%s\n",buf); goto extend_done; } } static void empty_complaint() { clear_on_stop = FALSE; putchar('\n'); if (sel_rereading) { #ifdef VERBOSE IF (verbose) fputs("\nNo articles to set unread.\n", stdout); ELSE #endif #ifdef TERSE fputs("\nNo articles.\n", stdout) FLUSH; #endif sel_rereading = 0; sel_mask = AF_SEL; } else { #ifdef VERBOSE IF (verbose) fputs("\nNo unread articles to select.", stdout); ELSE #endif #ifdef TERSE fputs("\nNo unread articles.", stdout); #endif putchar('\n'); /* let "them" FLUSH */ } selected_only = FALSE; art = curr_art; artp = curr_artp; } trn-3.6/nntp.c0000664000000000000000000002301507041327652010207 0ustar /* $Id: nntp.c,v 3.0 1991/11/22 04:12:21 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "init.h" #include "trn.h" #include "ngdata.h" #include "rcln.h" #include "cache.h" #include "bits.h" #include "head.h" #include "term.h" #include "final.h" #include "artio.h" #include "nntp.h" #ifdef USE_NNTP int nntp_list(type, arg, len) char *type; char *arg; int len; { int pos; sprintf(ser_line, "list %s ", type); pos = strlen(ser_line); strncpy(ser_line+pos, arg, len); ser_line[pos+len] = '\0'; nntp_command(ser_line); if (nntp_check(FALSE) != NNTP_CLASS_OK) { if (*ser_line == NNTP_CLASS_FATAL) return -1; return 0; } nntp_gets(ser_line, sizeof ser_line); #if defined(DEBUG) && defined(FLUSH) if (debug & DEB_NNTP) printf("<%s\n", ser_line) FLUSH; #endif if (NNTP_LIST_END(ser_line)) return 0; return 1; } /* try to access the specified group */ bool nntp_group(group, num) char *group; NG_NUM num; { sprintf(ser_line, "GROUP %s", group); nntp_command(ser_line); if (nntp_check(FALSE) != NNTP_CLASS_OK) { int ser_int = atoi(ser_line); if (ser_int != NNTP_NOSUCHGROUP_VAL && ser_int != NNTP_SYNTAX_VAL) { if (ser_int != NNTP_AUTH_NEEDED_VAL && ser_int != NNTP_ACCESS_VAL && ser_int != NNTP_AUTH_REJECT_VAL) { fprintf(stderr, "\nServer's response to GROUP %s:\n%s\n", group, ser_line); finalize(1); } } return FALSE; } if (num >= 0) { long count, first, last; (void) sscanf(ser_line,"%*d%ld%ld%ld",&count,&first,&last); /* NNTP mangles the high/low values when no articles are present. */ if (!count) abs1st[num] = ngmax[num]+1; else { abs1st[num] = (ART_NUM)first; if (last > ngmax[num]) ngmax[num] = (ART_NUM)last; } } return TRUE; } /* check on an article's existence */ bool nntp_stat(artnum) ART_NUM artnum; { sprintf(ser_line, "STAT %ld", (long)artnum); nntp_command(ser_line); return (nntp_check(TRUE) == NNTP_CLASS_OK); } /* check on an article's existence by its message id */ ART_NUM nntp_stat_id(msgid) char *msgid; { long artnum; sprintf(ser_line, "STAT %s", msgid); nntp_command(ser_line); if (nntp_check(TRUE) != NNTP_CLASS_OK || sscanf(ser_line, "%*d%ld", &artnum) != 1) return 0; return (ART_NUM)artnum; } /* prepare to get the header */ bool nntp_header(artnum) ART_NUM artnum; { sprintf(ser_line, "HEAD %ld", (long)artnum); nntp_command(ser_line); return (nntp_check(TRUE) == NNTP_CLASS_OK); } /* copy the body of an article to a temporary file */ static bool nntp_copybody _((char*,int,long)); static long body_pos = -1; static long body_end = 0; FILE * nntp_body(artnum) ART_NUM artnum; { char *artname; FILE *fp; if (!parseheader(artnum)) return Nullfp; artname = nntp_artname(); if (!(fp = fopen(artname, "w+"))) { fprintf(stderr, "\nUnable to write temporary file: '%s'.\n", artname); finalize(1); } sprintf(ser_line, "BODY %ld", (long)artnum); nntp_command(ser_line); if (nntp_check(TRUE) != NNTP_CLASS_OK) { /* and get it's reaction */ fclose(fp); errno = ENOENT; /* Simulate file-not-found */ return Nullfp; } fwrite(headbuf, 1, strlen(headbuf), fp); body_pos = 0; body_end = ftell(fp); htype[PAST_HEADER].ht_minpos = body_end; #ifdef GRAB_IT_ALL nntp_copybody(ser_line, sizeof ser_line, 0x7fffffffL); #endif fseek(fp, 0L, 0); return fp; } long nntp_artsize() { return body_pos < 0 ? body_end : -1; } static bool nntp_copybody(s, limit, pos) char *s; int limit; long pos; { int len; bool had_nl = TRUE; bool found_nl; while (pos > body_end || !had_nl) { found_nl = nntp_gets(s, limit-1); if (had_nl) { if (NNTP_LIST_END(s)) { fseek(artfp, body_pos, 0); body_pos = -1; return FALSE; } if (s[0] == '.') { strcpy(s, s+1); } } len = strlen(s); if (found_nl) strcpy(s+len, "\n"); fputs(s, artfp); body_end = ftell(artfp); had_nl = found_nl; } return TRUE; } int nntp_finishbody(bmode) int bmode; { char b[NNTP_STRLEN]; if (body_pos < 0) return FALSE; if (bmode == FB_OUTPUT) { #ifdef VERBOSE IF(verbose) printf("Receiving the rest of the article..."), fflush(stdout); ELSE #endif #ifdef TERSE printf("Receiving..."), fflush(stdout); #endif } if (body_end != body_pos) fseek(artfp, body_end, 0); if (bmode != FB_BACKGROUND) nntp_copybody(b, sizeof b, 0x7fffffffL); else { while (nntp_copybody(b, sizeof b, body_end+1)) { if (input_pending()) break; } if (body_pos >= 0) fseek(artfp, body_pos, 0); } if (bmode == FB_OUTPUT) { carriage_return(); erase_eol(); /* erase the prompt */ carriage_return(); /* Resets kernel's tab column counter to 0 */ } return TRUE; } void nntp_seekart(pos) long pos; { if (body_pos >= 0) { if (body_end < pos) { char b[NNTP_STRLEN]; fseek(artfp, body_end, 0); nntp_copybody(b, sizeof b, pos); if (body_pos >= 0) body_pos = pos; } else body_pos = pos; } fseek(artfp, pos, 0); } long nntp_tellart() { return body_pos < 0 ? ftell(artfp) : body_pos; } char * nntp_readart(s, limit) char *s; int limit; { if (body_pos >= 0) { if (body_pos == body_end) { if (!nntp_copybody(s, limit, body_pos+1)) return Nullch; body_pos = body_end; return s; } s = fgets(s, limit, artfp); body_pos = ftell(artfp); if (body_pos == body_end) fseek(artfp, body_pos, 0); return s; } return fgets(s, limit, artfp); } /* This is a 1-relative list */ static int maxdays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; time_t nntp_time() { char *s; int year, month, day, hh, mm; time_t ss; nntp_command("XDATE" + (CompliantServer? 0 : 1)); if (nntp_check(FALSE) != NNTP_CLASS_INF) return time((time_t*)NULL); s = rindex(ser_line, ' ') + 1; month = (s[4] - '0') * 10 + (s[5] - '0'); day = (s[6] - '0') * 10 + (s[7] - '0'); hh = (s[8] - '0') * 10 + (s[9] - '0'); mm = (s[10] - '0') * 10 + (s[11] - '0'); ss = (s[12] - '0') * 10 + (s[13] - '0'); s[4] = '\0'; year = atoi(s); /* This simple algorithm will be valid until the year 2400 */ if (year % 4) maxdays[2] = 28; else maxdays[2] = 29; if (month < 1 || month > 12 || day < 1 || day > maxdays[month] || hh < 0 || hh > 23 || mm < 0 || mm > 59 || ss < 0 || ss > 59) return time((time_t*)NULL); for (month--; month; month--) day += maxdays[month]; ss = ((((year-1970) * 365 + (year-1969)/4 + day - 1) * 24L + hh) * 60 + mm) * 60 + ss; return ss; } bool nntp_newgroups(t) time_t t; { struct tm *ts; ts = gmtime(&t); sprintf(ser_line, "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT", ts->tm_year % 100, ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec); nntp_command(ser_line); return (nntp_check(TRUE) == NNTP_CLASS_OK); } bool nntp_listgroup() { #ifdef NO_LISTGROUP static bool listgroup_works = FALSE; #else static bool listgroup_works = TRUE; #endif if (!listgroup_works) return FALSE; nntp_command("XLISTGROUP" + (CompliantServer? 0 : 1)); if (nntp_check(FALSE) != NNTP_CLASS_OK) { listgroup_works = FALSE; return FALSE; } return TRUE; } char * nntp_artname() { static char artname[20]; sprintf(artname,"rrn.%ld",our_pid); return artname; } char nntp_handle_timeout(strict) bool_int strict; { static bool handling_timeout = FALSE; char last_command_save[NNTP_STRLEN]; char ch; if (handling_timeout) return NNTP_CLASS_FATAL; handling_timeout = TRUE; strcpy(last_command_save, last_command); nntp_close(FALSE); if (!nntp_connect(0) || (in_ng && !nntp_group(ngname, -1))) { nntp_error("\n503 Server timed out.\n"); finalize(1); } nntp_command(last_command_save); ch = nntp_check(strict); handling_timeout = FALSE; return ch; } /* cleanup the odds and ends associated with NNTP usage */ void nntp_cleanup() { UNLINK(nntp_artname()); if (*active_name) UNLINK(active_name); nntp_close(TRUE); } #ifdef USE_XTHREAD static long rawbytes = -1; /* bytes remaining to be transfered */ /* nntp_readcheck -- get a line of text from the server, interpreting ** it as a status message for a binary command. Call this once ** before calling nntp_read() for the actual data transfer. */ long nntp_readcheck() { /* try to get the status line and the status code */ if (nntp_check(FALSE) != NNTP_CLASS_OK) return rawbytes = -1; /* try to get the number of bytes being transfered */ if (sscanf(ser_line, "%*d%ld", &rawbytes) != 1) return rawbytes = -1; return rawbytes; } /* nntp_read -- read data from the server in binary format. This call must ** be preceeded by an appropriate binary command and an nntp_readcheck call. */ long nntp_read(buf, n) char *buf; long n; { /* if no bytes to read, then just return EOF */ if (rawbytes < 0) return 0; #ifdef HAS_SIGHOLD sighold(SIGINT); #endif /* try to read some data from the server */ if (rawbytes) { n = fread(buf, 1, n > rawbytes ? rawbytes : n, ser_rd_fp); rawbytes -= n; } else n = 0; /* if no more left, then fetch the end-of-command signature */ if (!rawbytes) { char buf[5]; /* "\r\n.\r\n" */ fread(buf, 1, 5, ser_rd_fp); rawbytes = -1; } #ifdef HAS_SIGHOLD sigrelse(SIGINT); #endif return n; } #endif /* USE_XTHREAD */ #endif /* USE_NNTP */ trn-3.6/decode.c0000664000000000000000000000106205420174063010444 0ustar /* $Id: decode.c,v 3.0 1991/11/22 04:12:25 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "INTERN.h" #include "decode.h" void decode_init() { unship_init(); } void decode_end() { if (decode_fp != Nullfp) { fclose(decode_fp); decode_fp = Nullfp; printf("\n%s INCOMPLETE -- removed.\n", decode_dest) FLUSH; unlink(decode_dest); } } trn-3.6/hash.c0000664000000000000000000001613305551632705010160 0ustar /* $Id: hash.c,v 3.0 1992/12/14 00:14:13 davison Trn $ */ /* This file is an altered version of a set of hash routines by ** Geoffrey Collyer. See the end of the file for his copyright. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "final.h" #include "INTERN.h" #include "hash.h" /* tunable parameters */ #define RETAIN 600 /* retain & recycle this many HASHENTs */ static HASHENT *hereuse = NULL; static int reusables = 0; static HASHENT **hashfind _((HASHTABLE*,char*,int)); static unsigned hash _((char*,int)); static int default_cmp _((char*,int,HASHDATUM)); static HASHENT *healloc _((void)); static void hefree _((HASHENT*)); HASHTABLE * hashcreate(size, cmpfunc) unsigned size; /* a crude guide to size */ int (*cmpfunc)(); { register HASHTABLE *tbl; /* allocate HASHTABLE and (HASHENT*) array together to reduce the ** number of malloc calls. */ register struct alignalloc { HASHTABLE ht; HASHENT *hepa[1]; /* longer than it looks */ } *aap; if (size < 1) /* size < 1 is nonsense */ size = 1; aap = (struct alignalloc*) safemalloc(sizeof *aap + (size-1)*sizeof (HASHENT*)); bzero((char*)aap, sizeof *aap + (size-1)*sizeof (HASHENT*)); tbl = &aap->ht; tbl->ht_size = size; tbl->ht_magic = HASHMAG; tbl->ht_cmp = (cmpfunc == NULL? default_cmp: cmpfunc); tbl->ht_addr = aap->hepa; return tbl; } /* Free all the memory associated with tbl, erase the pointers to it, and ** invalidate tbl to prevent further use via other pointers to it. */ void hashdestroy(tbl) register HASHTABLE *tbl; { register unsigned idx; register HASHENT *hp, *next; register HASHENT **hepp; register int tblsize; if (tbl == NULL || BADTBL(tbl)) return; tblsize = tbl->ht_size; hepp = tbl->ht_addr; for (idx = 0; idx < tblsize; idx++) { for (hp = hepp[idx]; hp != NULL; hp = next) { next = hp->he_next; hp->he_next = NULL; hefree(hp); } hepp[idx] = NULL; } tbl->ht_magic = 0; /* de-certify this table */ tbl->ht_addr = NULL; free((char*)tbl); } void hashstore(tbl, key, keylen, data) register HASHTABLE *tbl; char *key; int keylen; HASHDATUM data; { register HASHENT *hp; register HASHENT **nextp; nextp = hashfind(tbl, key, keylen); hp = *nextp; if (hp == NULL) { /* absent; allocate an entry */ hp = healloc(); hp->he_next = NULL; hp->he_keylen = keylen; *nextp = hp; /* append to hash chain */ } hp->he_data = data; /* supersede any old data for this key */ } void hashdelete(tbl, key, keylen) register HASHTABLE *tbl; char *key; int keylen; { register HASHENT *hp; register HASHENT **nextp; nextp = hashfind(tbl, key, keylen); hp = *nextp; if (hp == NULL) /* absent */ return; *nextp = hp->he_next; /* skip this entry */ hp->he_next = NULL; hp->he_data.dat_ptr = NULL; hefree(hp); } HASHENT **slast_nextp; int slast_keylen; HASHDATUM /* data corresponding to key */ hashfetch(tbl, key, keylen) register HASHTABLE *tbl; char *key; int keylen; { register HASHENT *hp; register HASHENT **nextp; static HASHDATUM errdatum = { NULL, 0 }; nextp = hashfind(tbl, key, keylen); slast_nextp = nextp; slast_keylen = keylen; hp = *nextp; if (hp == NULL) /* absent */ return errdatum; else return hp->he_data; } void hashstorelast(data) HASHDATUM data; { register HASHENT *hp; hp = *slast_nextp; if (hp == NULL) { /* absent; allocate an entry */ hp = healloc(); hp->he_next = NULL; hp->he_keylen = slast_keylen; *slast_nextp = hp; /* append to hash chain */ } hp->he_data = data; /* supersede any old data for this key */ } /* Visit each entry by calling nodefunc at each, with key, data and extra as ** arguments. */ void hashwalk(tbl, nodefunc, extra) HASHTABLE *tbl; register void (*nodefunc)(); register int extra; { register unsigned idx; register HASHENT *hp; register HASHENT **hepp; register int tblsize; if (BADTBL(tbl)) return; hepp = tbl->ht_addr; tblsize = tbl->ht_size; for (idx = 0; idx < tblsize; idx++) for (hp = hepp[idx]; hp != NULL; hp = hp->he_next) (*nodefunc)(&hp->he_data, extra); } /* The returned value is the address of the pointer that refers to the ** found object. Said pointer may be NULL if the object was not found; ** if so, this pointer should be updated with the address of the object ** to be inserted, if insertion is desired. */ static HASHENT ** hashfind(tbl, key, keylen) register HASHTABLE *tbl; char *key; register int keylen; { register HASHENT *hp, *prevhp = NULL; register HASHENT **hepp; register unsigned size; if (BADTBL(tbl)) { fputs("Hash table is invalid.",stderr); finalize(1); } size = tbl->ht_size; hepp = &tbl->ht_addr[hash(key,keylen) % size]; for (hp = *hepp; hp != NULL; prevhp = hp, hp = hp->he_next) { if (hp->he_keylen == keylen && !(*tbl->ht_cmp)(key, keylen, hp->he_data)) break; } /* assert: *(returned value) == hp */ return (prevhp == NULL? hepp: &prevhp->he_next); } static unsigned /* not yet taken modulus table size */ hash(key, keylen) register char *key; register int keylen; { register unsigned hash = 0; while (keylen--) hash += *key++; return hash; } static int default_cmp(key, keylen, data) char *key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, data.dat_ptr, keylen); } static HASHENT * healloc() /* allocate a hash entry */ { register HASHENT *hp; if (hereuse == NULL) return (HASHENT*)safemalloc(sizeof (HASHENT)); /* pull the first reusable one off the pile */ hp = hereuse; hereuse = hereuse->he_next; hp->he_next = NULL; /* prevent accidents */ reusables--; return hp; } static void hefree(hp) /* free a hash entry */ register HASHENT *hp; { if (reusables >= RETAIN) /* compost heap is full? */ free((char*)hp); /* yup, just pitch this one */ else { /* no, just stash for reuse */ ++reusables; hp->he_next = hereuse; hereuse = hp; } } /* * Copyright (c) 1992 Geoffrey Collyer * All rights reserved. * Written by Geoffrey Collyer. * * This software is not subject to any license of the American Telephone * and Telegraph Company, the Regents of the University of California, or * the Free Software Foundation. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. The author is not responsible for the consequences of use of this * software, no matter how awful, even if they arise from flaws in it. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Since few users ever read sources, * credits must appear in the documentation. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. Since few users * ever read sources, credits must appear in the documentation. * * 4. This notice may not be removed or altered. */ trn-3.6/trn.c0000664000000000000000000003731305663312267010045 0ustar /* trn -- threaded readnews program based on rn 4.4 * * Author/Maintainer of trn: davison@borland.com (Wayne Davison) * Organization: Borland International * Author/Maintainer of rn: sob@bcm.tmc.edu (Stan Barber) * Organization: Baylor College of Medicine, Houston,Tx * Original Author: lwall@sdcrdcf.UUCP (Larry Wall) * Organization: System Development Corporation, Santa Monica * * History: * 01/14/83 - rn begun * 04/08/83 - rn 1.0 * 09/01/83 - rn 2.0 * 05/01/85 - rn 4.3 * 11/01/89 - rn/rrn integration * 11/25/89 - trn begun * 07/21/90 - trn 1.0 * 07/04/91 - rn 4.4 * 11/25/91 - trn 2.0 * 03/01/93 - trn 3.0 (or so... :-) ) */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "patchlevel.h" static char rnid[] = "@(#)$Id: trn.c,v 3.0 1991/11/22 00:14:59 davison Trn $"; static char patchlevel[] = PATCHLEVEL; #include "INTERN.h" #include "common.h" #include "trn.h" #include "EXTERN.h" #include "rcstuff.h" #include "term.h" #include "final.h" #include "search.h" #include "ngdata.h" #include "ngstuff.h" #include "util.h" #include "util2.h" #include "only.h" #include "ngsrch.h" #include "help.h" #include "last.h" #include "init.h" #include "intrp.h" #include "rcln.h" #include "sw.h" #include "cache.h" #include "addng.h" #include "ng.h" #include "kfile.h" #include "nntp.h" void rn_init() { ; } void main(argc,argv) int argc; char *argv[]; { bool foundany; register char *s; bool oh_for_the_good_old_days = FALSE; int direction = 1; #if !THREAD_INIT /* Default to threaded operation if our name starts with a 't' */ if ((s = rindex(argv[0],'/')) == Nullch) s = argv[0]; else s++; if (*s == 't') use_threads = TRUE; else select_on = 0; #endif foundany = initialize(argc,argv); if (maxngtodo) starthere = 0; else if (!foundany) { /* nothing to do? */ #ifdef VERBOSE if (verbose) fputs("\ No unread news in subscribed-to newsgroups. To subscribe to a new\n\ newsgroup use the g command.\n\ ",stdout) FLUSH; #endif starthere = nextrcline; } /* loop through all unread news */ { bool special = FALSE; /* temporarily allow newsgroup */ /* with no unread news? */ bool retry; /* cycle back to top of list? */ NG_NUM recent_ng = 0; current_ng = 0; for (;;) { retry = FALSE; if (findlast) { findlast = FALSE; starthere = 0; if (*lastngname) { if ((ng = find_ng(lastngname)) == nextrcline) ng = 0; else { set_ngname(lastngname); set_toread(ng); if (toread[ng] <= TR_NONE) ng = 0; } } } else { ng = starthere; starthere = 0; } while (ng <= nextrcline) { /* for each newsgroup */ if (ng == nextrcline) { /* after the last newsgroup? */ mode = 'f'; #ifdef ONLY if (maxngtodo) { if (retry) #ifdef VERBOSE IF(verbose) printf("\nRestriction %s%s still in effect.\n", ngtodo[0], maxngtodo > 1 ? ", etc." : nullstr) FLUSH; ELSE #endif #ifdef TERSE fputs("\n(\"Only\" mode.)\n",stdout) FLUSH; #endif else { #ifdef VERBOSE IF(verbose) fputs("\nNo articles under restriction.", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nNo \"only\" articles.",stdout) FLUSH; #endif end_only(); /* release the restriction */ retry = TRUE; } } #endif } else { bool shoe_fits; /* newsgroup matches restriction? */ mode = 'n'; if (toread[ng] >= TR_NONE) { /* recalc toread? */ set_ngname(rcline[ng]); shoe_fits = inlist(ngname); if (shoe_fits) set_toread(ng); if (paranoid) { recent_ng = current_ng; current_ng = ng; cleanup_rc(); /* this may move newsgroups around */ ng = current_ng; set_ngname(rcline[ng]); } } else shoe_fits = TRUE; if (toread[ng] < (emptyOnly || special ? TR_NONE : TR_ONE) || !shoe_fits) { /* unwanted newsgroup? */ ng += direction; /* then skip it */ if (ng < 0) { ng = 1; direction = 1; } continue; } } special = FALSE; /* go back to normal mode */ if (ng != current_ng) { recent_ng = current_ng; /* remember previous newsgroup */ current_ng = ng; /* remember current newsgroup */ } reask_newsgroup: unflush_output(); /* disable any ^O in effect */ if (ng >= nextrcline) { dfltcmd = (retry ? "npq" : "qnp"); #ifdef VERBOSE IF(verbose) printf("\n****** End of newsgroups -- what next? [%s] ", dfltcmd); ELSE #endif #ifdef TERSE printf("\n**** End -- next? [%s] ", dfltcmd); #endif } else { ThreadedGroup = (use_threads && rcchar[ng] != '0'); dfltcmd = (select_on && (ART_NUM)toread[ng] >= select_on ? "+ynq" : "ynq"); #ifdef VERBOSE IF(verbose) printf("\n%s %3ld unread article%s in %s -- read now? [%s] ", ThreadedGroup? "======" : "******", (long)toread[ng], (toread[ng]==TR_ONE ? nullstr : "s"), ngname, dfltcmd); ELSE #endif #ifdef TERSE printf("\n%s %3ld in %s -- read? [%s] ", ThreadedGroup? "====" : "****", (long)toread[ng],ngname,dfltcmd); #endif } fflush(stdout); reinp_newsgroup: eat_typeahead(); getcmd(buf); if (errno || *buf == '\f') { putchar('\n') FLUSH; /* if return from stop signal */ goto reask_newsgroup; /* give them a prompt again */ } setdef(buf,dfltcmd); #ifdef VERIFY printcmd(); #endif do_command: direction = 1; /* default to forward motion */ addnewbydefault = 0; switch (*buf) { case 'P': /* goto previous newsgroup */ special = TRUE; /* don't skip it if toread==0 */ /* FALL THROUGH */ case 'p': /* find previous unread newsgroup */ if (ng > 0) ng--; direction = -1; /* go backward in the newsrc */ break; case '-': ng = recent_ng; /* recall previous newsgroup */ if (ng < nextrcline) if (!get_ng(rcline[ng],0)) ng = current_ng; special = TRUE; /* don't skip it if toread==0 */ break; case 'x': putchar('\n'); in_char("Confirm: exit and abandon .newsrc changes?", 'A', "yn"); putchar('\n') FLUSH; if (*buf != 'y') break; oh_for_the_good_old_days = TRUE; printf("\nMoving changed version to '.newnewsrc'."); /* FALL THROUGH */ case 'q': case 'Q': /* quit? */ putchar('\n') FLUSH; ng = nextrcline+1; /* satisfy */ retry = FALSE; /* loop conditions */ break; case '^': putchar('\n') FLUSH; ng = 0; break; case 'n': /* find next unread newsgroup */ if (ng == nextrcline) { putchar('\n') FLUSH; retry = TRUE; } else if (toread[ng] > TR_NONE) retry = TRUE; ng++; break; case 'N': /* goto next newsgroup */ special = TRUE; /* and don't skip it if toread==0 */ ng++; break; case '1': /* goto 1st newsgroup */ ng = 0; special = TRUE; /* and don't skip it if toread==0 */ break; case '$': ng = nextrcline; /* goto last newsgroup */ retry = TRUE; break; case 'L': list_newsgroups(); goto reask_newsgroup; case '/': case '?': /* scan for newsgroup pattern */ #ifdef NGSEARCH switch (ng_search(buf,TRUE)) { case NGS_ERROR: goto reask_newsgroup; case NGS_ABORT: goto reinp_newsgroup; case NGS_INTR: #ifdef VERBOSE IF(verbose) fputs("\n(Interrupted)\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n(Intr)\n",stdout) FLUSH; #endif ng = current_ng; goto reask_newsgroup; case NGS_FOUND: special = TRUE; /* don't skip it if toread==0 */ break; case NGS_NOTFOUND: #ifdef VERBOSE IF(verbose) fputs("\n\nNot found -- use a or g to add newsgroups\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\nNot found\n",stdout) FLUSH; #endif goto reask_newsgroup; } #else notincl("/"); #endif break; case 'm': #ifndef RELOCATE notincl("m"); break; #endif case 'g': /* goto named newsgroup */ if (!finish_command(FALSE)) /* if they didn't finish command */ goto reinp_newsgroup; /* go try something else */ for (s = buf+1; *s == ' '; s++); /* skip leading spaces */ #ifdef RELOCATE if (!*s && *buf == 'm' && ngname && ng < nextrcline) strcpy(s,ngname); #endif { char *_s; for (_s=s; isdigit(*_s); _s++) ; if (isspace(*_s)) *_s = '\0'; if (*_s) /* found non-digit before hitting end */ set_ngname(s); else { int rcnum; rcnum = atoi(s); if (rcnum < nextrcline) set_ngname(rcline[rcnum]); else { printf("\nOnly %d groups. Try again.\n", nextrcline) FLUSH; goto reask_newsgroup; } } } /* try to find newsgroup */ #ifdef RELOCATE if (!get_ng(ngname,(*buf=='m'?GNG_RELOC:0) | GNG_FUZZY)) #else if (!get_ng(ngname,GNG_FUZZY)) #endif ng = current_ng;/* if not found, go nowhere */ special = TRUE; /* don't skip it if toread==0 */ break; #ifdef DEBUG case 'D': printf("\nTries: %d Hits: %d\n", softtries,softtries-softmisses) FLUSH; goto reask_newsgroup; #endif case '!': /* shell escape */ if (escapade()) /* do command */ goto reinp_newsgroup; /* if rubbed out, re input */ goto reask_newsgroup; case Ctl('k'): /* edit global KILL file */ edit_kfile(); goto reask_newsgroup; case 'c': /* catch up */ #ifdef CATCHUP if (ng < nextrcline) ask_catchup(); if (toread[ng] == TR_NONE) ng++; #else notincl("c"); #endif break; case 't': if (!use_threads) printf("\n\nNot running in thread mode.\n"); else if (ng < nextrcline && toread[ng] >= TR_NONE) { bool read_unthreaded = (rcchar[ng] == ':'); rcchar[ng] = (read_unthreaded ? '0' : ':'); printf("\n\n%s will be read %sthreaded.\n", rcline[ng], read_unthreaded? "un" : "") FLUSH; set_toread(ng); } special = TRUE; /* don't skip it if toread==0 */ break; case 'u': /* unsubscribe */ if (ng < nextrcline && toread[ng] >= TR_NONE) { /* unsubscribable? */ printf(unsubto,rcline[ng]) FLUSH; rcchar[ng] = NEGCHAR; /* unsubscribe to (from?) it */ toread[ng] = TR_UNSUB; /* and make line invisible */ ng++; /* do an automatic 'n' */ } break; case 'h': { /* help */ int cmd; if ((cmd = help_ng()) > 0) pushchar(cmd); goto reask_newsgroup; } case 'A': if (ng >= nextrcline) break; reask_abandon: #ifdef VERBOSE IF(verbose) in_char("\nAbandon changes to current newsgroup?", 'B', "yn"); ELSE #endif #ifdef TERSE in_char("\nAbandon?", 'B', "ynh"); #endif #ifdef VERIFY printcmd(); #endif putchar('\n') FLUSH; if (*buf == 'h') { #ifdef VERBOSE printf("Type y or SP to abandon the changes to this group since you started trn.\n"); printf("Type n to leave the group as it is.\n"); #else printf("y or SP to abandon changes to this group.\n"); printf("n to forget it.\n"); #endif goto reask_abandon; } else if (*buf != 'y' && *buf != 'n' && *buf != 'q') { printf(hforhelp); settle_down(); goto reask_abandon; } else if (*buf == 'y') abandon_ng(ng); special = TRUE; /* don't skip it if toread==0 */ break; case 'a': #ifndef FINDNEWNG notincl("a"); goto reask_newsgroup; #else /* FALL THROUGH */ #endif case 'o': case 'O': #ifdef ONLY { #ifdef FINDNEWNG bool doscan = (*buf == 'a'); #endif if (!finish_command(TRUE)) /* get rest of command */ goto reinp_newsgroup; /* if rubbed out, try something else */ end_only(); if (buf[1]) { bool minusd = instr(buf+1,"-d", TRUE) != Nullch; sw_list(buf+1); if (minusd) cwd_check(); putchar('\n') FLUSH; #ifdef FINDNEWNG if (doscan && maxngtodo) scanactive(); #endif emptyOnly = (*buf == 'o' && maxngtodo); } ng = 0; /* simulate ^ */ retry = FALSE; break; } #else notincl("o"); goto reask_newsgroup; #endif case '&': if (switcheroo()) /* get rest of command */ goto reinp_newsgroup; /* if rubbed out, try something else */ goto reask_newsgroup; case 'l': { /* list other newsgroups */ if (!finish_command(TRUE)) /* get rest of command */ goto reinp_newsgroup; /* if rubbed out, try something else */ for (s = buf+1; *s == ' '; s++); /* skip leading spaces */ sprintf(cmd_buf,"%s '%s'",filexp(NEWSGROUPS),s); resetty(); if (doshell(sh,cmd_buf)) #ifdef VERBOSE IF(verbose) fputs(" (Error from newsgroups program)\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("(Error)\n",stdout) FLUSH; #endif noecho(); crmode(); goto reask_newsgroup; } case 'U': case '+': case '.': case '=': case 'y': case 'Y': case '\t': /* do normal thing */ if (ng >= nextrcline) { fputs("\nNot on a newsgroup.",stdout) FLUSH; goto reask_newsgroup; } else if (*buf == '.') { /* start command? */ if (!finish_command(FALSE)) /* get rest of command */ goto reinp_newsgroup; s = savestr(buf+1); /* do_newsgroup will free it */ } else { s = buf; if (*buf == '+' || *buf == 'U' || *buf == '=') *s++ = lastchar; /* restore 0200 if from a macro */ save_typeahead(s, sizeof buf - 1); if (*buf) s = savestr(buf); else s = Nullch; } if (toread[ng]) retry = TRUE; switch (do_newsgroup(s)) { case NG_ERROR: case NG_NORM: ng++; break; case NG_ASK: goto reask_newsgroup; case NG_SELPRIOR: *buf = 'p'; goto do_command; case NG_SELNEXT: *buf = 'n'; goto do_command; case NG_MINUS: ng = recent_ng; /* recall previous newsgroup */ special = TRUE; /* don't skip it if toread==0 */ break; } break; #ifdef STRICTCR case '\n': fputs(badcr,stdout) FLUSH; goto reask_newsgroup; #endif case 'v': putchar('\n'); trn_version(); goto reask_newsgroup; default: printf("\n%s",hforhelp) FLUSH; settle_down(); goto reask_newsgroup; } } if (!retry) break; #ifdef USE_NNTP if (actfp && actFetchTime && time(Null(time_t*)) - lastactfetch > actFetchTime) { fclose(actfp); printf("Grabbing new active file..."), fflush(stdout); ngdata_init(); /* re-grab the active file */ carriage_return(); erase_eol(); } #endif } } /* now write .newsrc back out */ write_rc(); if (oh_for_the_good_old_days) get_old_rc(); finalize(0); /* and exit */ } void trn_version() { printf("\nTrn version:%s.\nConfigured for ",patchlevel,rnid); #ifdef USE_NNTP printf("NNTP news access.\n"); #else printf("Local news access.\n"); #endif printf("Send bug reports, suggestions, etc. to: davison@borland.com\n") FLUSH; } /* set current newsgroup */ void set_ngname(what) char *what; { int len = strlen(what)+1; growstr(&ngname,&ngnlen,len); strcpy(ngname,what); growstr(&ngdir,&ngdlen,len); strcpy(ngdir,getngdir(ngname)); } static char *myngdir; static int ngdirlen = 0; char * getngdir(ngnam) char *ngnam; { register char *s; growstr(&myngdir,&ngdirlen,strlen(ngnam)+1); strcpy(myngdir,ngnam); for (s = myngdir; *s; s++) if (*s == '.') *s = '/'; return myngdir; } trn-3.6/nntpclient.h0000664000000000000000000000346705663312263011424 0ustar /* $Id: nntpclient.h,v 3.0 1992/12/14 00:14:55 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifdef USE_NNTP int server_init _((char*)); int nntp_connect _((bool_int)); void nntp_command _((char*)); char nntp_check _((bool_int)); int nntp_gets _((char*, int)); void nntp_close _((bool_int)); #define NNTP_LIST_END(s) ((s)[0]=='.' && ((s)[1]=='\0' || (s)[1]=='\r')) #define nntp_get_a_line(buf,len,realloc) get_a_line(buf,len,realloc,ser_rd_fp) /* RFC 977 defines these, so don't change them */ #define NNTP_CLASS_INF '1' #define NNTP_CLASS_OK '2' #define NNTP_CLASS_CONT '3' #define NNTP_CLASS_ERR '4' #define NNTP_CLASS_FATAL '5' #define NNTP_POSTOK_VAL 200 /* Hello -- you can post */ #define NNTP_NOPOSTOK_VAL 201 /* Hello -- you can't post */ #define NNTP_LIST_FOLLOWS_VAL 215 /* There's a list a-comin' next */ #define NNTP_GOODBYE_VAL 400 /* Have to hang up for some reason */ #define NNTP_NOSUCHGROUP_VAL 411 /* No such newsgroup */ #define NNTP_AUTH_NEEDED_VAL 480 /* Authorization Failed */ #define NNTP_AUTH_REJECT_VAL 482 /* Authorization data rejected */ #define NNTP_BAD_COMMAND_VAL 500 /* Command not recognized */ #define NNTP_SYNTAX_VAL 501 /* Command syntax error */ #define NNTP_ACCESS_VAL 502 /* Access to server denied */ #define NNTP_TMPERR_VAL 503 /* Program fault, command not performed */ #define NNTP_AUTH_BAD_VAL 580 /* Authorization Failed */ #define NNTP_STRLEN 512 EXT FILE *ser_rd_fp INIT(NULL); EXT FILE *ser_wr_fp INIT(NULL); EXT char ser_line[NNTP_STRLEN]; #if defined(NNTP_HANDLE_TIMEOUT) || defined(USE_GENAUTH) EXT char last_command[NNTP_STRLEN]; #endif EXT bool CompliantServer INIT(FALSE); #endif /* USE_NNTP */ trn-3.6/overview.h0000664000000000000000000000337305545455746011125 0ustar /* $Id: overview.h,v 3.0 1992/12/14 00:14:09 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ /* The order of the overview file fields */ #define OV_NUM 0 #define OV_SUBJ 1 #define OV_FROM 2 #define OV_DATE 3 #define OV_MSGID 4 #define OV_REFS 5 #define OV_BYTES 6 #define OV_LINES 7 #define OV_OTHERS 8 /* this catch-all field must be last */ #undef OV_OTHERS_HAS_XREFS /* forces trn to assume xrefs always exist */ /* If ALL the overview files trn will be accessing have xrefs in the OTHERS ** field you may choose to define OV_OTHERS_HAS_XREFS. This may save some ** article accessing during the time that it would take trn to determine ** this for itself. */ /* NOTE that you must NOT define OV_XREFS unless you have opted to create ** a new (non-standard) overview field for xrefs instead of placing them ** in the OTHERS field. If you have included the xref header without its ** prefix into a field of its own, define OV_XREFS with the appropriate ** field number in the list above. If it has the "Xref:" prefix and you ** didn't want to point the OV_OTHERS field at it (for some reason) you ** can define both OV_XREFS and OV_LAX_XREFS and trn accepts a field with ** or without the "Xref:" prefix. */ /*#define OV_XREFS 8 *//* only define when using non-standard .overview */ /*#define OV_LAX_XREFS *//* allow xref field to have a header-prefix */ /* What name to append to the directory name to read an overview file. ** This REQUIRES a leading slash unless you're getting fancy. */ #define OV_FILE_NAME "/.overview" /* How many overview lines to read with one NNTP call */ #define OV_CHUNK_SIZE 40 trn-3.6/rt-process.c0000664000000000000000000003244507041327652011340 0ustar /* $Id: rt-process.c,v 3.0 1992/12/14 00:14:13 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "intrp.h" #include "trn.h" #include "cache.h" #include "bits.h" #include "final.h" #include "ng.h" #include "ngdata.h" #include "rcln.h" #include "util.h" #include "kfile.h" #include "hash.h" #include "rthread.h" #include "rt-select.h" #include "INTERN.h" #include "rt-process.h" extern HASHTABLE *msgid_hash; static char *valid_message_id _((char*, char*)); static void link_child _((ARTICLE*)); static void unlink_child _((ARTICLE*)); /* This depends on art being set to the current article number. */ ARTICLE * allocate_article(artnum) ART_NUM artnum; { register ARTICLE *article; /* create an new article */ if (artnum >= absfirst) { if (artnum > lastart) grow_cache(artnum); article = article_ptr(artnum); } else { article = (ARTICLE *)safemalloc(sizeof (ARTICLE)); bzero((char*)article, sizeof (ARTICLE)); article->flags |= AF_READ|AF_FAKE|AF_MISSING|AF_TMPMEM; } return article; } void fix_msgid(msgid) char *msgid; { register char *cp; if ((cp = index(msgid, '@')) != Nullch) { while (*++cp) { if (isupper(*cp)) { *cp = tolower(*cp); /* lower-case domain portion */ } } } } int msgid_cmp(key, keylen, data) char *key; int keylen; HASHDATUM data; { ARTICLE *article = (data.dat_ptr? (ARTICLE*)data.dat_ptr : article_ptr(data.dat_len)); /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, article->msgid, keylen); } SUBJECT *fake_had_subj; /* the fake-turned-real article had this subject */ bool valid_article(article) ARTICLE *article; { ARTICLE *ap, *fake_ap; char *msgid = article->msgid; HASHDATUM data; if (msgid) { fix_msgid(msgid); data = hashfetch(msgid_hash, msgid, strlen(msgid)); if ((fake_ap = (ARTICLE*)data.dat_ptr) == Nullart) { if (!data.dat_len) { data.dat_len = article_num(article); hashstorelast(data); fake_had_subj = Nullsubj; return TRUE; } if (data.dat_len == article_num(article)) { fake_had_subj = Nullsubj; return TRUE; } } /* Whenever we replace a fake art with a real one, it's a lot of work ** cleaning up the references. Fortunately, this is not often. */ if (fake_ap) { article->parent = fake_ap->parent; article->child1 = fake_ap->child1; article->sibling = fake_ap->sibling; fake_had_subj = fake_ap->subj; if (fake_ap->autofl) { article->autofl |= fake_ap->autofl; localkf_changes = 2; } if (curr_artp == fake_ap) { curr_artp = article; curr_art = article_num(article); } if (recent_artp == fake_ap) { recent_artp = article; recent_art = article_num(article); } if ((ap = article->parent) != Nullart) { if (ap->child1 == fake_ap) ap->child1 = article; else { ap = ap->child1; goto sibling_search; } } else if (fake_had_subj) { register SUBJECT *sp = fake_had_subj; if ((ap = sp->thread) == fake_ap) { do { sp->thread = article; sp = sp->thread_link; } while (sp != fake_had_subj); } else { sibling_search: while (ap->sibling) { if (ap->sibling == fake_ap) { ap->sibling = article; break; } ap = ap->sibling; } } } for (ap = article->child1; ap; ap = ap->sibling) ap->parent = article; clear_article(fake_ap); free((char*)fake_ap); data.dat_ptr = Nullch; data.dat_len = article_num(article); hashstorelast(data); return TRUE; } } /* Forget about the duplicate message-id or bogus article. */ uncache_article(article,TRUE); return FALSE; } /* Take a message-id and see if we already know about it. If so, return ** the article, otherwise create a fake one. */ ARTICLE * get_article(msgid) char *msgid; { register ARTICLE *article; HASHDATUM data; fix_msgid(msgid); data = hashfetch(msgid_hash, msgid, strlen(msgid)); if (!(article = (ARTICLE *)data.dat_ptr)) { if (data.dat_len) article = article_ptr(data.dat_len); else { article = allocate_article(0); data.dat_ptr = (char*)article; article->msgid = savestr(msgid); hashstorelast(data); } } return article; } /* Take all the data we've accumulated about the article and shove it into ** the article tree at the best place we can deduce. */ void thread_article(article) ARTICLE *article; { register ARTICLE *ap, *last; register char *cp, *end; ARTICLE *kill_ap = ((article->autofl & AUTO_KILL)? article : Nullart); int art_chain_autofl = article->autofl | (article->subj->articles? article->subj->articles->autofl : 0); int art_thread_autofl; /* We're definitely not a fake anymore */ article->flags = (article->flags & ~AF_FAKE) | AF_THREADED; /* If the article was already part of an existing thread, unlink it ** to try to put it in the best possible spot. */ if (fake_had_subj) { ARTICLE *stopper; if (fake_had_subj->thread != article->subj->thread) merge_threads(fake_had_subj, article->subj); /* Check for a real or shared-fake parent */ ap = article->parent; while (ap && (ap->flags&AF_FAKE) == AF_FAKE && !ap->child1->sibling) { last = ap; ap = ap->parent; } stopper = ap; unlink_child(article); /* We'll assume that this article has as good or better references ** than the child that faked us initially. Free the fake reference- ** chain and process our references as usual. */ for (ap = article->parent; ap != stopper; ap = last) { unlink_child(ap); last = ap->parent; ap->date = 0; ap->subj = 0; ap->parent = 0; /* don't free it until group exit since we probably re-use it */ } article->parent = Nullart; /* neaten up */ article->sibling = Nullart; } /* If we have references, process them from the right end one at a time ** until we either run into somebody, or we run out of references. */ if (*references) { last = article; ap = Nullart; end = references + strlen(references) - 1; while ((cp = rindex(references, '<')) != Nullch) { while (end >= cp && ((unsigned char)*end <= ' ' || *end == ',')) { end--; } end[1] = '\0'; /* Quit parsing references if this one is garbage. */ if (!(end = valid_message_id(cp, end))) break; /* Dump all domains that end in '.', such as "..." & "1@DEL." */ if (end[-1] == '.') break; ap = get_article(cp); *cp = '\0'; art_chain_autofl |= ap->autofl; if (ap->autofl & AUTO_KILL) kill_ap = ap; /* Check for duplicates on the reference line. Brand-new data has ** no date. Data we just allocated earlier on this line has a ** date but no subj. Special-case the article itself, since it ** does have a subj. */ if ((ap->date && !ap->subj) || ap == article) { if ((ap = last) == article) ap = Nullart; continue; } last->parent = ap; link_child(last); if (ap->subj) break; ap->date = article->date; last = ap; end = cp-1; } if (!ap) goto no_references; /* Check if we ran into anybody that was already linked. If so, we ** just use their thread. */ if (ap->subj) { /* See if this article spans the gap between what we thought ** were two different threads. */ if (article->subj->thread != ap->subj->thread) merge_threads(ap->subj, article->subj); } else { /* We didn't find anybody we knew, so either create a new thread ** or use the article's thread if it was previously faked. */ ap->subj = article->subj; link_child(ap); } /* Set the subj of faked articles we created as references. */ for (ap = article->parent; ap && !ap->subj; ap = ap->parent) ap->subj = article->subj; /* Make sure we didn't circularly link to a child article(!), by ** ensuring that we run off the top before we run into ourself. */ while (ap && ap->parent != article) ap = ap->parent; if (ap) { /* Ugh. Someone's tweaked reference line with an incorrect ** article-order arrived first, and one of our children is ** really one of our ancestors. Cut off the bogus child branch ** right where we are and link it to the thread. */ unlink_child(ap); ap->parent = Nullart; link_child(ap); } } else { no_references: /* The article has no references. Either turn it into a new thread ** or re-attach the fleshed-out article to its old thread. */ link_child(article); } if (!(article->flags & AF_CACHED)) cache_article(article); art_thread_autofl = art_chain_autofl; if (sel_mode == SM_THREAD && article == article->subj->articles) { SUBJECT *sp = article->subj->thread_link; while (sp != article->subj) { if (sp->articles) art_thread_autofl |= sp->articles->autofl; sp = sp->thread_link; } } if (art_thread_autofl & AUTO_SELECTALL) { if (sel_mode == SM_THREAD) select_arts_thread(article, AUTO_SELECTALL); else select_arts_subject(article, AUTO_SELECTALL); } else if (art_chain_autofl & AUTO_SELECT) select_subthread(article, AUTO_SELECT); if (art_thread_autofl & AUTO_KILLALL) { if (sel_mode == SM_THREAD) kill_arts_thread(article, KF_ALL|KF_KILLFILE); else kill_arts_subject(article, KF_ALL|KF_KILLFILE); } else if (kill_ap) kill_subthread(kill_ap, KF_ALL|KF_KILLFILE); } /* Check if the string we've found looks like a valid message-id reference. */ static char * valid_message_id(start, end) register char *start, *end; { char *mid; if (start == end) return 0; if (*end != '>') { /* Compensate for space cadets who include the header in their ** subsitution of all '>'s into another citation character. */ if (*end == '<' || *end == '-' || *end == '!' || *end == '%' || *end == ')' || *end == '|' || *end == ':' || *end == '}' || *end == '*' || *end == '+' || *end == '#' || *end == ']' || *end == '@' || *end == '$') { *end = '>'; } } else if (end[-1] == '>') { *(end--) = '\0'; } /* Id must be "<...@...?" */ if (*start != '<' || *end != '>' || (mid = index(start, '@')) == Nullch || mid == start+1 || mid+1 == end) { return 0; } /* Id must not contain '>' or non-printing characters or space */ for (mid = start+1; mid < end; mid++) if (*mid <= ' ' || *mid == '>') return 0; return end; } /* Remove an article from its parent/siblings. Leave parent pointer intact. */ static void unlink_child(child) register ARTICLE *child; { register ARTICLE *last; if (!(last = child->parent)) { register SUBJECT *sp = child->subj; if ((last = sp->thread) == child) { do { sp->thread = child->sibling; sp = sp->thread_link; } while (sp != child->subj); } else goto sibling_search; } else { if (last->child1 == child) last->child1 = child->sibling; else { last = last->child1; sibling_search: while (last->sibling != child) last = last->sibling; last->sibling = child->sibling; } } } /* Link an article to its parent article. If its parent pointer is zero, ** link it to its thread. Sorts siblings by date. */ static void link_child(child) register ARTICLE *child; { register ARTICLE *ap; if (!(ap = child->parent)) { register SUBJECT *sp = child->subj; ap = sp->thread; if (!ap || child->date < ap->date) { do { sp->thread = child; sp = sp->thread_link; } while (sp != child->subj); child->sibling = ap; } else goto sibling_search; } else { ap = ap->child1; if (!ap || child->date < ap->date) { child->sibling = ap; child->parent->child1 = child; } else { sibling_search: while (ap->sibling && ap->sibling->date <= child->date) ap = ap->sibling; child->sibling = ap->sibling; ap->sibling = child; } } } /* Merge all of s2's thread into s1's thread. */ void merge_threads(s1, s2) SUBJECT *s1, *s2; { register SUBJECT *sp; register ARTICLE *t1, *t2; t1 = s1->thread; t2 = s2->thread; /* Change all of t2's thread pointers to a common lead article */ sp = s2; do { sp->thread = t1; sp = sp->thread_link; } while (sp != s2); /* Join the two circular lists together */ sp = s2->thread_link; s2->thread_link = s1->thread_link; s1->thread_link = sp; /* If thread mode is set, ensure the subjects are adjacent in the list. */ /* Don't do this if the selector is active, because it gets messed up. */ if (sel_mode == SM_THREAD && mode != 't') { for (sp = s2; sp->prev && sp->prev->thread == t1; ) { sp = sp->prev; if (sp == s1) goto artlink; } while (s2->next && s2->next->thread == t1) { s2 = s2->next; if (s2 == s1) goto artlink; } /* Unlink the s2 chunk of subjects from the list */ if (!sp->prev) first_subject = s2->next; else sp->prev->next = s2->next; if (!s2->next) last_subject = sp->prev; else s2->next->prev = sp->prev; /* Link the s2 chunk after s1 */ sp->prev = s1; s2->next = s1->next; if (!s1->next) last_subject = s2; else s1->next->prev = s2; s1->next = sp; } artlink: /* Link each article that was attached to t2 to t1. */ for (t1 = t2; t1; t1 = t2) { t2 = t2->sibling; link_child(t1); /* parent is null, thread is newly set */ } } trn-3.6/util2.c0000664000000000000000000000525105654303443010271 0ustar /* $Id: util2.c,v 3.6 1994/10/28 11:11:11 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "nntp.h" #include "nntpauth.h" #include "util.h" #include "INTERN.h" #include "util2.h" /* return ptr to little string in big string, NULL if not found */ char * instr(big, little, case_matters) char *big, *little; bool_int case_matters; { register char *t, *s, *x; for (t = big; *t; t++) { for (x=t,s=little; *s; x++,s++) { if (!*x) return Nullch; if (case_matters == TRUE) { if(*s != *x) break; } else { register char c,d; if (isupper(*s)) c = tolower(*s); else c = *s; if (isupper(*x)) d = tolower(*x); else d = *x; if ( c != d ) break; } } if (!*s) return t; } return Nullch; } #ifdef SETENV static bool firstexport = TRUE; extern char **environ; void export(nam,val) char *nam, *val; { register int i=envix(nam); /* where does it go? */ if (!environ[i]) { /* does not exist yet */ if (firstexport) { /* need we copy environment? */ int j; #ifndef lint char **tmpenv = (char**) /* point our wand at memory */ safemalloc((MEM_SIZE) (i+2) * sizeof(char*)); #else char **tmpenv = Null(char **); #endif /* lint */ firstexport = FALSE; for (j=0; jSpeller <>Speller <<'!NO!SUBS!' tmpdir="${TMPDIR-/tmp}" # get us some temporary files. hdrs=$tmpdir/sp$$hdr body=$tmpdir/sp$$body sig=$tmpdir/sp$$sig mine=$tmpdir/sp$$mine quoted=$tmpdir/sp$$quoted bad=$tmpdir/sp$$bad Cmdname=`$basename $0` if $test "X$1" = X; then $echo "$Cmdname: insufficent arguments" >&2 $echo "$Cmdname: usage: $Cmdname " >&2 exit 1 fi trap "$rm -f $hdrs $body $body~ $sig $mine $quoted $bad; exit 1" 0 1 2 15 while $test "X$1" != X; do # create the files, so that cat is quiet later.. >$hdrs >$body >$sig >$mine >$quoted # tear the wanted headers out and toss them at body, leaving the # the remainder to be put back in later. $awk 'BEGIN { inhdr = 1; keephdr = 0; insig = 0 } /^$/ { inhdr = 0; print $0 > Body; next; } /^-- $/ { insig = 1; print $0 > Sig; next; } /^Subject: / { if (inhdr) { keephdr = 1; print $0 > Body; next; } } /^Keywords: / { if (inhdr) { keephdr = 1; print $0 > Body } next; } /^Summary: / { if (inhdr) { keephdr = 1; print $0 > Body } next; } /^[ \t]/ { if (keephdr && indhr) { print $0 > Body; } else if (inhdr) { print $0 > Hdrs; } } /^.*: / { if (inhdr) { keephdr = 0; print $0 > Hdrs; next; } } /^.*$/ { if (!inhdr && !insig) { print $0 > Body; next; } if (insig) { print $0 > Sig; next } } ' Body=$body Hdrs=$hdrs Sig=$sig $1 # now rip out the quoted text from the article, so we only # spell check our own pristine prose.. if $test "X$QUOTECHARS" = X ; then $mv $body $mine else $sed -e "/^$QUOTECHARS/s/.*//" $body >$mine $diff -e $mine $body > $quoted fi # ok, we've torn everything asunder, now lets spell check # the guts of the article.. if $test "X$ispell" = "Xnone"; then $spell $ispell_options $mine > $bad if $test -s $bad ; then ($echo ---- misspelled words ------------------------------------- #$cat $bad | fmt $cat $bad | pr -t -4 $echo ----------------------------------------------------------- ) | $pager else $echo 'No misspelled words.' fi else $ispell $ispell_options $mine fi if $test $? -ne 0; then $echo "$Cmdname: error returned, leaving message untouched" # don't want to mess with this file again, either shift continue fi # resurrect the body of the article.. if $test -s $quoted ; then ($cat $quoted; $echo w $body; $echo q) | $ed - $mine else $mv $mine $body fi # ..and re-assemble the article. $cat $hdrs $body $sig >$1 # move to the next filename! shift done !NO!SUBS! $eunicefix Speller chmod 755 Speller trn-3.6/makedir.SH0000664000000000000000000000206705545455742010751 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting makedir (with variable substitutions)" $spitshell >makedir </dev/null 2>&1 done !GROK!THIS! $eunicefix makedir chmod +x makedir trn-3.6/help.h0000664000000000000000000000146505420174072010165 0ustar /* $Id: help.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ void help_init _((void)); int help_ng _((void)); int help_art _((void)); int help_page _((void)); #ifdef ESCSUBS int help_subs _((void)); #endif int help_select _((void)); trn-3.6/intrp.h0000664000000000000000000000301111124144463010355 0ustar /* $Id: intrp.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT char *lib INIT(Nullch); /* news library */ EXT char *rnlib INIT(Nullch); /* private news program library */ EXT char *origdir INIT(Nullch); /* cwd when rn invoked */ EXT char *homedir INIT(Nullch); /* login directory */ EXT char *dotdir INIT(Nullch); /* where . files go */ EXT char *tmpdir INIT(Nullch); /* where tmp files go */ EXT char *loginName INIT(Nullch); /* login id of user */ EXT char *realname INIT(Nullch); /* real name of user */ EXT char *phostname INIT(Nullch); /* host name in a posting */ EXT int perform_cnt; #ifdef NEWS_ADMIN EXT char newsadmin[] INIT(NEWS_ADMIN);/* news administrator */ EXT int newsuid INIT(0); #endif void intrp_init _((char*)); char *filexp _((char*)); char *dointerp _((char*,int,char*,char*)); void interp _((char*,int,char*)); int refscpy _((char*,int,char*)); void reducerefs _((char*,int,char*)); char *getrealname _((long)); trn-3.6/kfile.c0000664000000000000000000002676211124144120010316 0ustar /* $Id: kfile.c,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "term.h" #include "util.h" #include "cache.h" #include "artsrch.h" #include "ng.h" #include "ngdata.h" #include "intrp.h" #include "ngstuff.h" #include "rcstuff.h" #include "trn.h" #include "bits.h" #include "hash.h" #include "rthread.h" #include "rt-process.h" #include "rt-select.h" #include "INTERN.h" #include "kfile.h" extern HASHTABLE *msgid_hash; static bool exitcmds = FALSE; void kfile_init() { ; } #ifndef KILLFILES int edit_kfile() { notincl("^K"); return -1; } #else /* KILLFILES */ char killglobal[] = KILLGLOBAL; char killlocal[] = KILLLOCAL; void mention(str) char *str; { #ifdef VERBOSE IF(verbose) { #ifdef NOFIREWORKS no_sofire(); #endif standout(); fputs(str,stdout); un_standout(); putchar('\n'); } ELSE #endif #ifdef TERSE putchar('.'); #endif fflush(stdout); } static bool kill_mentioned; int do_kfile(kfp,entering) FILE *kfp; int entering; { bool first_time = (entering && !killfirst); ART_UNREAD selections = selected_count; ART_UNREAD unread = toread[ng]; int thread_kill_cnt = 0; int thread_select_cnt = 0; char *cp; art = lastart+1; killfirst = firstart; fseek(kfp,0L,0); /* rewind file */ while (fgets(buf,LBUFLEN,kfp) != Nullch) { if (*(cp = buf + strlen(buf) - 1) == '\n') *cp = '\0'; if (strnEQ(buf,"THRU",4)) { killfirst = atol(buf+4)+1; if (killfirst < absfirst) killfirst = absfirst; if (killfirst > lastart) killfirst = lastart+1; continue; } if (*buf == 'X') { /* exit command? */ if (entering) { exitcmds = TRUE; continue; } strcpy(buf,buf+1); } else if (!entering) continue; if (*buf == '&') { mention(buf); switcheroo(); } else if (*buf == '/') { has_normal_kills = TRUE; if (firstart > lastart) continue; mention(buf); kill_mentioned = TRUE; switch (art_search(buf, (sizeof buf), FALSE)) { case SRCH_ABORT: continue; case SRCH_INTR: #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art) FLUSH; #endif return -1; case SRCH_DONE: break; case SRCH_SUBJDONE: fputs("\tsubject not found (?)\n",stdout) FLUSH; break; case SRCH_NOTFOUND: fputs("\tnot found\n",stdout) FLUSH; break; case SRCH_FOUND: fputs("\tfound\n",stdout) FLUSH; } } else if (first_time && *buf == '<') { register ARTICLE *ap; cp = index(buf,' '); if (!cp) cp = "T,"; else *cp++ = '\0'; if ((ap = get_article(buf)) != Nullart) { if ((ap->flags & AF_FAKE) == AF_FAKE) { if (*cp == 'T') cp++; switch (*cp) { case '+': ap->autofl |= AUTO_SELECTALL; thread_select_cnt++; break; case '.': ap->autofl |= AUTO_SELECT; thread_select_cnt++; break; case 'J': case 'j': ap->autofl |= AUTO_KILLALL; thread_kill_cnt++; break; case ',': ap->autofl |= AUTO_KILL; thread_kill_cnt++; break; } } else { art = article_num(ap); artp = ap; perform(cp,FALSE); if (ap->autofl & (AUTO_SELECT|AUTO_SELECTALL)) thread_select_cnt++; else if (ap->autofl & (AUTO_KILL|AUTO_KILLALL)) thread_kill_cnt++; } } art = lastart+1; } else if (*buf == '*') { register ARTICLE *ap; register int killmask = AF_READ; switch (buf[1]) { case 'X': killmask |= sel_mask; /* don't kill selected articles */ /* FALL THROUGH */ case 'j': for (art = killfirst, ap = article_ptr(killfirst); art <= lastart; art++, ap++ ) { if (!(ap->flags & killmask)) set_read(ap); else if (ap->flags & sel_mask) { ap->flags &= ~sel_mask; if (!selected_count--) selected_count = 0; } } break; } has_normal_kills = TRUE; } } if (thread_kill_cnt) { sprintf(buf,"%ld auto-kill command%s.", (long)thread_kill_cnt, thread_kill_cnt == 1? "" : "s"); mention(buf); kill_mentioned = TRUE; } if (thread_select_cnt) { sprintf(buf,"%ld auto-select command%s.", (long)thread_select_cnt, thread_select_cnt == 1? "" : "s"); mention(buf); kill_mentioned = TRUE; } unread -= toread[ng]; selections -= selected_count; #ifdef VERBOSE IF(verbose && (unread > 0 || selections < 0)) { putchar('\n'); if (unread > 0) { printf("Killed %ld article%s", (long)unread, unread == 1? nullstr : "s"); if (selections < 0) fputs("; ",stdout); } if (selections < 0) printf("Selected %ld article%s", (long)-selections, selections == -1? nullstr : "s"); fputs(".\n",stdout) FLUSH; kill_mentioned = TRUE; } #endif return 0; } void kill_unwanted(starting,message,entering) ART_NUM starting; char *message; int entering; { bool intr = FALSE; /* did we get an interrupt? */ ART_NUM oldfirst; char oldmode = mode; bool anytokill = (toread[ng] > 0); mode = 'k'; if ((entering || exitcmds) && (localkfp || globkfp)) { exitcmds = FALSE; oldfirst = firstart; firstart = starting; clear(); #ifdef VERBOSE # ifdef TERSE if (message && (verbose || entering)) # else if (message) # endif #else if (message && entering) #endif fputs(message,stdout) FLUSH; kill_mentioned = FALSE; if (localkfp) { if (entering) localkf_changes |= 1; intr = do_kfile(localkfp,entering); } if (globkfp && !intr) intr = do_kfile(globkfp,entering); putchar('\n') FLUSH; if (entering && kill_mentioned && novice_delays) #ifdef VERBOSE IF(verbose) get_anything(); ELSE #endif #ifdef TERSE pad(just_a_sec); #endif if (anytokill) /* if there was anything to kill */ forcelast = FALSE; /* allow for having killed it all */ firstart = oldfirst; } if (!entering && localkf_changes && !intr) rewrite_kfile(lastart); mode = oldmode; } static FILE *newkfp; static void write_thread_commands(data, extra) HASHDATUM *data; int extra; { register ARTICLE *ap = (data->dat_ptr? (ARTICLE*)data->dat_ptr : article_ptr(data->dat_len)); register int autofl; char ch; if ((autofl = ap->autofl) != 0) { if (!(ap->flags & AF_MISSING) || ap->child1) { if (autofl & AUTO_KILLALL) ch = 'J'; else if (autofl & AUTO_KILL) ch = ','; else if (autofl & AUTO_SELECTALL) ch = '+'; else if (autofl & AUTO_SELECT) ch = '.'; else ch = '?'; fprintf(newkfp,"%s T%c\n", ap->msgid, ch); } } } void rewrite_kfile(thru) ART_NUM thru; { bool no_kills = 0, has_star_commands = FALSE; char *oldkf, *newkf; if (localkfp) { fseek(localkfp,0L,0); /* rewind current file */ /* If we're writing ids, we know the file is not null */ if (localkf_changes > 1) ; else if (fgets(buf,LBUFLEN,localkfp) != Nullch && (strnNE(buf,"THRU",4) || fgets(buf,LBUFLEN,localkfp) != Nullch)) fseek(localkfp,0L,0); else no_kills = 1; } strcpy(buf,filexp(getval("KILLLOCAL",killlocal))); oldkf = savestr(buf); strcat(buf, ".tmp"); newkf = savestr(buf); if (!localkfp) makedir(newkf,MD_FILE); if (no_kills) { UNLINK(buf); open_kfile(KF_LOCAL); /* close file and reset open flag */ } else if (newkfp = fopen(newkf,"w")) { fprintf(newkfp,"THRU %ld\n",(long)thru); while (localkfp && fgets(buf,LBUFLEN,localkfp) != Nullch) { if (strnEQ(buf,"THRU",4)) continue; /* Write star commands after other kill commands */ if (*buf == '*') { has_star_commands = TRUE; continue; } /* Leave out any outdated thread commands */ if (*buf != 'T' && *buf != '<') fputs(buf,newkfp); } if (has_star_commands) { fseek(localkfp,0L,0); /* rewind file */ while (fgets(buf,LBUFLEN,localkfp) != Nullch) { if (*buf == '*') fputs(buf,newkfp); } } /* Append all the still-valid thread commands */ hashwalk(msgid_hash, write_thread_commands, 0); fclose(newkfp); #ifdef HAS_RENAME rename(newkf, oldkf); #else UNLINK(oldkf); safelink(newkf, oldkf); UNLINK(newkf); #endif open_kfile(KF_LOCAL); /* and reopen local file */ } else printf(cantcreate,newkf) FLUSH; localkf_changes = 0; has_normal_kills = FALSE; free(newkf); free(oldkf); } /* edit KILL file for newsgroup */ int edit_kfile() { int r = -1; if (in_ng) { register SUBJECT *sp; if (localkf_changes) rewrite_kfile(lastart); for (sp = first_subject; sp; sp = sp->next) clear_subject(sp); localkf_changes = 0; strcpy(buf,filexp(getval("KILLLOCAL",killlocal))); } else strcpy(buf,filexp(getval("KILLGLOBAL",killglobal))); if ((r = makedir(buf,MD_FILE)) >= 0) { sprintf(cmd_buf,"%s %s", filexp(getval("VISUAL",getval("EDITOR",defeditor))),buf); printf("\nEditing %s KILL file:\n%s\n", (in_ng?"local":"global"),cmd_buf) FLUSH; resetty(); /* make sure tty is friendly */ r = doshell(sh,cmd_buf);/* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ open_kfile(in_ng); if (localkfp) { fseek(localkfp,0L,0); /* rewind file */ has_normal_kills = FALSE; while (fgets(buf,LBUFLEN,localkfp) != Nullch) { if (*buf == '/' || *buf == '*') has_normal_kills = TRUE; else if (*buf == '<') { register ARTICLE *ap; char *cp = index(buf,' '); if (!cp) cp = ","; else *cp++ = '\0'; if ((ap = get_article(buf)) != Nullart) { if (*cp == 'T') cp++; switch (*cp) { case '+': ap->autofl |= AUTO_SELECTALL; break; case '.': ap->autofl |= AUTO_SELECT; break; case 'J': case 'j': ap->autofl |= AUTO_KILLALL; break; case ',': ap->autofl |= AUTO_KILL; break; } } } } } } else printf("Can't make %s\n",buf) FLUSH; return r; } void open_kfile(local) int local; { char *kname = filexp(local ? getval("KILLLOCAL",killlocal) : getval("KILLGLOBAL",killglobal) ); /* delete the file if it is empty */ if (stat(kname,&filestat) >= 0 && !filestat.st_size) UNLINK(kname); if (local) { if (localkfp) fclose(localkfp); localkfp = fopen(kname,"r"); } else { if (globkfp) fclose(globkfp); globkfp = fopen(kname,"r"); } } void kf_append(cmd) char *cmd; { strcpy(cmd_buf,filexp(getval("KILLLOCAL",killlocal))); if (makedir(cmd_buf,MD_FILE) >= 0) { #ifdef VERBOSE IF(verbose) printf("\nDepositing command in %s...",cmd_buf); ELSE #endif #ifdef TERSE printf("\n--> %s...",cmd_buf); #endif fflush(stdout); if (novice_delays) sleep(2); if ((tmpfp = fopen(cmd_buf,"a+")) != Nullfp) { char ch; if (fseek(tmpfp,-1L,2) < 0) ch = '\n'; else ch = getc(tmpfp); fseek(tmpfp,0L,2); if (ch != '\n') putc('\n', tmpfp); fprintf(tmpfp,"%s\n",cmd); fclose(tmpfp); if (!localkfp) open_kfile(KF_LOCAL); fputs("done\n",stdout) FLUSH; } else printf(cantopen,cmd_buf) FLUSH; } has_normal_kills = TRUE; } #endif /* KILLFILES */ trn-3.6/decode.h0000664000000000000000000000110205420174064010445 0ustar /* $Id: decode.h,v 3.0 1991/11/22 04:12:25 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT FILE *decode_fp INIT(NULL); EXT char decode_fname[MAXFILENAME]; EXT char decode_dest[MAXFILENAME]; EXT int decode_type; void decode_init _((void)); void decode_end _((void)); void uud_start _((void)); int uudecode _((FILE*)); void unship_init _((void)); int unship _((FILE*)); #define UUDECODE 0 #define UNSHIP 1 trn-3.6/head.c0000664000000000000000000002503205653332107010130 0ustar /* $Id: head.c,v 3.0 1992/02/23 21:25:39 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "artio.h" #include "cache.h" #include "ng.h" #include "ngdata.h" #include "util.h" #include "hash.h" #include "rthread.h" #include "rt-process.h" #include "rt-util.h" #include "final.h" #include "nntp.h" #include "INTERN.h" #include "head.h" bool first_one; /* is this the 1st occurance of this header line? */ static char htypeix[26] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; void head_init() { register int i; for (i=HEAD_FIRST+1; i LONGKEY+2) return SOME_LINE; for (t=lc,f=bufptr; f= 'a' && *f <= 'z') { for (i = htypeix[*f - 'a']; *htype[i].ht_name == *f; --i) { if (len == htype[i].ht_length && strEQ(f, htype[i].ht_name)) { return i; } } } return SOME_LINE; } void start_header(artnum) ART_NUM artnum; { register int i; #ifdef DEBUG if (debug & DEB_HEADER) dumpheader("start_header\n"); #endif for (i=0; idate) ap->date = parsedate(art_buf+6); } } #ifdef DEBUG if (debug & DEB_HEADER) dumpheader(art_buf); #endif if (htype[in_header].ht_flags & HT_HIDE) return newhide; } } return FALSE; /* don't hide this line */ } void end_header() { register ARTICLE *ap = article_ptr(parsed_art); end_header_line(); in_header = PAST_HEADER; /* just to be sure */ if (!ap->subj) set_subj_line(ap,"",6); #ifndef DBM_XREFS if (!ap->xrefs) ap->xrefs = nullstr; #endif #ifdef USE_NNTP htype[PAST_HEADER].ht_minpos = artpos+1; /* remember where body starts */ #else htype[PAST_HEADER].ht_minpos = tellart(); #endif if (ThreadedGroup && !(ap->flags & AF_THREADED)) { if (valid_article(ap)) { ARTICLE *artp_hold = artp; references = fetchlines(parsed_art, REFS_LINE); thread_article(ap); free(references); artp = artp_hold; check_poster(ap); } } else if (!(ap->flags & AF_CACHED)) { cache_article(ap); check_poster(ap); } } /* read the header into memory and parse it if we haven't already */ bool parseheader(artnum) ART_NUM artnum; { register char *bp; register int len; bool had_nl = TRUE; bool found_nl; if (parsed_art == artnum) return TRUE; if (artnum > lastart) return FALSE; spin(20); #ifdef USE_NNTP if (!nntp_header(artnum)) { uncache_article(article_ptr(artnum),FALSE); return FALSE; } #else if (!artopen(artnum)) return FALSE; #endif start_header(artnum); artpos = 0; bp = headbuf; while (in_header) { if (headbuf_size < artpos + LBUFLEN) { len = bp - headbuf; headbuf_size += LBUFLEN * 4; headbuf = saferealloc(headbuf,headbuf_size); bp = headbuf + len; } #ifdef USE_NNTP found_nl = nntp_gets(bp,LBUFLEN); if (had_nl && *bp == '.') { if (!bp[1]) { *bp++ = '\n'; /* tag the end with an empty line */ break; } strcpy(bp,bp+1); } len = strlen(bp); if (found_nl) bp[len++] = '\n'; bp[len] = '\0'; #else if (readart(bp,LBUFLEN) == Nullch) break; len = strlen(bp); found_nl = (bp[len-1] == '\n'); #endif if (had_nl) parseline(bp,FALSE,FALSE); had_nl = found_nl; artpos += len; bp += len; } *bp = '\0'; /* this probably isn't needed */ end_header(); return TRUE; } /* get a header line from an article */ char * fetchlines(artnum,which_line) ART_NUM artnum; /* article to get line from */ int which_line; /* type of line desired */ { char *s, *t; register ART_POS firstpos; register ART_POS lastpos; int size; /* Only return a cached line if it isn't the current article */ if (parsed_art != artnum) { s = fetchcache(artnum,which_line,FILL_CACHE); if (s) return savestr(s); } if ((firstpos = htype[which_line].ht_minpos) < 0) return savestr(nullstr); firstpos += htype[which_line].ht_length + 1; lastpos = htype[which_line].ht_maxpos; size = lastpos - firstpos; t = headbuf + firstpos; while (*t == ' ' || *t == '\t') t++, size--; #ifdef DEBUG if (debug && (size < 1 || size > 1000)) { printf("Firstpos = %ld, lastpos = %ld\n",(long)firstpos,(long)lastpos); fgets(cmd_buf, sizeof cmd_buf, stdin); } #endif s = safemalloc((MEM_SIZE)size); *s = '\0'; safecat(s,t,size); return s; } /* prefetch a header line from one or more articles */ char * prefetchlines(artnum,which_line,copy) ART_NUM artnum; /* article to get line from */ int which_line; /* type of line desired */ bool_int copy; /* do you want it savestr()ed? */ { char *s, *t; register ART_POS firstpos; register ART_POS lastpos; int size; #ifdef USE_NNTP if (parsed_art != artnum) { ARTICLE *ap; int size; register ART_NUM num, priornum, lastnum; bool cached; s = fetchcache(artnum,which_line,DONT_FILL_CACHE); if (s) { if (copy) s = savestr(s); return s; } spin(20); if (copy) s = safemalloc((MEM_SIZE)(size = LBUFLEN)); else { s = cmd_buf; size = sizeof cmd_buf; } *s = '\0'; priornum = artnum-1; if ((cached = (htype[which_line].ht_flags & HT_CACHED)) != 0) { lastnum = artnum + PREFETCH_SIZE - 1; if (lastnum > lastart) lastnum = lastart; sprintf(ser_line,"XHDR %s %ld-%ld",htype[which_line].ht_name, artnum,lastnum); } else { lastnum = artnum; sprintf(ser_line,"XHDR %s %ld",htype[which_line].ht_name,artnum); } nntp_command(ser_line); if (nntp_check(TRUE) == NNTP_CLASS_OK) { char *line, *last_buf = ser_line; MEM_SIZE last_buflen = sizeof ser_line; for (ap = find_article(artnum); ; ) { line = nntp_get_a_line(last_buf,last_buflen,last_buf!=ser_line); # ifdef DEBUG if (debug & DEB_NNTP) printf("<%s", line) FLUSH; # endif if (NNTP_LIST_END(line)) break; last_buf = line; last_buflen = buflen_last_line_got; if ((t = index(line, '\r')) != Nullch) *t = '\0'; if (!(t = index(line, ' '))) continue; t++; num = atol(line); if (num < artnum || num > lastnum) continue; while (++priornum < num) uncache_article(ap++,FALSE); if (which_line == SUBJ_LINE) set_subj_line(ap++, t, strlen(t)); else if (cached) set_cached_line(ap++, which_line, savestr(t)); if (num == artnum) safecat(s,t,size); } if (last_buf != ser_line) free(last_buf); } else { fprintf(stderr,"\nUnexpected close of server socket.\n"); finalize(1); } while (priornum++ < lastnum) uncache_article(ap++,FALSE); if (copy) s = saferealloc(s, (MEM_SIZE)strlen(s)+1); return s; } #endif /* USE_NNTP */ /* Only return a cached line if it isn't the current article */ s = Nullch; if (parsed_art != artnum) s = fetchcache(artnum,which_line,FILL_CACHE); if ((firstpos = htype[which_line].ht_minpos) < 0) s = nullstr; if (s) { if (copy) s = savestr(s); return s; } firstpos += htype[which_line].ht_length + 1; lastpos = htype[which_line].ht_maxpos; size = lastpos - firstpos; t = headbuf + firstpos; while (*t == ' ' || *t == '\t') t++, size--; if (copy) s = safemalloc((MEM_SIZE)size); else { /* hope this is okay--we're */ s = cmd_buf; /* really scraping for space here */ if (size > sizeof cmd_buf) size = sizeof cmd_buf; } *s = '\0'; safecat(s,t,size); return s; } trn-3.6/cache.c0000664000000000000000000005137407041327652010304 0ustar /* $Id: cache.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "INTERN.h" #include "cache.h" #include "EXTERN.h" #include "intrp.h" #include "search.h" #include "ng.h" #include "trn.h" #include "ngdata.h" #include "term.h" #include "final.h" #include "artsrch.h" #include "head.h" #include "bits.h" #include "rcstuff.h" #include "hash.h" #include "nntp.h" #include "rthread.h" #include "rt-ov.h" #include "rt-page.h" #include "rt-process.h" #include "rt-select.h" #include "rt-util.h" #include "util.h" #include "util2.h" #ifdef PENDING # ifdef ARTSEARCH COMPEX srchcompex; /* compiled regex for searchahead */ # endif #endif HASHTABLE *subj_hash = 0; HASHTABLE *shortsubj_hash = 0; int subject_cmp _((char *,int,HASHDATUM)); void cache_init() { #ifdef PENDING # ifdef ARTSEARCH init_compex(&srchcompex) # endif #endif ; } NG_NUM cached_ng = -1; time_t cached_time = 0; ART_NUM cached_cnt = 0; ART_NUM cached_absfirst = 0; /* We keep track of whether the cache of the group was made with ThreadedGroup * set, because if it wasn't then there is no threading information. * If we don't flush the cache on a change of ThreadedGroup, then trn tries * to access the threading information when it's invalid, causing errors * and occasionally segfaults. Possibly we could avoid having to completely * flushing the cache in this situation, but I don't think it will occur * very often, and this fix should definitely work, whereas a more complex * solution has more chance of being buggy [esp. since I don't actually * know the trn code that well :->] * -- Peter Maydell , 8/9/1999 */ bool cached_threaded = 0; void build_cache() { if (cached_ng == ng && cached_absfirst == absfirst && cached_threaded == ThreadedGroup && time((time_t*)NULL) < cached_time + 6*60*60L) { grow_cache(lastart); rc_to_bits(); if (sel_mode == SM_ARTICLE) set_selector(sel_mode, sel_artsort); else set_selector(sel_threadmode, sel_threadsort); thread_grow(); return; } close_cache(); cached_ng = ng; cached_threaded = ThreadedGroup; cached_absfirst = absfirst; cached_time = time((time_t*)NULL); cached_cnt = lastart-absfirst+2 + 10; article_list = (ARTICLE*) safemalloc((MEM_SIZE)(cached_cnt * sizeof (ARTICLE))); bzero((char*)article_list, cached_cnt * sizeof (ARTICLE)); subj_hash = hashcreate(201, subject_cmp); /*TODO: pick a better size */ rc_to_bits(); /* sets firstart */ first_cached = thread_always? absfirst : firstart; last_cached = first_cached-1; cached_all_in_range = FALSE; #ifdef PENDING subj_to_get = xref_to_get = firstart; #endif #ifndef USE_NNTP setmissingbits(); #endif /* Cache as much data in advance as possible, possibly threading ** articles as we go. */ thread_open(); } #define FIXPTR(p) if ((p) && (p) >= oldlist_start && (p) <= oldlist_end)\ (p) = (article_list + ((p) - oldlist_start)); else void grow_cache(newlast) ART_NUM newlast; { ART_NUM new_cnt = newlast-absfirst+2; if (new_cnt > cached_cnt) { ARTICLE *oldlist_start = article_list; ARTICLE *oldlist_end = oldlist_start + cached_cnt; new_cnt += 10; article_list = (ARTICLE*)saferealloc((char*)article_list, (MEM_SIZE)(new_cnt * sizeof (ARTICLE))); bzero((char*)(article_list+cached_cnt), (new_cnt-cached_cnt) * sizeof (ARTICLE)); if (article_list != oldlist_start) { register ARTICLE *ap; register SUBJECT *sp; for (sp = first_subject; sp; sp = sp->next) { FIXPTR(sp->thread); FIXPTR(sp->articles); if (sp->thread) { for (ap = sp->thread; ap; ap = bump_art(ap)) { FIXPTR(ap->child1); FIXPTR(ap->parent); FIXPTR(ap->sibling); FIXPTR(ap->subj_next); } } else { for (ap = sp->articles; ap; ) { FIXPTR(ap->subj_next); ap = ap->subj_next; } } } FIXPTR(artp); FIXPTR(curr_artp); FIXPTR(recent_artp); } cached_cnt = new_cnt; } cached_time = time((time_t*)NULL); } void close_cache() { SUBJECT *sp, *next; ARTICLE *ap; ART_NUM i; if (subj_hash) { hashdestroy(subj_hash); subj_hash = 0; } if (shortsubj_hash) { hashdestroy(shortsubj_hash); shortsubj_hash = 0; } /* Free all the subjects. */ for (sp = first_subject; sp; sp = next) { next = sp->next; free(sp->str); free((char*)sp); } first_subject = last_subject = Nullsubj; subject_count = 0; /* just to be sure */ parsed_art = 0; if (artptr_list) { free((char*)artptr_list); artptr_list = Null(ARTICLE**); } artptr = Null(ARTICLE**); thread_close(); if (cached_cnt) { for (i = 0, ap = article_list; i < cached_cnt; i++, ap++) clear_article(ap); free((char*)article_list); cached_cnt = 0; } cached_ng = -1; } /* The article has all it's data in place, so add it to the list of articles ** with the same subject. */ void cache_article(ap) register ARTICLE *ap; { register ARTICLE *next, *ap2; if (!(next = ap->subj->articles) || ap->date < next->date) ap->subj->articles = ap; else { while ((next = (ap2 = next)->subj_next) && next->date <= ap->date) ; ap2->subj_next = ap; } ap->subj_next = next; ap->flags |= AF_CACHED; if (!(ap->flags & AF_READ) ^ sel_rereading) { if (ap->subj->flags & SF_WASSELECTED) select_article(ap, 0); else ap->subj->flags |= SF_VISIT; } if (join_subject_len != 0) check_for_near_subj(ap); } void check_for_near_subj(ap) ARTICLE *ap; { register SUBJECT *sp; if (!shortsubj_hash) { shortsubj_hash = hashcreate(201, subject_cmp); /*TODO: pick a better size */ sp = first_subject; } else { sp = ap->subj; if (sp->next) sp = 0; } while (sp) { if (strlen(sp->str+4) >= join_subject_len && sp->thread) { SUBJECT *sp2; HASHDATUM data; data = hashfetch(shortsubj_hash, sp->str+4, join_subject_len); if (!(sp2 = (SUBJECT*)data.dat_ptr)) { data.dat_ptr = (char*)sp; hashstorelast(data); } else if (sp->thread != sp2->thread) { merge_threads(sp2, sp); } } sp = sp->next; } } void change_join_subject_len(len) int len; { if (join_subject_len != len) { if (shortsubj_hash) { hashdestroy(shortsubj_hash); shortsubj_hash = 0; } join_subject_len = len; if (len && first_subject && first_subject->articles) check_for_near_subj(first_subject->articles); } } void check_poster(ap) register ARTICLE *ap; { if (auto_select_postings && !(ap->flags & AF_MISSING) && ap->from) { if (ap->flags & AF_FROMTRUNCED) { strcpy(cmd_buf,realname); if (strEQ(ap->from,compress_name(cmd_buf,16))) { untrim_cache = TRUE; fetchfrom(article_num(ap),FALSE); untrim_cache = FALSE; } } if (!(ap->flags & AF_FROMTRUNCED)) { char *s = cmd_buf, *u, *h; strcpy(s,ap->from); if ((h=index(s,'<')) != Nullch) { /* grab the good part */ s = h+1; if ((h=index(s,'>')) != Nullch) *h = '\0'; } else if ((h=index(s,'(')) != Nullch) { while (h-- != s && *h == ' ') ; h[1] = '\0'; /* or strip the comment */ } if ((h=index(s,'%'))!=Nullch || (h=index(s,'@'))!=Nullch) { *h++ = '\0'; u = s; } else if ((u=rindex(s,'!')) != Nullch) { *u++ = '\0'; h = s; } else h = u = s; if (strEQ(u,loginName)) { if (instr(h,phostname,FALSE)) { switch (auto_select_postings) { case '.': select_subthread(ap,AUTO_SELECT); break; case '+': select_arts_thread(ap,AUTO_SELECT); break; case 'p': if (ap->parent) select_subthread(ap->parent,AUTO_SELECT); else select_subthread(ap,AUTO_SELECT); break; } } else { #ifdef REPLYTO_POSTER_CHECKING char *reply_buf = fetchlines(article_num(ap),REPLY_LINE); if (instr(reply_buf,loginName,TRUE)) select_subthread(ap,AUTO_SELECT); free(reply_buf); #endif } } } } } /* The article turned out to be a duplicate, so remove it from the cached ** list and possibly destroy the subject (should only happen if the data ** was corrupt and the duplicate id got a different subject). */ void uncache_article(ap, remove_empties) register ARTICLE *ap; bool_int remove_empties; { register ARTICLE *next; if (ap->subj) { if ((ap->flags & (AF_CACHED|AF_MISSING)) == AF_CACHED) { if ((next = ap->subj->articles) == ap) ap->subj->articles = ap->subj_next; else { register ARTICLE *ap2; while (next) { if ((ap2 = next->subj_next) == ap) { next->subj_next = ap->subj_next; break; } next = ap2; } } } if (remove_empties && !ap->subj->articles) { register SUBJECT *sp = ap->subj; if (sp == first_subject) first_subject = sp->next; else sp->prev->next = sp->next; if (sp == last_subject) last_subject = sp->prev; else sp->next->prev = sp->prev; hashdelete(subj_hash, sp->str+4, strlen(sp->str+4)); free((char*)sp); ap->subj = Nullsubj; subject_count--; } } onemissing(ap); } /* get the header line from an article's cache or parse the article trying */ char * fetchcache(artnum,which_line,fill_cache) ART_NUM artnum; int which_line; bool_int fill_cache; { register char *s; register ARTICLE *ap; register bool cached = (htype[which_line].ht_flags & HT_CACHED); /* find_article() returns a Nullart if the artnum value is invalid */ if (!(ap = find_article(artnum)) || (ap->flags & AF_MISSING)) return nullstr; if (cached && (s=get_cached_line(ap,which_line,untrim_cache)) != Nullch) return s; if (!fill_cache) return Nullch; if (!parseheader(artnum)) return nullstr; if (cached) return get_cached_line(ap,which_line,untrim_cache); return Nullch; } /* Return a pointer to a cached header line for the indicated article. ** Truncated headers (e.g. from a .thread file) are optionally ignored. */ char * get_cached_line(ap, which_line, no_truncs) register ARTICLE *ap; int which_line; bool_int no_truncs; { register char *s; switch (which_line) { case SUBJ_LINE: if (!ap->subj || (no_truncs && (ap->subj->flags & SF_SUBJTRUNCED))) s = Nullch; else s = ap->subj->str + ((ap->flags & AF_HAS_RE) ? 0 : 4); break; case FROM_LINE: if (no_truncs && (ap->flags & AF_FROMTRUNCED)) s = Nullch; else s = ap->from; break; #ifdef DBM_XREFS case NGS_LINE: #else case XREF_LINE: #endif s = ap->xrefs; break; case MESSID_LINE: s = ap->msgid; break; default: s = Nullch; break; } return s; } void set_subj_line(ap, subj, size) register ARTICLE *ap; register char *subj; /* not yet allocated, so we can tweak it first */ register int size; { HASHDATUM data; SUBJECT *sp; char *newsubj, *subj_start; char *t, *f; int i; while (*subj && *subj != '\n' && (unsigned char)*subj <= ' ') subj++; if (subj != (subj_start = get_subject_start(subj))) { if ((size -= subj_start - subj) < 0) size = 0; ap->flags |= AF_HAS_RE; } if (ap->subj && strnEQ(ap->subj->str+4, subj_start, size)) return; newsubj = safemalloc(size + 4 + 1); strcpy(newsubj, "Re: "); for (t = newsubj + 4, f = subj_start, i = size; i--; ) { if ((unsigned char)*f <= ' ') { while (i && ((unsigned char)*++f <= ' ')) i--, size--; *t++ = ' '; } else if (*f != '\n') *t++ = *f++; else f++; } while (size > 4 && t[-1] == ' ') t--, size--; *t = '\0'; if (ap->subj) { /* This only happens when we freshen truncated subjects */ hashdelete(subj_hash, ap->subj->str+4, strlen(ap->subj->str+4)); free(ap->subj->str); ap->subj->str = newsubj; data.dat_ptr = (char*)ap->subj; hashstore(subj_hash, newsubj + 4, size, data); } else { data = hashfetch(subj_hash, newsubj + 4, size); if (!(sp = (SUBJECT*)data.dat_ptr)) { sp = (SUBJECT*)safemalloc(sizeof (SUBJECT)); bzero((char*)sp, sizeof (SUBJECT)); subject_count++; if ((sp->prev = last_subject) != NULL) sp->prev->next = sp; else first_subject = sp; last_subject = sp; sp->str = newsubj; sp->thread_link = sp; sp->flags = 0; data.dat_ptr = (char*)sp; hashstorelast(data); } else free(newsubj); ap->subj = sp; } } void set_cached_line(ap, which_line, s) register ARTICLE *ap; register int which_line; register char *s; /* already allocated, ready to save */ { char *cp; /* SUBJ_LINE is handled specially above */ switch (which_line) { case FROM_LINE: ap->flags &= ~AF_FROMTRUNCED; if (ap->from) free(ap->from); ap->from = s; break; #ifdef DBM_XREFS case NGS_LINE: if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); if (!index(s, ',')) { /* if no comma, no Xref! */ free(s); s = nullstr; } ap->xrefs = s; break; #else case XREF_LINE: if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); /* Exclude an xref for just this group or "(none)". */ cp = index(s, ':'); if (!cp || !index(cp+1, ':')) { free(s); s = nullstr; } ap->xrefs = s; break; #endif case MESSID_LINE: if (ap->msgid) free(ap->msgid); ap->msgid = s; break; } } int subject_cmp(key, keylen, data) char *key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, ((SUBJECT*)data.dat_ptr)->str+4, keylen); } /* see what we can do while they are reading */ #ifdef PENDING void look_ahead() { #ifdef ARTSEARCH register char *h, *s; #ifdef DEBUG if (debug && srchahead) { printf("(%ld)",(long)srchahead); fflush(stdout); } #endif #endif if (ThreadedGroup) { artp = curr_artp; inc_art(selected_only,FALSE); if (artp) parseheader(art); } else #ifdef ARTSEARCH if (srchahead && srchahead < art) { /* in ^N mode? */ char *pattern; pattern = buf+1; strcpy(pattern,": *"); h = pattern + strlen(pattern); interp(h,(sizeof buf) - (h-buf),"%\\s"); { /* compensate for notesfiles */ register int i; for (i = 24; *h && i--; h++) if (*h == '\\') h++; *h = '\0'; } #ifdef DEBUG if (debug & DEB_SEARCH_AHEAD) { fputs("(hit CR)",stdout); fflush(stdout); fgets(buf+128, sizeof buf-128, stdin); printf("\npattern = %s\n",pattern); } #endif if ((s = compile(&srchcompex,pattern,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; srchahead = 0; } if (srchahead) { srchahead = art; for (;;) { srchahead++; /* go forward one article */ if (srchahead > lastart) { /* out of articles? */ #ifdef DEBUG if (debug) fputs("(not found)",stdout); #endif break; } if (!was_read(srchahead) && wanted(&srchcompex,srchahead,0)) { /* does the shoe fit? */ #ifdef DEBUG if (debug) printf("(%ld)",(long)srchahead); #endif parseheader(srchahead); break; } if (input_pending()) break; } fflush(stdout); } } else #endif /* ARTSEARCH */ { if (art+1 <= lastart) /* how about a pre-fetch? */ parseheader(art+1); /* look for the next article */ } } #endif /* PENDING */ /* see what else we can do while they are reading */ void cache_until_key() { #ifdef PENDING if (!in_ng || input_pending()) return; untrim_cache = TRUE; sentinel_artp = curr_artp; #ifdef USE_NNTP if (nntp_finishbody(FB_BACKGROUND)) return; #endif /* Prioritize our caching based on what mode we're in */ if (mode == 't') { if (cache_subjects()) if (cache_xrefs()) if (chase_xrefs(TRUE)) if (ThreadedGroup) cache_all_arts(); else cache_unread_arts(); } else { if (!ThreadedGroup || cache_all_arts()) if (cache_subjects()) if (cache_unread_arts()) if (cache_xrefs()) chase_xrefs(TRUE); } setspin(SPIN_OFF); untrim_cache = FALSE; #endif } #ifdef PENDING bool cache_subjects() { register ARTICLE *ap; register ART_NUM an; if (subj_to_get > lastart) return TRUE; setspin(SPIN_BACKGROUND); for (an = subj_to_get, ap = article_ptr(an); an <= lastart; ap++, an++) { if (input_pending()) break; if (!(ap->flags & AF_READ)) fetchsubj(an,FALSE); } subj_to_get = an; return subj_to_get > lastart; } bool cache_xrefs() { register ARTICLE *ap; register ART_NUM an; if (olden_days || xref_to_get > lastart) return TRUE; setspin(SPIN_BACKGROUND); for (an = xref_to_get, ap = article_ptr(an); an <= lastart; ap++, an++) { if (input_pending()) break; if (!(ap->flags & AF_READ)) fetchxref(an,FALSE); } xref_to_get = an; return xref_to_get > lastart; } bool cache_all_arts() { int old_last_cached = last_cached; if (!cached_all_in_range) last_cached = first_cached-1; if (last_cached >= lastart && first_cached <= absfirst) return TRUE; /* turn it on as late as possible to avoid fseek()ing openart */ setspin(SPIN_BACKGROUND); if (last_cached < lastart) { if (ov_opened) ov_data(last_cached+1, lastart, TRUE); if (!art_data(last_cached+1, lastart, TRUE, TRUE)) { last_cached = old_last_cached; return FALSE; } cached_all_in_range = TRUE; } if (first_cached > absfirst) { if (ov_opened) ov_data(absfirst, first_cached-1, TRUE); else art_data(absfirst, first_cached-1, TRUE, TRUE); /* If we got interrupted, make a quick exit */ if (first_cached > absfirst) { last_cached = old_last_cached; return FALSE; } } /* We're all done threading the group, so if the current article is ** still in doubt, tell them it's missing. */ if (curr_artp && !(curr_artp->flags & AF_CACHED) && !input_pending()) pushchar('\f' | 0200); /* A completely empty group needs a count & a sort */ if (mode != 't' && !article_count && !selected_only) thread_grow(); return TRUE; } bool cache_unread_arts() { if (last_cached >= lastart) return TRUE; setspin(SPIN_BACKGROUND); return art_data(last_cached+1, lastart, TRUE, FALSE); } #endif bool art_data(first, last, cheating, all_articles) ART_NUM first, last; bool_int cheating; bool_int all_articles; { register ARTICLE *ap; register ART_NUM i; int cachemask = (ThreadedGroup ? AF_THREADED : AF_CACHED) + (all_articles? 0 : AF_READ); setspin(cheating? SPIN_BACKGROUND : SPIN_FOREGROUND); assert(first >= absfirst && last <= lastart); for (i = first, ap = article_ptr(first); i <= last; i++, ap++) { if (ap->flags & cachemask) continue; if (int_count) { int_count = 0; break; } if (cheating) { if (input_pending()) break; /* If the current article is no longer a '?', let them know. */ if (curr_artp != sentinel_artp) { pushchar('\f' | 0200); break; } } /* This parses the header which will cache/thread the article */ (void) parseheader(i); } setspin(SPIN_POP); if (--i > last_cached) last_cached = i; if (i == last) { if (first < first_cached) first_cached = first; return TRUE; } return FALSE; } bool cache_range(first,last) ART_NUM first; ART_NUM last; { bool success = TRUE; bool all_arts = (sel_rereading || thread_always); ART_NUM count = 0; if (sel_rereading && !cached_all_in_range) { first_cached = first; last_cached = first-1; } if (first < first_cached) count = first_cached-first; if (last > last_cached) count += last-last_cached; if (!count) return TRUE; if (first_cached > last_cached) { if (sel_rereading) { if (first_subject) count -= toread[ng]; } else if (first == firstart && last == lastart && !all_arts) count = toread[ng]; } printf("\n%sing %ld article%s.", ThreadedGroup? "Thread" : "Cach", (long)count, count==1? nullstr : "s") FLUSH; setspin(SPIN_FOREGROUND); if (first < first_cached) { if (ov_opened) { ov_data(absfirst,first_cached-1,FALSE); if ((success = (first_cached == absfirst)) != FALSE) ov_close(); } else { success = art_data(first, first_cached-1, FALSE, all_arts); cached_all_in_range = (all_arts && success); } } if (success && last_cached < last) { if (ov_opened) ov_data(last_cached+1, last, FALSE); success = art_data(last_cached+1, last, FALSE, all_arts); cached_all_in_range = (all_arts && success); } setspin(SPIN_POP); return success; } void clear_article(ap) register ARTICLE *ap; { if (ap->from) free(ap->from); if (ap->msgid) free(ap->msgid); if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); } trn-3.6/init.h0000664000000000000000000000143105420174073010172 0ustar /* $Id: init.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT char *lockname INIT(nullstr); EXT long our_pid; bool initialize _((int,char**)); void lock_check _((void)); void newsnews_check _((void)); trn-3.6/Policy.sh.SH0000664000000000000000000001126405626515162011175 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting Policy.sh (with variable substitutions)" $spitshell <Policy.sh $startsh # # This file was produced by running the Polich.sh.SH script, which # gets its values from config.sh, which is generally produced by # running Configure. # # $Id: Policy.sh.SH,v 3.5 1994/04/24 16:47:38 davison Trn $ # login name of the person who configured trn (not particularly interesting). cf_by='$cf_by' # time of configuration (not particularly interesting). cf_time='$cf_time' # install directives. # bin directories (string values) # name of the final resting place bin='$bin' # how to get to the final resting place (thank you, AFS) installbin='$installbin' # private libraries # name of the final resting place for those items in the library # directory (string value) privlib='$privlib' # How to get to the library final resting place (thanks, AFS) installprivlib='$installprivlib' # interesting questions about man # where do man page sources go? mansrc='$mansrc' # what extention do man pages get? manext='$manext' # path to assorted programs that we might want to override. # name of the default editor. (string value) defeditor='$defeditor' # prefered user shell (string value) prefshell='$prefshell' # favorite local pager (string value) pager='$pager' # where is inews? (string value) inewsloc='$inewsloc' # path to interactive speller or "none" (string value) ispell_prg='$ispell_prg' # spelling options for ispell_prg or "spell" if "none" (string value) ispell_options='$ispell_options' # internal options # ignore the ORGANIZATION environment variable? (define/undef) d_ignoreorg='$d_ignoreorg' # does the mailer understand FQDN addressing? (define/undef) d_internet='$d_internet' # do you have a news admin? (define/undef) d_newsadm='$d_newsadm' # name of the news admin? (string value) newsadmin='$newsadmin' # read via NNTP? (define/undef) d_nntp='$d_nntp' # use the XDATA NNTP extension? (define/undef) d_xdata='$d_xdata' # path to a file containing a server name, or a hostname (string value) servername='$servername' # distribution names (string values) # local city citydist='$citydist' # "local" country cntrydist='$cntrydist' # "local" continent contdist='$contdist' # site distribution locdist='$locdist' # organizational distribution orgdist='$orgdist' # state/province distribution name statedist='$statedist' # multistate region distribution name multistatedist='$multistatedist' # Naming information. # password file contains names (define/undef) d_passnames='$d_passnames' # berkeley style password entries (name first in GCOS) (define/undef) d_berknames='$d_berknames' # USG style password entries (account number first in GCOS) # (define/undef) d_usgnames='$d_usgnames' # what type of name to use.. (bsd/usg/other) nametype='$nametype' # How portable do we want to be? Determines if we do lookups now # or wait until run time. (define/undef) d_portable='$d_portable' # news library information # where is the news library (usually /usr/lib/news) may contain ~ newslib='$newslib' # absolute path name to /usr/lib/news. newslibexp='$newslibexp' # where is the news spool (usually /{var,usr}/spool/news) newsspool='$newsspool' # active file stuff, like where is it, what is its name, etc # path to the active file. (string value) active='$active' # do we have an active.times file? (define/undef) d_acttimes='$d_acttimes' # path to the active.times file. (string value) acttimes='$acttimes' # organizations name. path to file, or constant string orgname='$orgname' # only one of the two following is needed # command to find the posting hosts name (string value, optional) phostcmd='$phostcmd' # file containing posting hosts name or constant string # (string value, optional) # phost='$phost' # what should we use? mthreads or overview # use the mthreads format? (define/undef) d_usemt='$d_usemt' # where do we find the thread files? (string value) threaddir='$threaddir' # use the overview format? (define/undef) d_useov='$d_useov' # where do we find the .overview fils? (string value) overviewdir='$overviewdir' # trn start up options trn_init='$trn_init' # start up with the selector? trn_select='$trn_select' !GROK!THIS! $eunicefix Policy.sh case "$srcdir" in '') srcdir='.';; esac if test -f $srcdir/hints/Policy.sh ; then echo "Checking for changes." grep -v '^cf_' Policy.sh >Policy.tmp if grep -v '^cf_' $srcdir/hints/Policy.sh | diff - Policy.tmp >/dev/null then echo "Policy.sh unchanged, retaining original" rm Policy.sh else echo "installing new Policy.sh, old one left in Policy.sh.old" (cd $srcdir/hints; mv Policy.sh Policy.sh.old) mv Policy.sh $srcdir/hints fi rm Policy.tmp else mv Policy.sh $srcdir/hints fi trn-3.6/trn-artchk.c0000664000000000000000000001705305654303441011310 0ustar /* $Id: trn-artchk.c,v 3.5 1993/04/18 03:09:32 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ /* A program to check an article's validity and print warnings if problems ** are found. ** ** Usage: trn-artchk
*/ #include "EXTERN.h" #include "common.h" #ifdef USE_NNTP #include "nntpclient.h" #endif #define MAXNGS 100 char *safemalloc _((MEM_SIZE)); #ifdef USE_NNTP int server_connection _((void)); void finalize _((int)); char nntp_handle_timeout _((bool_int)); int debug = 0; #ifdef USE_GENAUTH char *loginName; #endif #endif /* USE_NNTP */ int main(argc, argv) int argc; char *argv[]; { FILE *fp, *fp_active = NULL, *fp_ng = NULL; bool check_active = FALSE, check_ng = FALSE; char buff[LBUFLEN], *cp, *cp2; char *ngptrs[MAXNGS]; int nglens[MAXNGS]; int foundactive[MAXNGS]; int i, col, max_col_len, line_num = 0, ngcnt = 0, ngleft; int found_newsgroups = 0; if (argc != 5 || !(max_col_len = atoi(argv[2]))) { fprintf(stderr, "\ Usage: trn-artchk
\n"); exit(1); } if ((fp = fopen(argv[1], "r")) == NULL) { fprintf(stderr, "trn-artchk: unable to open article `%s'.\n", argv[1]); exit(1); } /* Check the header for proper format and report on the newsgroups */ while (fgets(buff, LBUFLEN, fp)) { line_num++; buff[strlen(buff)-1] = '\0'; if (!*buff) break; if (*buff == ' ' || *buff == '\t') continue; if (!(cp = index(buff, ':'))) { printf("\nERROR: line %d is an invalid header line:\n%s\n", line_num, buff); break; } if (cp[1] != ' ' && cp[1] != '\0') { printf("\n\ ERROR: header on line %d does not have a space after the colon:\n%s\n", line_num, buff); } if (cp - buff == 10 && strnEQ(buff, "Newsgroups", 10)) { found_newsgroups = 1; for (cp = buff + 11; *cp == ' '; cp++) ; if (index(cp, ' ')) { printf("\n\ ERROR: the \"Newsgroups:\" line has spaces in it that MUST be removed. The\n\ only allowable space is the one separating the colon (:) from the contents.\n\ Use a comma (,) to separate multiple newsgroup names.\n"); continue; } while (*cp) { if (!(cp2 = index(cp, ','))) cp2 = cp + strlen(cp); else *cp2++ = '\0'; if (ngcnt < MAXNGS) { nglens[ngcnt] = strlen(cp); foundactive[ngcnt] = 0; ngptrs[ngcnt] = safemalloc(nglens[ngcnt]+1); strcpy(ngptrs[ngcnt], cp); ngcnt++; } cp = cp2; } if (!ngcnt) { printf("\n\ ERROR: the \"Newsgroups:\" line lists no newsgroups.\n"); continue; } } } if (!found_newsgroups) { printf("\nERROR: the \"Newsgroups:\" line is missing from the header.\n"); } /* Check the body of the article for long lines */ while (fgets(buff, LBUFLEN, fp)) { line_num++; col = strlen(buff)-1; if (buff[col] != '\n') printf("\n\ Warning: line %d has no trailing newline character and may get lost.\n", line_num); else buff[col] = '\0'; col = 0; for (cp = buff; *cp; cp++) { if (*cp == '\t') col += 8 - (col%8); else col++; } if (col > max_col_len) { printf("\n\ Warning: posting exceeds %d columns. Line %d is the first long one:\n%s\n", max_col_len, line_num, buff); break; } } if (ngcnt) { struct stat st; if (stat(argv[3], &st) != -1) check_ng = st.st_size > 0 && (fp_ng = fopen(argv[3], "r")) != NULL; #ifdef USE_NNTP else if (server_connection()) check_ng = TRUE; #endif if (stat(argv[4], &st) != -1) check_active = st.st_size > 0 && (fp_active = fopen(argv[4], "r")) != NULL; #ifdef USE_NNTP else if (server_connection()) check_active = TRUE; #endif } if (ngcnt && (check_ng || check_active)) { /* Print a note about each newsgroup */ printf("\nYour article's newsgroup%s:\n", ngcnt == 1? "" : "s"); if (!check_active) { for (i = 0; i < ngcnt; i++) { foundactive[i] = 1; } } else if (fp_active != NULL) { ngleft = ngcnt; while (fgets(buff, LBUFLEN, fp_active)) { if (!ngleft) break; for (i = 0; i < ngcnt; i++) { if (!foundactive[i]) { if ((buff[nglens[i]] == '\t' || buff[nglens[i]] == ' ') && strnEQ(ngptrs[i], buff, nglens[i])) { foundactive[i] = 1; ngleft--; } } } } fclose(fp_active); } #ifdef USE_NNTP else { int listactive_works = 1; for (i = 0; i < ngcnt; i++) { if (listactive_works) { sprintf(ser_line, "list active %s", ngptrs[i]); nntp_command(ser_line); if (nntp_check(FALSE) == NNTP_CLASS_OK) { while (nntp_gets(ser_line, sizeof ser_line) >= 0) { if (NNTP_LIST_END(ser_line)) break; foundactive[i] = 1; } } else if (*ser_line == NNTP_CLASS_FATAL) { listactive_works = FALSE; i--; } } else { sprintf(ser_line, "GROUP %s", ngptrs[i]); nntp_command(ser_line); if (nntp_check(FALSE) == NNTP_CLASS_OK) foundactive[i] = 1; } } } if (check_ng && fp_ng == NULL) { fp_ng = fopen(argv[3], "w+"); unlink(argv[3]); if (fp_ng != NULL) { for (i = 0; i < ngcnt; i++) { /* issue a description list command */ sprintf(ser_line, "XGTITLE %s", ngptrs[i]); nntp_command(ser_line); /*$$ use list newsgroups if this fails...? */ if (nntp_check(FALSE) == NNTP_CLASS_OK) { /* write results to fp_ng */ while (nntp_gets(ser_line, sizeof ser_line) >= 0) { if (NNTP_LIST_END(ser_line)) break; fprintf(fp_ng, "%s\n", ser_line); } } } fseek(fp_ng, 0L, 0); } } #endif if (fp_ng != NULL) { ngleft = ngcnt; while (fgets(buff, LBUFLEN, fp_ng)) { if (!ngleft) break; for (i = 0; i < ngcnt; i++) { if (foundactive[i] && ngptrs[i]) { if ((buff[nglens[i]] == '\t' || buff[nglens[i]] == ' ') && strnEQ(ngptrs[i], buff, nglens[i])) { cp = &buff[nglens[i]]; *cp++ = '\0'; while (*cp == ' ' || *cp == '\t') cp++; if (cp[0] == '?' && cp[1] == '?') cp = "[no description available]\n"; printf("%-23s %s", buff, cp); free(ngptrs[i]); ngptrs[i] = 0; ngleft--; } } } } fclose(fp_ng); } for (i = 0; i < ngcnt; i++) { if (!foundactive[i]) { printf("%-23s ** invalid news group -- check spelling **\n", ngptrs[i]); free(ngptrs[i]); } else if (ngptrs[i]) { printf("%-23s [no description available]\n", ngptrs[i]); free(ngptrs[i]); } } } return 0; } #ifdef USE_NNTP int server_connection() { static int server_stat = 0; if (!server_stat) { if (nntp_connect(0)) server_stat = 1; else server_stat = -1; } return server_stat == 1; } char nntp_handle_timeout(strict) bool_int strict; { nntp_error("\n503 Server timed out.\n"); if (strict) finalize(1); return NNTP_CLASS_FATAL; } #endif void finalize(num) int num; { #ifdef USE_NNTP nntp_close(TRUE); #endif exit(num); } static char nomem[] = "trn: out of memory!\n"; /* paranoid version of malloc */ char * safemalloc(size) MEM_SIZE size; { char *ptr; ptr = malloc(size ? size : (MEM_SIZE)1); if (ptr == Nullch) { fputs(nomem,stdout) FLUSH; finalize(1); } return ptr; } /* paranoid version of realloc. If where is NULL, call malloc */ char * saferealloc(where,size) char *where; MEM_SIZE size; { char *ptr; ptr = realloc(where, size ? size : (MEM_SIZE)1); if (!ptr) { fputs(nomem,stdout) FLUSH; finalize(1); } return ptr; } trn-3.6/makedepend.SH0000664000000000000000000000513705625465775011441 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting makedepend (with variable substitutions)" case "$srcdir" in '') srcdir='.';; esac $spitshell >makedepend <\$deptmp $echo "(Note: this is going to take a while.)" cd \$srcdir for file in *.[cy]; do case "\$file" in *.c) filebase=\`basename \$file .c\`;; *.y) filebase=\`basename \$file .y\`;; esac $echo "Finding dependencies for \$filebase.o." $sed -n <\$file \\ -e "/^\${filebase}_init(/q" \\ -e '/^#/{' \\ -e 's|/\*.*$||' \\ -e 's/\\\\[ ]*$//' \\ -e p \\ -e '}' | $cppstdin -I\$objdir -I$usrinc $cppminus | $sed \\ -e '/^# *line/s/line//' \ -e '/^# *[0-9]/!d' \\ -e 's/^.*"\(.*\)".*\$/'\$filebase'.o: \1/' \\ -e 's|: \./|: |' \\ -e "s|\$objdir/||" \\ -e 's/: .*\.c/: '\$file'/' | \\ $uniq | $sort | $uniq >>\$deptmp done for file in *.SH; do $echo \`basename \$file .SH\`: \$file config.sh \; /bin/sh "\\\$(srcdir)/\$file" >>\$deptmp done cd \$objdir $sed Makefile.new -e '1,/^# AUTOMATICALLY/!d' if $test -s \$deptmp; then echo "Updating Makefile..." $sed -e 's/\\\$[^(]/\$\$/g' \$deptmp | $egrep -v $usrinc >dependencies else $echo "You don't seem to have a proper C preprocessor. Using grep instead." cd \$srcdir $egrep '^#include ' *.[cyh] ?.[cyh] >\$deptmp cd \$objdir echo "Updating Makefile..." <\$deptmp $sed -n 's|c:#include "\(.*\)".*\$\$|o: \1|p' > dependencies <\$deptmp $sed -n 's|y:#include "\(.*\)".*\$\$|o: \1|p' >> dependencies <\$deptmp $sed -n 's|h:#include "\(.*\)".*\$\$|h: \1|p' >> dependencies fi $echo "# WARNING: Put nothing here or make depend will gobble it up!" >> dependencies $mv Makefile Makefile.old $mv Makefile.new Makefile $cat dependencies >>Makefile rm \$deptmp !GROK!THIS! $eunicefix makedepend chmod 755 makedepend trn-3.6/unipatch.c0000664000000000000000000000274305316116211011036 0ustar /* A filter to turn a unified diff into a degenerate context diff (no '!'s) for patch. Version 1.1. Author: davison@borland.com */ #include #define ERR(a) {fputs(a,stderr);exit(1);} #define NUM(x) {for(x=0;*cp<='9'&&*cp>='0';)x=x*10+*cp++-'0';ch= *cp++;} struct Ln {struct Ln *lk; char t; char s[1];} r,*h,*ln; char bf[2048],*cp,ch,*malloc(); long os,ol,ns,nl,ne,lncnt; main() { for(;;){ for(;;){ if(!fgets(bf,sizeof bf,stdin)) exit(0); lncnt++; if(!strncmp(bf,"@@ -",4)) break; fputs(bf,stdout); } ol=nl=1, cp=bf+4; NUM(os) if(ch==',') NUM(ol) if(*cp++!='+') goto bad; NUM(ns) if(ch==',') NUM(nl) if(*cp!='@') goto bad; r.lk=0, h= &r, ne=ns+nl-1; printf("***************\n*** %ld,%ld ****\n",os,os+ol-(os>0)); while(ol||nl){ if(!fgets(bf,sizeof bf,stdin)){ if(nl>2) ERR("Unexpected end of file.\n") strcpy(bf," \n"); } lncnt++; if(*bf=='\t'||*bf=='\n') ch=' ', cp=bf; else ch= *bf, cp=bf+1; switch(ch){ case'-':if(!ol--) goto bad; printf("- %s",cp); break; case'=':ch=' '; case' ':if(!ol--) goto bad; printf(" %s",cp); case'+':if(!nl--) goto bad; ln = (struct Ln*)malloc(sizeof(*ln)+strlen(cp)); if(!ln) ERR("Out of memory!\n") ln->lk=0, ln->t=ch, strcpy(ln->s,cp); h->lk=ln, h=ln; break; default: bad: fprintf(stderr,"Malformed unified diff at line %ld: ",lncnt); ERR(bf) } } printf("--- %ld,%ld ----\n",ns,ne); for(ln=r.lk;ln;ln=h){ printf("%c %s",ln->t,ln->s); h=ln->lk; free(ln); } } } trn-3.6/cache.h0000664000000000000000000001027705663312255010307 0ustar /* $Id: cache.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ /* Subjects get their own structure */ typedef struct rt_subj { struct rt_subj *next; struct rt_subj *prev; struct rt_art *articles; struct rt_art *thread; struct rt_subj *thread_link; char *str; time_t date; short flags; short misc; /* used for temporary totals and subject numbers */ } SUBJECT; /* subject flags */ #define SF_SEL 0x0001 #define SF_DEL 0x0002 #define SF_DELSEL 0x0004 #define SF_OLDSEL 0x0008 #define SF_INCLUDED 0x0010 #define SF_VISIT 0x0200 #define SF_WASSELECTED 0x0400 #define SF_SUBJTRUNCED 0x1000 /* This is our article-caching structure */ typedef struct rt_art { time_t date; SUBJECT *subj; char *from; char *msgid; char *xrefs; struct rt_art *parent; /* parent article */ struct rt_art *child1; /* first child of a chain */ struct rt_art *sibling; /* our next sibling */ struct rt_art *subj_next; /* next article in subject order */ short flags; /* state flags */ short autofl; /* auto-processing flags */ } ARTICLE; /* article flags */ #define AF_SEL 0x0001 #define AF_DEL 0x0002 #define AF_DELSEL 0x0004 #define AF_OLDSEL 0x0008 #define AF_INCLUDED 0x0010 #define AF_READ 0x0020 #define AF_CACHED 0x0040 #define AF_THREADED 0x0080 #define AF_MISSING 0x0100 #define AF_HAS_RE 0x0200 #define AF_KCHASE 0x0400 #define AF_MCHASE 0x0800 #define AF_YANKBACK 0x1000 #define AF_FROMTRUNCED 0x2000 #define AF_TMPMEM (0x4000|AF_MISSING) #define AF_FAKE (0x8000|AF_MISSING) #define AUTO_KILL 0x0001 #define AUTO_KILLALL 0x0002 #define AUTO_SELECT 0x0004 #define AUTO_SELECT1 0x0008 #define AUTO_SELECTALL 0x0010 /* The following define is only valid as a flag to the select_article call */ #define AUTO_ECHO 0x8000 #define Nullart Null(ARTICLE*) #define Nullsubj Null(SUBJECT*) #define was_read(a) ((a) < absfirst || (article_ptr(a)->flags & AF_READ)) /* These must never use their args more than once in the definition */ #define article_num(ap) (((ap)-article_list)+absfirst) #define article_ptr(an) (article_list+((an)-absfirst)) #define find_article(an) ((an < absfirst || an > lastart)? \ Nullart : article_ptr(an)) EXT ARTICLE *article_list INIT(Nullart); EXT ARTICLE **artptr_list INIT(0); EXT ARTICLE **artptr; EXT ART_NUM artptr_list_size INIT(0); #ifdef ARTSEARCH EXT ART_NUM srchahead INIT(0); /* are we in subject scan mode? */ /* (if so, contains art # found or -1) */ #endif EXT ART_NUM first_cached; EXT ART_NUM last_cached; EXT bool cached_all_in_range; EXT ARTICLE *sentinel_artp; #define DONT_FILL_CACHE 0 #define FILL_CACHE 1 EXT struct rt_subj *first_subject INIT(0); EXT struct rt_subj *last_subject INIT(0); EXT bool untrim_cache INIT(FALSE); #ifdef PENDING EXT ART_NUM subj_to_get; EXT ART_NUM xref_to_get; #endif void cache_init _((void)); void build_cache _((void)); void grow_cache _((ART_NUM)); void close_cache _((void)); void cache_article _((ARTICLE*)); void check_for_near_subj _((ARTICLE*)); void change_join_subject_len _((int)); void check_poster _((ARTICLE*)); void uncache_article _((ARTICLE*,bool_int)); char *fetchcache _((ART_NUM,int,bool_int)); char *get_cached_line _((ARTICLE*, int, bool_int)); void set_subj_line _((ARTICLE*, char*, int)); void set_cached_line _((ARTICLE*, int, char*)); void look_ahead _((void)); void cache_until_key _((void)); bool cache_subjects _((void)); bool cache_xrefs _((void)); bool cache_all_arts _((void)); bool cache_unread_arts _((void)); bool art_data _((ART_NUM,ART_NUM,bool_int,bool_int)); bool cache_range _((ART_NUM,ART_NUM)); void clear_article _((ARTICLE*)); void free_subject _((SUBJECT*)); trn-3.6/unship.c0000664000000000000000000002714605563336051010547 0ustar /* unship.c -- for unpacking ship files via trn */ /* Based on ship.c -- Not copyrighted 1991 Mark Adler. */ /* Modified by Wayne Davison, but still not copyrighted. */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "respond.h" #include "decode.h" typedef unsigned long ulg; /* 32-bit unsigned integer */ /* Function prototypes */ static void decode_line _((unsigned char *)); static void err _((int)); /* Globals for ship() */ ulg ccnt; /* count of bytes read or written */ ulg crc; /* CRC register */ ulg buf4; /* four byte buffer */ int bcnt; /* buffer count */ unsigned int decb; /* bit buffer for decode */ unsigned int decn; /* number of bits in decb */ bool fast; /* true for arithmetic coding, else base 85 */ bool overwrite = 1; /* should we overwrite existing files? */ /* Errors */ #define SE_FORM 1 #define SE_CONT 2 #define SE_CRC 3 #define SE_OVER 4 #define SE_FULL 5 char *errors[] = { /* 1 */ "Invalid ship format.", /* 2 */ "This piece is out of sequence.", /* 3 */ "CRC check failed.", /* 4 */ "File already exists.", /* 5 */ "Error writing file.", }; /* Set of 86 characters used for the base 85 digits (last one not used), and the 86 character arithmetic coding. Selected to be part of both the ASCII printable characters, and the common EBCDIC printable characters whose ASCII translations are universal. */ unsigned char safe[] = { '{','"','#','$','%','&','\'','(',')','*','+',',','-','.','/', '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?','@', 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z','_', 'a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z','}'}; #define LOWSZ (sizeof(safe)-64) /* low set size for fast coding */ /* Special replacement pairs--if first of each pair is received, it is treated like the second member of the pair. You're probably wondering why. The first pair is for compatibility with an earlier version of ship that used ! for the base 85 zero digit. However, there exist ASCII-EBCDIC translation tables that don't know about exclamation marks. The second set has mysterious historical origins that are best left unspoken ... */ unsigned char aliases[] = {'!','{','|','+',0}; /* Inverse of safe[], filled in by unship_init() */ unsigned char invsafe[256]; /* Table of CRC-32's of all single byte values (made by makecrc.c) */ ulg crctab[] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; /* Macro to update the CRC shift register one byte at a time */ #define CRC(c,b) (crctab[((int)(c)^(int)(b))&0xff]^((c)>>8)) /* cputc(d,x)--like putc(d,f), but delays four bytes and computes a CRC. x is a cfile *, and d is expected to be an ulg. */ #define cputf(fp) (int)(crc=CRC(crc,buf4),putc((int)buf4&0xff,fp),ccnt++) #define cputc(d,fp) (bcnt!=4?bcnt++:cputf(fp),buf4=(buf4>>8)+((ulg)(d)<<24)) /* Build invsafe[], the inverse of safe[]. */ void unship_init() { int i; for (i = 0; i < 256; i++) invsafe[i] = 127; for (i = 0; i < sizeof(safe); i++) invsafe[safe[i]] = (char)i; for (i = 0; aliases[i]; i += 2) invsafe[aliases[i]] = invsafe[aliases[i + 1]]; } int unship(in) FILE *in; { int b; /* state of line loop */ char l[LBUFLEN]; /* line buffer on input */ char *p; /* modifies line buffer */ char *q; /* scans continuation line */ /* Loop on the latest article's lines */ b = 2; /* not in body yet */ while (1) /* return on end of last file */ { /* Get next line from file */ if (fgets(l, LBUFLEN, in) == Nullch) break; /* Strip control characters and leading blank space, if any */ for (q = l; *q && *q <= ' ' && *q != '\n'; q++) ; for (p = l; *q; q++) if (*q >= ' ' || *q == '\n') *p++ = *q; *p = 0; /* Based on current state, end or start on terminator. States are: b == 0: at start of body or body terminator line b == 1: in middle of body line b == 2: at start of non-body line b == 3: in middle of non-body line b == 4: at information line */ switch (b) { case 0: if ((!fast && strEQ(l, "$\n")) || (fast && strEQ(l, "$ f\n"))) { b = 4; break; } /* fall through to case 1 */ case 1: decode_line((unsigned char *)l); b = l[strlen(l) - 1] != '\n'; break; case 2: if (strEQ(l, "$\n") || strEQ(l, "$ f\n")) { fast = l[1] == ' '; b = 4; break; } /* fall through to case 3 */ case 3: b = l[strlen(l)-1] == '\n' ? 2 : 3; break; case 4: /* Possible information lines are ship, more, cont, and end */ if (l[b = strlen(l) - 1] != '\n') { err(SE_FORM); decode_end(); return -1; } l[b] = 0; if (strnEQ(l, "ship ", 5)) { /* get name, open new output file */ if (decode_fp != Nullfp) decode_end(); /* outputs an "incomplete" warning */ if (strEQ(l + 5, "-")) strcpy(decode_fname, "unnamed"); else strcpy(decode_fname, l + 5); sprintf(decode_dest, "%s/%s", extractdest, decode_fname); printf("Decoding: %s\n", decode_fname); #ifndef VMS /* shouldn't have explicit version #, so VMS won't overwrite */ if (!overwrite && (decode_fp = fopen(decode_dest, "r")) != Nullfp) { fclose(decode_fp); decode_fp = Nullfp; err(SE_OVER); return -1; } #endif /* !VMS */ if ((decode_fp = fopen(decode_dest, FOPEN_WB)) == Nullfp) { err(SE_FULL); return -1; } crc = 0xffffffffL; /* preload CRC register */ buf4 = 0; /* empty fifo (for output) */ bcnt = 0; /* fifo is empty (output) */ b = decb = decn = 0; ccnt = 0; } else if (strEQ(l, "more")) { /* check if currently writing */ if (decode_fp == Nullfp) { err(SE_FORM); return -1; } b = 2; } else if (strnEQ(l, "cont ", 5)) { /* check name and file offset */ if (decode_fp == Nullfp) { err(SE_CONT); return -1; } for (q = l + 5; *q && *q != ' '; q++) ; if (*q == 0 || atol(l + 5) != ccnt + 4 + (decn != 0) || strNE(q + 1, decode_fname)) { err(SE_CONT); return -1; } b = 0; } else if (strcmp(l, "end") == 0) { /* check crc, close output file */ if (decode_fp == Nullfp) { err(SE_FORM); return -1; } if (bcnt != 4 || buf4 != ~crc) err(SE_CRC); else printf("CRC verified -- Done.\n"); if (ferror(decode_fp) || fclose(decode_fp)) { err(SE_FULL); decode_end(); return -1; } decode_fp = Nullfp; b = 2; } else { for (q = l; *q && *q != ' '; q++) ; *q = 0; printf("Ignoring unsupported ship keyword: '%s'\n", l); b = 4; } break; } } if (!(b & 2)) { err(SE_FORM); return -1; } if (decode_fp) printf("(Continued)\n"); return 0; } /* Decode s, a string of base 85 digits or, if fast is true, a string of safe characters generated arithmetically, into its binary equivalent, writing the result to decode_fp, using cputc(). */ static void decode_line(s) unsigned char *s; /* data to decode */ { int b; /* state of line loop, next character */ int k; /* counts bits or digits read */ /* powers of 85 table for decoding */ static ulg m[] = {1L,85L,85L*85L,85L*85L*85L,85L*85L*85L*85L}; if (fast) { unsigned int d; /* disperses bits */ d = decb; k = decn; while ((b = *s++) != 0) if ((b = invsafe[b]) < sizeof(safe)) { if (b < LOWSZ) { d |= b << k; k += 7; } else if ((b -= LOWSZ) < LOWSZ) { d |= (b + 0x40) << k; k += 7; } else { d |= b << k; k += 6; } if (k >= 8) { cputc(d, decode_fp); d >>= 8; k -= 8; } } decb = d; decn = k; } else { ulg d; /* disperses bytes */ d = k = 0; while ((b = *s++) != 0) if ((b = invsafe[b]) < 85) { d += m[k] * b; if (++k == 5) { cputc(d, decode_fp); d >>= 8; cputc(d, decode_fp); d >>= 8; cputc(d, decode_fp); d >>= 8; cputc(d, decode_fp); d = k = 0; } } if (--k > 0) { while (--k) { cputc(d, decode_fp); d >>= 8; } cputc(d, decode_fp); } } } static void err(n) int n; /* error number */ { if (n == SE_FULL) perror("ship"); fputs(errors[n - 1], stdout); putchar('\n') FLUSH; } trn-3.6/search.c0000664000000000000000000003217305424541447010505 0ustar /* $Id: search.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* string search routines */ /* Copyright (c) 1981,1980 James Gosling */ /* Modified Aug. 12, 1981 by Tom London to include regular expressions as in ed. RE stuff hacked over by jag to correct a few major problems, mainly dealing with searching within the buffer rather than copying each line to a separate array. Newlines can now appear in RE's */ /* Ripped to shreds and glued back together to make a search package, * July 6, 1984, by Larry Wall. (If it doesn't work, it's probably my fault.) * Changes include: * Buffer, window, and mlisp stuff gone. * Translation tables reduced to 1 table. * Expression buffer is now dynamically allocated. * Character classes now implemented with a bitmap. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "INTERN.h" #include "search.h" #ifndef BITSPERBYTE #define BITSPERBYTE 8 #endif #define BMAPSIZ (127 / BITSPERBYTE + 1) /* meta characters in the "compiled" form of a regular expression */ #define CBRA 2 /* \( -- begin bracket */ #define CCHR 4 /* a vanilla character */ #define CDOT 6 /* . -- match anything except a newline */ #define CCL 8 /* [...] -- character class */ #define NCCL 10 /* [^...] -- negated character class */ #define CDOL 12 /* $ -- matches the end of a line */ #define CEND 14 /* The end of the pattern */ #define CKET 16 /* \) -- close bracket */ #define CBACK 18 /* \N -- backreference to the Nth bracketed string */ #define CIRC 20 /* ^ matches the beginning of a line */ #define WORD 32 /* matches word character \w */ #define NWORD 34 /* matches non-word characer \W */ #define WBOUND 36 /* matches word boundary \b */ #define NWBOUND 38 /* matches non-(word boundary) \B */ #define STAR 01 /* * -- Kleene star, repeats the previous REas many times as possible; the value ORs with the other operator types */ #define ASCSIZ 0200 typedef char TRANSTABLE[ASCSIZ]; static TRANSTABLE trans = { 0000,0001,0002,0003,0004,0005,0006,0007, 0010,0011,0012,0013,0014,0015,0016,0017, 0020,0021,0022,0023,0024,0025,0026,0027, 0030,0031,0032,0033,0034,0035,0036,0037, 0040,0041,0042,0043,0044,0045,0046,0047, 0050,0051,0052,0053,0054,0055,0056,0057, 0060,0061,0062,0063,0064,0065,0066,0067, 0070,0071,0072,0073,0074,0075,0076,0077, 0100,0101,0102,0103,0104,0105,0106,0107, 0110,0111,0112,0113,0114,0115,0116,0117, 0120,0121,0122,0123,0124,0125,0126,0127, 0130,0131,0132,0133,0134,0135,0136,0137, 0140,0141,0142,0143,0144,0145,0146,0147, 0150,0151,0152,0153,0154,0155,0156,0157, 0160,0161,0162,0163,0164,0165,0166,0167, 0170,0171,0172,0173,0174,0175,0176,0177, }; static bool folding = FALSE; static int err; static char *FirstCharacter; void search_init() { #ifdef UNDEF register int i; for (i = 0; i < ASCSIZ; i++) trans[i] = i; #else ; #endif } void init_compex(compex) register COMPEX *compex; { /* the following must start off zeroed */ compex->eblen = 0; compex->brastr = Nullch; } void free_compex(compex) register COMPEX *compex; { if (compex->eblen) { free(compex->expbuf); compex->eblen = 0; } if (compex->brastr) { free(compex->brastr); compex->brastr = Nullch; } } static char *gbr_str = Nullch; static int gbr_siz = 0; char * getbracket(compex,n) register COMPEX *compex; int n; { int length = compex->braelist[n] - compex->braslist[n]; if (!compex->nbra) return Nullch; if (n > compex->nbra || !compex->braelist[n] || length < 0) return nullstr; growstr(&gbr_str, &gbr_siz, length+1); safecpy(gbr_str, compex->braslist[n], length+1); return gbr_str; } void case_fold(which) int which; { register int i; if (which != folding) { if (which) { for (i = 'A'; i <= 'Z'; i++) trans[i] = tolower(i); } else { for (i = 'A'; i <= 'Z'; i++) trans[i] = i; } folding = which; } } /* Compile the given regular expression into a [secret] internal format */ char * compile(compex, strp, RE, fold) register COMPEX *compex; register char *strp; int RE; int fold; { register int c; register char *ep; char *lastep; char bracket[NBRA], *bracketp; char **alt = compex->alternatives; char *retmes = "Badly formed search string"; case_fold(compex->do_folding = fold); if (!compex->eblen) { compex->expbuf = safemalloc(84); compex->eblen = 80; } ep = compex->expbuf; /* point at expression buffer */ *alt++ = ep; /* first alternative starts here */ bracketp = bracket; /* first bracket goes here */ if (*strp == 0) { /* nothing to compile? */ if (*ep == 0) /* nothing there yet? */ return "Null search string"; return Nullch; /* just keep old expression */ } compex->nbra = 0; /* no brackets yet */ lastep = 0; for (;;) { if (ep + 4 - compex->expbuf >= compex->eblen) ep = grow_eb(compex, ep, alt); c = *strp++; /* fetch next char of pattern */ if (c == 0) { /* end of pattern? */ if (bracketp != bracket) { /* balanced brackets? */ #ifdef VERBOSE retmes = "Unbalanced parens"; #endif goto cerror; } *ep++ = CEND; /* terminate expression */ *alt++ = 0; /* terminal alternative list */ return Nullch; /* return success */ } if (c != '*') lastep = ep; if (!RE) { /* just a normal search string? */ *ep++ = CCHR; /* everything is a normal char */ *ep++ = c; } else /* it is a regular expression */ switch (c) { case '\\': /* meta something */ switch (c = *strp++) { case '(': if (compex->nbra >= NBRA) { #ifdef VERBOSE retmes = "Too many parens"; #endif goto cerror; } *bracketp++ = ++compex->nbra; *ep++ = CBRA; *ep++ = compex->nbra; break; case '|': if (bracketp>bracket) { #ifdef VERBOSE retmes = "No \\| in parens"; /* Alas! */ #endif goto cerror; } *ep++ = CEND; *alt++ = ep; break; case ')': if (bracketp <= bracket) { #ifdef VERBOSE retmes = "Unmatched right paren"; #endif goto cerror; } *ep++ = CKET; *ep++ = *--bracketp; break; case 'w': *ep++ = WORD; break; case 'W': *ep++ = NWORD; break; case 'b': *ep++ = WBOUND; break; case 'B': *ep++ = NWBOUND; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *ep++ = CBACK; *ep++ = c - '0'; break; default: *ep++ = CCHR; if (c == '\0') goto cerror; *ep++ = c; break; } break; case '.': *ep++ = CDOT; continue; case '*': if (lastep == 0 || *lastep == CBRA || *lastep == CKET || *lastep == CIRC || (*lastep&STAR)|| *lastep>NWORD) goto defchar; *lastep |= STAR; continue; case '^': if (ep != compex->expbuf && ep[-1] != CEND) goto defchar; *ep++ = CIRC; continue; case '$': if (*strp != 0 && (*strp != '\\' || strp[1] != '|')) goto defchar; *ep++ = CDOL; continue; case '[': { /* character class */ register int i; if (ep - compex->expbuf >= compex->eblen - BMAPSIZ) ep = grow_eb(compex, ep, alt); /* reserve bitmap */ for (i = BMAPSIZ; i; --i) ep[i] = 0; if ((c = *strp++) == '^') { c = *strp++; *ep++ = NCCL; /* negated */ } else *ep++ = CCL; /* normal */ i = 0; /* remember oldchar */ do { if (c == '\0') { #ifdef VERBOSE retmes = "Missing ]"; #endif goto cerror; } if (*strp == '-' && *(++strp) != ']' && *strp) i = *strp++; else i = c; while (c <= i) { ep[c / BITSPERBYTE] |= 1 << (c % BITSPERBYTE); if (fold && isalpha(c)) ep[(c ^ 32) / BITSPERBYTE] |= 1 << ((c ^ 32) % BITSPERBYTE); /* set the other bit too */ c++; } } while ((c = *strp++) != ']'); ep += BMAPSIZ; continue; } defchar: default: *ep++ = CCHR; *ep++ = c; } } cerror: compex->expbuf[0] = 0; compex->nbra = 0; return retmes; } char * grow_eb(compex, epp, alt) register COMPEX *compex; char *epp; char **alt; { register char *oldbuf = compex->expbuf; register char **altlist = compex->alternatives; compex->eblen += 80; compex->expbuf = saferealloc(compex->expbuf, (MEM_SIZE)compex->eblen + 4); if (compex->expbuf != oldbuf) { /* realloc can change expbuf! */ epp += compex->expbuf - oldbuf; while (altlist != alt) *altlist++ += compex->expbuf - oldbuf; } return epp; } char * execute(compex, addr) register COMPEX *compex; char *addr; { register char *p1 = addr; register char *trt = trans; register int c; if (addr == Nullch || compex->expbuf == Nullch) return Nullch; if (compex->nbra) { /* any brackets? */ for (c = 0; c <= compex->nbra; c++) compex->braslist[c] = compex->braelist[c] = Nullch; if (compex->brastr) free(compex->brastr); compex->brastr = savestr(p1); /* in case p1 is not static */ p1 = compex->brastr; /* ! */ } case_fold(compex->do_folding); /* make sure table is correct */ FirstCharacter = p1; /* for ^ tests */ if (compex->expbuf[0] == CCHR && !compex->alternatives[1]) { c = trt[compex->expbuf[1]]; /* fast check for first character */ do { if (trt[*p1] == c && advance(compex, p1, compex->expbuf)) return p1; p1++; } while (*p1 && !err); if (err) err = 0; return Nullch; } else { /* regular algorithm */ do { register char **alt = compex->alternatives; while (*alt) { if (advance(compex, p1, *alt++)) return p1; } p1++; } while (*p1 && !err); if (err) err = 0; return Nullch; } /*NOTREACHED*/ } /* advance the match of the regular expression starting at ep along the string lp, simulates an NDFSA */ bool advance(compex, lp, ep) register COMPEX *compex; register char *ep; register char *lp; { register char *curlp; register char *trt = trans; register int i; while ((*ep & STAR) || *lp || *ep == CIRC || *ep == CKET) switch (*ep++) { case CCHR: if (trt[*ep++] != trt[*lp]) return FALSE; lp++; continue; case CDOT: if (*lp == '\n') return FALSE; lp++; continue; case CDOL: if (!*lp || *lp == '\n') continue; return FALSE; case CIRC: if (lp == FirstCharacter || lp[-1]=='\n') continue; return FALSE; case WORD: if (isalnum(*lp)) { lp++; continue; } return FALSE; case NWORD: if (!isalnum(*lp)) { lp++; continue; } return FALSE; case WBOUND: if ((lp == FirstCharacter || !isalnum(lp[-1])) != (!*lp || !isalnum(*lp)) ) continue; return FALSE; case NWBOUND: if ((lp == FirstCharacter || !isalnum(lp[-1])) == (!*lp || !isalnum(*lp))) continue; return FALSE; case CEND: return TRUE; case CCL: if (cclass(ep, *lp, 1)) { ep += BMAPSIZ; lp++; continue; } return FALSE; case NCCL: if (cclass(ep, *lp, 0)) { ep += BMAPSIZ; lp++; continue; } return FALSE; case CBRA: compex->braslist[*ep++] = lp; continue; case CKET: i = *ep++; compex->braelist[i] = lp; compex->braelist[0] = lp; compex->braslist[0] = compex->braslist[i]; continue; case CBACK: if (compex->braelist[i = *ep++] == 0) { fputs("bad braces\n",stdout) FLUSH; err = TRUE; return FALSE; } if (backref(compex, i, lp)) { lp += compex->braelist[i] - compex->braslist[i]; continue; } return FALSE; case CBACK | STAR: if (compex->braelist[i = *ep++] == 0) { fputs("bad braces\n",stdout) FLUSH; err = TRUE; return FALSE; } curlp = lp; while (backref(compex, i, lp)) { lp += compex->braelist[i] - compex->braslist[i]; } while (lp >= curlp) { if (advance(compex, lp, ep)) return TRUE; lp -= compex->braelist[i] - compex->braslist[i]; } continue; case CDOT | STAR: curlp = lp; while (*lp++ && lp[-1] != '\n'); goto star; case WORD | STAR: curlp = lp; while (*lp++ && isalnum(lp[-1])); goto star; case NWORD | STAR: curlp = lp; while (*lp++ && !isalnum(lp[-1])); goto star; case CCHR | STAR: curlp = lp; while (*lp++ && trt[lp[-1]] == trt[*ep]); ep++; goto star; case CCL | STAR: case NCCL | STAR: curlp = lp; while (*lp++ && cclass(ep, lp[-1], ep[-1] == (CCL | STAR))); ep += BMAPSIZ; goto star; star: do { lp--; if (advance(compex, lp, ep)) return TRUE; } while (lp > curlp); return FALSE; default: fputs("Badly compiled pattern\n",stdout) FLUSH; err = TRUE; return -1; } if (*ep == CEND || *ep == CDOL || *ep == WBOUND) { return TRUE; } return FALSE; } bool backref(compex, i, lp) register COMPEX *compex; register int i; register char *lp; { register char *bp; bp = compex->braslist[i]; while (*lp && *bp == *lp) { bp++; lp++; if (bp >= compex->braelist[i]) return TRUE; } return FALSE; } bool cclass(set, c, af) register char *set; register int c; int af; { c &= 0177; #if BITSPERBYTE == 8 if (set[c >> 3] & 1 << (c & 7)) #else if (set[c / BITSPERBYTE] & 1 << (c % BITSPERBYTE)) #endif return af; return !af; } trn-3.6/kfile.h0000664000000000000000000000234605420174076010332 0ustar /* $Id: kfile.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #define KF_GLOBAL 0 #define KF_LOCAL 1 #ifdef KILLFILES EXT FILE *globkfp INIT(Nullfp); /* global article killer file */ EXT FILE *localkfp INIT(Nullfp); /* local (for this newsgroup) file */ EXT int localkf_changes; /* do we need to write changes? */ EXT bool has_normal_kills; /* flag when KILLs needs rereading */ EXT ART_NUM killfirst; /* used as firstart when killing */ #endif void kfile_init _((void)); int do_kfile _((FILE*,int)); void kill_unwanted _((ART_NUM,char*,int)); int edit_kfile _((void)); void open_kfile _((int)); void kf_append _((char*)); void rewrite_kfile _((ART_NUM)); trn-3.6/ngstuff.c0000664000000000000000000002556407041327652010717 0ustar /* $Id: ngstuff.c,v 3.0 1991/09/09 20:23:31 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "term.h" #include "util.h" #include "util2.h" #include "cache.h" #include "bits.h" #include "ngdata.h" #include "ng.h" #include "intrp.h" #include "head.h" #include "final.h" #include "sw.h" #include "rthread.h" #include "rt-select.h" #include "rt-wumpus.h" #include "trn.h" #include "rcstuff.h" #include "respond.h" #include "kfile.h" #include "decode.h" #include "INTERN.h" #include "ngstuff.h" void ngstuff_init() { ; } /* do a shell escape */ int escapade() { register char *s; bool interactive = (buf[1] == FINISHCMD); bool docd; char whereiam[512]; if (!finish_command(interactive)) /* get remainder of command */ return -1; s = buf+1; docd = *s != '!'; if (!docd) { s++; } else { getcwd(whereiam,sizeof(whereiam)); if (chdir(cwd)) { printf(nocd,cwd) FLUSH; sig_catcher(0); } } while (*s == ' ') s++; /* skip leading spaces */ interp(cmd_buf, (sizeof cmd_buf), s);/* interpret any % escapes */ resetty(); /* make sure tty is friendly */ doshell(Nullch,cmd_buf); /* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ if (docd) { if (chdir(whereiam)) { printf(nocd,whereiam) FLUSH; sig_catcher(0); } } #ifdef MAILCALL mailcount = 0; /* force recheck */ #endif return 0; } /* process & command */ int switcheroo() { if (!finish_command(TRUE)) /* get rest of command */ return -1; /* if rubbed out, try something else */ if (!buf[1]) pr_switches(); else if (buf[1] == '&') { if (!buf[2]) { page_init(); show_macros(); } else { char tmpbuf[LBUFLEN]; register char *s; for (s=buf+2; isspace(*s); s++); mac_line(s,tmpbuf,(sizeof tmpbuf)); } } else { bool docd = (instr(buf,"-d", TRUE) != Nullch); char whereami[512]; if (docd) getwd(whereami); sw_list(buf+1); if (docd) { cwd_check(); if (chdir(whereami)) { /* -d does chdirs */ printf(nocd,whereami) FLUSH; sig_catcher(0); } } } return 0; } /* process range commands */ int numnum() { ART_NUM min, max; char *cmdlst = Nullch; register char *s, *c; ART_NUM oldart = art; char tmpbuf[LBUFLEN]; bool justone = TRUE; /* assume only one article */ perform_cnt = 0; if (!finish_command(TRUE)) /* get rest of command */ return NN_INP; if (lastart < 1) { fputs("\nNo articles\n",stdout) FLUSH; return NN_ASK; } #ifdef ARTSEARCH if (srchahead) srchahead = -1; #endif for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++) if (!isdigit(*s)) justone = FALSE; if (*s) { cmdlst = savestr(s); justone = FALSE; } else if (!justone) cmdlst = savestr("m"); *s++ = ','; *s = '\0'; safecpy(tmpbuf,buf,LBUFLEN); for (s = tmpbuf; c = index(s,','); s = ++c) { *c = '\0'; if (*s == '.') min = oldart; else min = atol(s); if (min < absfirst) { min = absfirst; printf("(First article is %ld)\n",(long)absfirst) FLUSH; pad(just_a_sec/3); } if ((s=index(s,'-')) != Nullch) { s++; if (*s == '$') max = lastart; else if (*s == '.') max = oldart; else max = atol(s); } else max = min; if (max>lastart) { max = lastart; if (min > max) min = max; printf("(Last article is %ld)\n",(long)lastart) FLUSH; pad(just_a_sec/3); } if (max < min) { fputs("\nBad range\n",stdout) FLUSH; if (cmdlst) free(cmdlst); return NN_ASK; } if (justone) { art = min; return NN_REREAD; } for (art=min, artp=article_ptr(min); art<=max; art++, artp++) { if (perform(cmdlst,TRUE)) { #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art) FLUSH; #endif if (cmdlst) free(cmdlst); return NN_ASK; } } } art = oldart; if (cmdlst) free(cmdlst); return NN_NORM; } int thread_perform() { register SUBJECT *sp; register ARTICLE *ap; bool want_read; char *cmdstr; int len, ret = 1; int bits; bool one_thread = FALSE; if (!finish_command(TRUE)) /* get rest of command */ return 0; if (!buf[1]) return -1; len = 1; if (buf[1] == ':') { bits = 0; len++; } else bits = SF_VISIT; if (buf[len] == '.') { if (!artp) return -1; one_thread = TRUE; len++; } cmdstr = savestr(buf+len); want_read = (sel_rereading || *cmdstr == 'm'); perform_cnt = 0; page_line = 1; len = strlen(cmdstr); /* A few commands can just loop through the subjects. */ if ((len == 1 && (*cmdstr == 't' || *cmdstr == 'J')) || (len == 2 && (((*cmdstr == '+' || *cmdstr == '-') && cmdstr[0] == cmdstr[1]) || *cmdstr == 'T'))) { if (one_thread) sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); else sp = next_subj(Nullsubj,bits); for ( ; sp; sp = next_subj(sp,bits)) { if (!(sp->flags & sel_mask) ^ !bits) continue; artp = first_art(sp); if (artp) { art = article_num(artp); if (perform(cmdstr, FALSE)) { fputs("\nInterrupted\n", stdout) FLUSH; goto break_out; } } #ifdef VERBOSE IF(verbose) if (mode != 't' && *cmdstr != 't' && *cmdstr != 'T') putchar('\n') FLUSH; #endif if (one_thread) break; } } else if (strEQ(cmdstr, "E")) { /* The 'E'nd-decode command doesn't do any looping at all. */ if (decode_fp) decode_end(); else ret = 2; } else if (*cmdstr == 'p') { ART_NUM oldart = art; art = lastart+1; followup(); forcegrow = TRUE; art = oldart; } else { /* The rest loop through the articles. */ /* Use the explicit article-order if it exists */ if (artptr_list) { ARTICLE **app, **limit = artptr_list + artptr_list_size; sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); for (app = artptr_list; app < limit; app++) { ap = *app; if (one_thread && ap->subj->thread != sp->thread) continue; if ((!(ap->flags & AF_READ) ^ want_read) && !(ap->flags & sel_mask) ^ !!bits) { art = article_num(ap); artp = ap; if (perform(cmdstr, TRUE)) { fputs("\nInterrupted\n", stdout) FLUSH; goto break_out; } } } } else { if (one_thread) sp = (sel_mode==SM_THREAD? artp->subj->thread->subj : artp->subj); else sp = next_subj(Nullsubj,bits); for ( ; sp; sp = next_subj(sp,bits)) { for (ap = first_art(sp); ap; ap = next_art(ap)) if ((!(ap->flags & AF_READ) ^ want_read) && !(ap->flags & sel_mask) ^ !!bits) { art = article_num(ap); artp = ap; if (perform(cmdstr, TRUE)) { fputs("\nInterrupted\n", stdout) FLUSH; goto break_out; } } if (one_thread) break; } } } break_out: free(cmdstr); return ret; } int perform(cmdlst,toplevel) register char *cmdlst; int toplevel; { register int ch; bool saveit = FALSE; if (toplevel) { printf("%-6ld ",art); fflush(stdout); } perform_cnt++; for (; ch = *cmdlst; cmdlst++) { if (isspace(ch) || ch == ':') continue; if (ch == 'j' && !saveit) { if (!was_read(art)) { mark_as_read(); #ifdef VERBOSE IF(verbose) fputs("\tJunked",stdout); #endif } if (sel_rereading) deselect_article(artp); } else if (ch == '+') { if (saveit || cmdlst[1] == '+') { if (sel_mode == SM_THREAD) select_arts_thread(artp, saveit? AUTO_SELECTALL : 0); else select_arts_subject(artp, saveit? AUTO_SELECTALL : 0); if (cmdlst[1] == '+') cmdlst++; } else select_article(artp, AUTO_ECHO); } else if (ch == '.') { select_subthread(artp, saveit? AUTO_SELECT : 0); } else if (ch == '-') { if (cmdlst[1] == '-') { if (sel_mode == SM_THREAD) deselect_arts_thread(artp); else deselect_arts_subject(artp); cmdlst++; } else deselect_article(artp); } else if (ch == ',') { kill_subthread(artp, saveit? (KF_ALL|KF_KILLFILE) : KF_ALL); } else if (ch == 'J' || ch == 'j') { if (sel_mode == SM_THREAD) kill_arts_thread(artp, saveit? (KF_ALL|KF_KILLFILE) : KF_ALL); else kill_arts_subject(artp, saveit? (KF_ALL|KF_KILLFILE) : KF_ALL); } else if (ch == 'x') { if (!was_read(art)) { oneless(artp); #ifdef VERBOSE IF(verbose) fputs("\tKilled",stdout); #endif } if (sel_rereading) deselect_article(artp); } else if (ch == 't') { entire_tree(artp); } else if (ch == 'T') { saveit = TRUE; } else if (ch == 'm') { if ((article_ptr(art)->flags & (AF_READ|AF_MISSING)) == AF_READ) { unmark_as_read(); #ifdef VERBOSE IF(verbose) fputs("\tMarked unread",stdout); #endif } } else if (ch == 'M') { delay_unmark(artp); oneless(artp); #ifdef VERBOSE IF(verbose) fputs("\tWill return",stdout); #endif } else if (ch == '=') { printf("\t%s",fetchsubj(art,FALSE)); #ifdef VERBOSE IF(verbose) ; ELSE #endif putchar('\n') FLUSH; /* ghad! */ } else if (ch == 'C') { #ifdef ASYNC_PARSE printf("\t%sancelled",(cancel_article() ? "Not c" : "C")); #else notincl("C"); return -1; #endif } else if (ch == '%') { #ifdef ASYNC_PARSE char tmpbuf[512]; if (one_command) interp(tmpbuf, (sizeof tmpbuf), cmdlst); else cmdlst = dointerp(tmpbuf, (sizeof tmpbuf), cmdlst, ":") - 1; perform_cnt--; if (perform(tmpbuf,FALSE)) return -1; #else notincl("%"); return -1; #endif } else if (index("!&sSwWe|",ch)) { if (one_command) strcpy(buf,cmdlst); else cmdlst = cpytill(buf,cmdlst,':') - 1; /* we now have the command in buf */ if (ch == '!') { escapade(); #ifdef VERBOSE IF(verbose) fputs("\tShell escaped",stdout); #endif } else if (ch == '&') { switcheroo(); #ifdef VERBOSE IF(verbose) if (buf[1] && buf[1] != '&') fputs("\tSwitched",stdout); #endif } else { putchar('\t'); save_article(); #ifdef VERBOSE IF(verbose) ; ELSE #endif putchar('\n') FLUSH; } } else { printf("\t???%s\n",cmdlst); return -1; } #ifdef VERBOSE fflush(stdout); #endif if (one_command) break; } if (toplevel) { #ifdef VERBOSE IF(verbose) putchar('\n') FLUSH; #endif } if (int_count) { int_count = 0; return -1; } return 0; } trn-3.6/util.c0000664000000000000000000002126505654303443010212 0ustar /* $Id: util.c,v 3.0 1992/02/23 21:25:39 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "final.h" #include "term.h" #include "nntp.h" #include "nntpauth.h" #include "INTERN.h" #include "util.h" void util_init() { ; } /* fork and exec a shell command */ int doshell(shl,s) char *s, *shl; { int status, pid, w; char *shell; bool mouse_flag; /* if TRUE, mouse tracking was on */ mouse_flag = xmouse_status; xmouse_off(); #ifdef SIGTSTP sigset(SIGTSTP,SIG_DFL); sigset(SIGTTOU,SIG_DFL); sigset(SIGTTIN,SIG_DFL); #endif if (shl != Nullch) shell = shl; else if ((shell = getenv("SHELL")) == Nullch || !*shell) shell = PREFSHELL; if ((pid = vfork()) == 0) { #ifdef USE_NNTP int i; /* This is necessary to keep bourne shell from puking */ for (i = 3; i < 10; ++i) { #ifdef USE_GENAUTH if (cookiefd != -1) { if (i == fileno(ser_rd_fp) || i == fileno(ser_wr_fp) || i == cookiefd) continue; } #endif close(i); } #endif /* USE_NNTP */ if (*s) execl(shell, shell, "-c", s, Nullch); else execl(shell, shell, Nullch, Nullch, Nullch); _exit(127); } signal(SIGINT, SIG_IGN); #ifdef SIGQUIT signal(SIGQUIT, SIG_IGN); #endif termlib_reset(); waiting = TRUE; while ((w = wait(&status)) != pid) if (w == -1 && errno != EINTR) break; if (w == -1) status = -1; termlib_init(); if (mouse_flag) xmouse_on(); waiting = FALSE; sigset(SIGINT, int_catcher); /* always catch interrupts */ #ifdef SIGQUIT signal(SIGQUIT, SIG_DFL); #endif #ifdef SIGTSTP sigset(SIGTSTP,stop_catcher); sigset(SIGTTOU,stop_catcher); sigset(SIGTTIN,stop_catcher); #endif return status; } static char nomem[] = "trn: out of memory!\n"; /* paranoid version of malloc */ char * safemalloc(size) MEM_SIZE size; { char *ptr; ptr = malloc(size ? size : (MEM_SIZE)1); if (ptr == Nullch) { fputs(nomem,stdout) FLUSH; sig_catcher(0); } return ptr; } /* paranoid version of realloc. If where is NULL, call malloc */ char * saferealloc(where,size) char *where; MEM_SIZE size; { char *ptr; if (!where) ptr = malloc(size ? size : (MEM_SIZE)1); else ptr = realloc(where, size ? size : (MEM_SIZE)1); if (!ptr) { fputs(nomem,stdout) FLUSH; sig_catcher(0); } return ptr; } /* safe version of string copy */ char * safecpy(to,from,len) char *to; register char *from; register int len; { register char *dest = to; if (from != Nullch) for (len--; len && (*dest++ = *from++); len--) ; *dest = '\0'; return to; } /* safe version of string concatenate, with \n deletion and space padding */ char * safecat(to,from,len) char *to; register char *from; register int len; { register char *dest = to; len--; /* leave room for null */ if (*dest) { while (len && *dest++) len--; if (len) { len--; *(dest-1) = ' '; } } if (from != Nullch) while (len && (*dest++ = *from++)) len--; if (len) dest--; if (*(dest-1) == '\n') dest--; *dest = '\0'; return to; } /* copy a string up to some (non-backslashed) delimiter, if any */ char * cpytill(to,from,delim) register char *to, *from; register int delim; { for (; *from; from++,to++) { if (*from == '\\' && from[1] == delim) from++; else if (*from == delim) break; *to = *from; } *to = '\0'; return from; } /* effective access */ #ifdef SETUIDGID int eaccess(filename, mod) char *filename; int mod; { int protection, euid; mod &= 7; /* remove extraneous garbage */ if (stat(filename, &filestat) < 0) return -1; euid = geteuid(); if (euid == ROOTID) return 0; protection = 7 & (filestat.st_mode >> (filestat.st_uid == euid ? 6 : (filestat.st_gid == getegid() ? 3 : 0) )); if ((mod & protection) == mod) return 0; errno = EACCES; return -1; } #endif /* * Get working directory */ #ifndef HAS_GETWD #ifdef HAS_GETCWD char * getwd(np) char *np; { char *ret; extern char *getcwd(); if ((ret = getcwd(np,512)) == Nullch) { printf("Cannot determine current working directory!\n") FLUSH; finalize(1); } return ret; } #else char * getwd(np) char *np; { FILE *popen(); FILE *pipefp; if ((pipefp = popen("/bin/pwd","r")) == Nullfp) { printf("Can't run /bin/pwd\n") FLUSH; finalize(1); } fgets(np,512,pipefp); np[strlen(np)-1] = '\0'; /* wipe out newline */ if (pclose(pipefp) == EOF) { printf("Failed to run /bin/pwd\n") FLUSH; finalize(1); } return np; } #endif #endif /* just like fgets but will make bigger buffer as necessary */ char * get_a_line(buffer,buffer_length,realloc_ok,fp) char *buffer; register int buffer_length; bool_int realloc_ok; FILE *fp; { register int bufix = 0; register int nextch; do { if (bufix >= buffer_length) { buffer_length *= 2; if (realloc_ok) { /* just grow in place, if possible */ buffer = saferealloc(buffer,(MEM_SIZE)buffer_length+1); } else { char *tmp = safemalloc((MEM_SIZE)buffer_length+1); strncpy(tmp,buffer,buffer_length/2); buffer = tmp; realloc_ok = TRUE; } } if ((nextch = getc(fp)) == EOF) return Nullch; buffer[bufix++] = (char)nextch; } while (nextch && nextch != '\n'); buffer[bufix] = '\0'; len_last_line_got = bufix; buflen_last_line_got = buffer_length; return buffer; } /* copy a string to a safe spot */ char * savestr(str) char *str; { register char *newaddr = safemalloc((MEM_SIZE)(strlen(str)+1)); strcpy(newaddr,str); return newaddr; } int makedir(dirname,nametype) register char *dirname; int nametype; { #ifdef MAKEDIR register char *end; register char *s; char tmpbuf[1024]; register char *tbptr = tmpbuf+5; for (end = dirname; *end; end++) ; /* find the end */ if (nametype == MD_FILE) { /* not to create last component? */ for (--end; end != dirname && *end != '/'; --end) ; if (*end != '/') return 0; /* nothing to make */ *end = '\0'; /* isolate file name */ } strcpy(tmpbuf,"mkdir"); s = end; for (;;) { if (stat(dirname,&filestat) >= 0 && S_ISDIR(filestat.st_mode)) { /* does this much exist as a dir? */ *s = '/'; /* mark this as existing */ break; } s = rindex(dirname,'/'); /* shorten name */ if (!s) /* relative path! */ break; /* hope they know what they are doing */ *s = '\0'; /* mark as not existing */ } for (s=dirname; s <= end; s++) { /* this is grody but efficient */ if (!*s) { /* something to make? */ sprintf(tbptr," %s",dirname); tbptr += strlen(tbptr); /* make it, sort of */ *s = '/'; /* mark it made */ } } if (nametype == MD_DIR) /* don't need final slash unless */ *end = '\0'; /* a filename follows the dir name */ return (tbptr==tmpbuf+5 ? 0 : doshell(sh,tmpbuf)); /* exercise our faith */ #else sprintf(cmd_buf,"%s %s %d", filexp(DIRMAKER), dirname, nametype); return doshell(sh,cmd_buf); #endif } void notincl(feature) char *feature; { printf("\nNo room for feature \"%s\" on this machine.\n",feature) FLUSH; } char * getval(nam,def) char *nam,*def; { char *val; if ((val = getenv(nam)) == Nullch || !*val) val = def; return val; } /* grow a static string to at least a certain length */ void growstr(strptr,curlen,newlen) char **strptr; int *curlen; int newlen; { if (newlen > *curlen) { /* need more room? */ if (*curlen) *strptr = saferealloc(*strptr,(MEM_SIZE)newlen); else *strptr = safemalloc((MEM_SIZE)newlen); *curlen = newlen; } } void setdef(buffer,dflt) char *buffer,*dflt; { #ifdef STRICTCR if (*buffer == ' ') #else if (*buffer == ' ' || *buffer == '\n') #endif { if (*dflt == '^' && isupper(dflt[1])) pushchar(Ctl(dflt[1])); else pushchar(*dflt); getcmd(buffer); } } void safelink(old, new) char *old, *new; { #if 0 extern int sys_nerr; extern char *sys_errlist[]; #endif if (link(old,new)) { printf("Can't link backup (%s) to .newsrc (%s)\n", old, new) FLUSH; #if 0 if (errno>0 && errnotm_wday < 0 || ts->tm_wday > 6) putstr = "?"; else if (ch == 'A') putstr = day_name[ts->tm_wday]; else sprintf(tmpbuf, "%-.3s", day_name[ts->tm_wday]); break; case 'B': case 'b': case 'h': if (ts->tm_mon < 0 || ts->tm_mon > 11) putstr = "?"; else if (ch == 'B') putstr = mth_name[ts->tm_mon]; else sprintf(tmpbuf, "%-.3s", mth_name[ts->tm_mon]); break; case 'C': strftime(tmpbuf, sizeof tmpbuf, "%A, %B %e, %Y", ts); break; case 'c': strftime(tmpbuf, sizeof tmpbuf, "%x %X", ts); break; case 'D': #ifndef LOCAL_DDMMYY case 'x': #endif strftime(tmpbuf, sizeof tmpbuf, "%m/%d/%y", ts); break; case 'd': sprintf(tmpbuf, "%02d", ts->tm_mday); break; case 'e': /* day of month, blank padded */ sprintf(tmpbuf, "%2d", ts->tm_mday); break; case 'H': sprintf(tmpbuf, "%02d", ts->tm_hour); break; case 'I': { int n; n = ts->tm_hour; if (n == 0) n = 12; else if (n > 12) n -= 12; sprintf(tmpbuf, "%02d", n); break; } case 'j': sprintf(tmpbuf, "%03d", ts->tm_yday + 1); break; case 'm': sprintf(tmpbuf, "%02d", ts->tm_mon + 1); break; case 'M': sprintf(tmpbuf, "%02d", ts->tm_min); break; case 'p': putstr = (ts->tm_hour < 12) ? "AM" : "PM"; break; case 'r': strftime(tmpbuf, sizeof tmpbuf, "%I:%M:%S %p", ts); break; case 'R': strftime(tmpbuf, sizeof tmpbuf, "%H:%M", ts); break; case 'S': sprintf(tmpbuf, "%02d", ts->tm_sec); break; case 'T': #ifndef LOCAL_DOTTIME case 'X': #endif strftime(tmpbuf, sizeof tmpbuf, "%H:%M:%S", ts); break; case 'U': /* week of year - starting Sunday */ sprintf(tmpbuf, "%02d", (ts->tm_yday - ts->tm_wday + 10) / 7); break; case 'W': /* week of year - starting Monday */ sprintf(tmpbuf, "%02d", (ts->tm_yday - ((ts->tm_wday + 6) % 7) + 10) / 7); break; case 'w': sprintf(tmpbuf, "%d", ts->tm_wday); break; case 'y': sprintf(tmpbuf, "%02d", ts->tm_year % 100); break; #ifdef LOCAL_DOTTIME case 'X': strftime(tmpbuf, sizeof tmpbuf, "%H.%M.%S", ts); break; #endif #ifdef LOCAL_DDMMYY case 'x': strftime(tmpbuf, sizeof tmpbuf, "%d/%m/%y", ts); break; #endif case 'Y': sprintf(tmpbuf, "%d", ts->tm_year + 1900); break; case 'Z': #ifdef HAS_FTIME # ifdef TM_ZONE sprintf(tmpbuf, "%s", ts->tm_zone); # else if (*tznm == '\0') { char *timezone(); struct timeval tv; struct timezone tz; (void) gettimeofday(&tv, &tz); strcpy(tznm, timezone(tz.tz_minuteswest, ts->tm_isdst)); } sprintf(tmpbuf, "%s", tznm); # endif #else sprintf(tmpbuf, "%s", tzname[ts->tm_isdst]); #endif break; case '%': case '\0': putstr = "%"; break; case 'n': /* same as \n */ putstr = "\n"; break; case 't': /* same as \t */ putstr = "\t"; break; default: sprintf(tmpbuf, "%%%c", ch); break; } len = strlen(putstr); num += len; if (num > maxsize) { len -= num - maxsize; num = 0; ch = '\0'; } strncpy(str, putstr, len); str += len; if (!ch) break; } *str = '\0'; return num; } trn-3.6/ngsrch.c0000664000000000000000000000645505626515163010530 0ustar /* $Id: ngsrch.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "rcstuff.h" #include "final.h" #include "search.h" #include "trn.h" #include "util.h" #include "term.h" #include "rcln.h" #include "INTERN.h" #include "ngsrch.h" #ifdef NGSEARCH COMPEX ngcompex; #endif void ngsrch_init() { #ifdef NGSEARCH init_compex(&ngcompex) #endif ; } #ifdef NGSEARCH int ng_search(patbuf,get_cmd) char *patbuf; /* if patbuf != buf, get_cmd must */ int get_cmd; /* be set to FALSE!!! */ { char *pattern; /* unparsed pattern */ register char cmdchr = *patbuf; /* what kind of search? */ register char *s; bool backward = cmdchr == '?'; /* direction of search */ int_count = 0; if (get_cmd && buf == patbuf) if (!finish_command(FALSE)) /* get rest of command */ return NGS_ABORT; for (pattern = patbuf+1; *pattern == ' '; pattern++) ; if (*pattern) { ng_doread = FALSE; } s = rindex(pattern,cmdchr); if (s != Nullch && *(s-1) != '\\') { *s++ = '\0'; if (index(s,'r') != Nullch) ng_doread = TRUE; } if ((s = ng_comp(&ngcompex,pattern,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; return NGS_ERROR; } fputs("\nSearching...",stdout) FLUSH; /* give them something to read */ fflush(stdout); for (;;) { if (int_count) { int_count = 0; return NGS_INTR; } if (backward) { if (ng > 0) --ng; else ng = nextrcline; } else { if (ng >= nextrcline) ng = 0; else ++ng; } if (ng == current_ng) return NGS_NOTFOUND; if (ng == nextrcline || toread[ng] < TR_NONE || !ng_wanted()) continue; if (toread[ng] == TR_NONE) set_toread(ng); if (toread[ng] > TR_NONE) return NGS_FOUND; else if (toread[ng] == TR_NONE) if (ng_doread) return NGS_FOUND; else printf("\n[0 unread in %s -- skipping]",rcline[ng]) FLUSH; } } bool ng_wanted() { return execute(&ngcompex,rcline[ng]) != Nullch; } #endif /* NGSEARCH */ #ifdef NGSORONLY char * ng_comp(compex,pattern,RE,fold) COMPEX *compex; char *pattern; bool_int RE; bool_int fold; { char ng_pattern[128]; register char *s = pattern, *d = ng_pattern; if (!*s) { if (compile(compex, "", RE, fold)) return "No previous search pattern"; else return Nullch; /* reuse old pattern */ } for (; *s; s++) { if (*s == '.') { *d++ = '\\'; *d++ = *s; } else if (*s == '?') { *d++ = '.'; } else if (*s == '*') { *d++ = '.'; *d++ = *s; } #if OLD_RN_WAY else if (strnEQ(s,"all",3)) { *d++ = '.'; *d++ = '*'; s += 2; } #endif else *d++ = *s; } *d = '\0'; return compile(compex,ng_pattern,RE,fold); } #endif trn-3.6/rt-select.h0000664000000000000000000000334605654303437011147 0ustar /* $Id: rt-select.h,v 3.0 1992/12/14 00:14:15 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #define MAX_SEL 64 EXT bool sel_rereading INIT(0); EXT int sel_mode INIT(0); EXT int sel_threadmode INIT(0); #define SM_THREAD 0 #define SM_SUBJECT 1 #define SM_ARTICLE 2 EXT char *sel_mode_string; EXT int sel_sort INIT(0); EXT int sel_artsort INIT(4); EXT int sel_threadsort INIT(0); #define SS_DATE 0 #define SS_SUBJECT 1 #define SS_AUTHOR 2 #define SS_COUNT 3 #define SS_NUMBER 4 #define SS_GROUPS 5 EXT char *sel_sort_string; EXT int sel_direction INIT(1); EXT bool sel_exclusive INIT(FALSE); EXT int sel_mask INIT(1); EXT bool selected_only INIT(FALSE); EXT ART_UNREAD selected_count INIT(0); EXT int selected_subj_cnt INIT(0); EXT int added_articles INIT(0); #define VOIDPTR void struct sel_item { VOIDPTR *ptr; int line; int sel; }; EXT char *sel_chars; EXT struct sel_item sel_items[MAX_SEL]; EXT int sel_item_index; EXT int sel_item_cnt; EXT int sel_total_arts; EXT int sel_prior_arts; EXT int sel_page_arts; EXT int sel_page_cnt; EXT int sel_max_cnt; EXT int sel_line; EXT int sel_last_line; EXT bool sel_at_end; EXT ARTICLE **sel_page_app; EXT SUBJECT *sel_page_sp; EXT ARTICLE **sel_next_app; EXT SUBJECT *sel_next_sp; EXT ARTICLE *sel_last_ap; EXT SUBJECT *sel_last_sp; char do_selector _((char_int)); #define DS_POS 0 #define DS_ASK 1 #define DS_UPDATE 2 #define DS_DISPLAY 3 #define DS_RESTART 4 #define DS_STATUS 5 #define DS_QUIT 6 #ifdef DOINIT static void empty_complaint _((void)); static void sel_cleanup _((void)); static int sel_command _((char_int)); #endif trn-3.6/config.sh.orig0000775000000000000000000001026407205300124011614 0ustar #!/bin/sh # # This file was produced by running the Configure script. It holds all the # definitions figured out by Configure. Should you modify one of these values, # do not forget to propagate your changes by running "Configure -der". You may # instead choose to run each of the .SH files by yourself, or "Configure -S". # # Configuration time: Thu Sep 7 00:47:33 BST 2000 # Configured by: cjw44 # Target system: linux pick 2.2.14 #1 wed jan 12 13:49:58 gmt 2000 i686 unknown d_bsd='undef' d_eunice='undef' d_xenix='undef' eunicefix=':' Mcc='Mcc' awk='/usr/bin/awk' basename='/usr/bin/basename' bash='/bin/bash' cat='/bin/cat' cp='/bin/cp' cpp='/usr/bin/cpp' csh='/usr/bin/csh' date='/bin/date' diff='/usr/bin/diff' echo='/bin/echo' ed='/bin/ed' egrep='/bin/egrep' expr='/usr/bin/expr' grep='/bin/grep' inews='/usr/bin/inews' ispell='/usr/bin/ispell' ksh='/usr/bin/ksh' less='/usr/bin/less' lint='' mail='/usr/bin/mail' metamail='/usr/bin/metamail' mhn='mhn' mkdir='/bin/mkdir' more='/bin/more' munpack='/usr/bin/munpack' mv='/bin/mv' nroff='/usr/bin/nroff' pg='pg' rm='/bin/rm' rmail='/usr/sbin/rmail' sed='/bin/sed' sendmail='/usr/sbin/sendmail' smail='smail' sort='/usr/bin/sort' spell='/usr/bin/spell' tail='/usr/bin/tail' test='test' tr='/usr/bin/tr' uname='/bin/uname' uniq='/usr/bin/uniq' uuname='uuname' vi='/usr/bin/vi' vspell='' who='' hint='previous' myuname='linux pick 2.2.14 #1 wed jan 12 13:49:58 gmt 2000 i686 unknown ' srcdir='.' vincludes='' vpath='.' Id='$Id' Log='$Log' active='none' acttimes='nntp' d_acttimes='define' myactive='.falseactive' bin='/usr/lib/trn' binexp='/usr/lib/trn' installbin='/usr/lib/trn' cc='gcc' ccflags='' cppflags='' ldflags='-s' lkflags='' optimize='-O3' cf_by='cjw44' cf_time='Thu Sep 7 00:47:33 BST 2000' contains='grep' cpplast='-' cppminus='-' cpprun='gcc -E' cppstdin='gcc -E' d_ftime='define' aphostcmd='' d_gethname='undef' d_phostcmd='undef' d_uname='undef' d_getpwent='define' d_getcwd='undef' d_getwd='define' d_havetlib='define' termlib='-lcurses' d_ignoreorg='undef' d_internet='define' d_memcmp='define' d_memcpy='define' d_memset='define' d_mimeshow='undef' d_mimestore='undef' mimeshow="none" mimestore="none" d_newsadm='define' newsadmin='news' d_nntp='define' d_xdata='define' d_genauth='define' servername='/etc/news/server' d_nolnbuf='undef' d_normsig='define' jobslib='' d_novoid='undef' void='' d_portable='undef' d_rdchk='undef' d_rename='define' d_sigblock='define' d_sighold='define' d_sizet='undef' d_strccmp='define' d_strchr='define' d_strftime='define' strftimec='' strftimeo='' d_libndir='undef' d_usendir='undef' libndir='' ndirc='' ndiro='' d_vfork='define' d_voidsig='define' signal_t='void' defeditor='/usr/bin/emacs' filexp='/usr/share/trn/filexp' d_dirnamlen='' i_dirent='define' i_ptem='undef' i_stdlib='define' i_string='define' strings='/usr/include/string.h' i_sysdir='define' d_voidtty='' i_bsdioctl='' i_sysfilio='undef' i_sysioctl='define' i_syssockio='' i_sysndir='undef' i_sgtty='undef' i_termio='undef' i_termios='define' i_systime='define' i_time='undef' i_unistd='define' i_vfork='undef' ispell_options='' ispell_prg='/usr/bin/ispell' libc='/usr/lib/libc.a' plibpth='/usr/ccs/lib /usr/lib /usr/ucblib /usr/local/lib' xlibpth='/usr/lib/386 /lib/386' libs=' ' citydist='none' cntrydist='none' contdist='none' locdist='none' multistatedist='none' orgdist='none' statedist='none' mailer='/usr/sbin/sendmail' mailfile='/var/spool/mail/%L' installmansrc='/usr/share/man/man1' manext='1' mansrc='/usr/share/man/man1' mansrcexp='/usr/share/man/man1' mboxchar='F' c='' n='-n' d_berknames='define' d_passnames='define' d_usgnames='undef' nametype='bsd' inewsloc='inews' newslib='/tmp' newslibexp='/tmp' newsspool='/tmp' orgname='/etc/news/organization' package='trn' spackage='Trn' pager='/usr/bin/pager' phost='/etc/news/whoami' phostcmd='' prefshell='/bin/sh' installprivlib='/usr/share/trn' privlib='/usr/share/trn' privlibexp='/usr/share/trn' rootid='0' sharpbang='#!' shsharp='true' spitshell='cat' startsh='#!/bin/sh' sysman='/usr/man/man1' d_usemt='define' d_useov='define' overviewdir='/tmp' threaddir='/tmp' trn_init='FALSE' trn_select='TRUE' nm_opt='' runnm='true' usenm='true' incpath='' mips='' mips_type='' usrinc='/usr/include' PATCHLEVEL="3.6 (20 Nov 1994)" CONFIG=true trn-3.6/rt-ov.c0000664000000000000000000001702105664225164010303 0ustar /* $Id: rt-ov.c,v 3.0 1992/12/14 00:14:13 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "trn.h" #include "cache.h" #include "bits.h" #include "head.h" #include "ngdata.h" #include "util.h" #include "ng.h" #include "nntp.h" #include "term.h" #include "final.h" #include "hash.h" #include "rthread.h" #include "rt-process.h" #include "rt-util.h" #include "overview.h" #include "INTERN.h" #include "rt-ov.h" bool ov_init() { #ifdef USE_XOVER /* Check if the server is XOVER compliant (we're not in a group, BTW) */ nntp_command("XOVER"); nntp_check(FALSE); if (atoi(ser_line) == NNTP_BAD_COMMAND_VAL) return FALSE; /* paranoia reins supreme */ if (*ser_line == NNTP_CLASS_OK) { do { while (nntp_gets(ser_line, sizeof ser_line) == 0) { /* A line w/o a newline is too long to be the end of the ** list, so grab the rest of this line and try again. */ while (nntp_gets(ser_line, sizeof ser_line) == 0) ; } } while (!NNTP_LIST_END(ser_line)); } #endif return TRUE; } #ifndef OV_XREFS # ifdef OV_OTHERS_HAS_XREFS bool ov_files_have_xrefs = TRUE; /* set once per session */ # else bool ov_files_have_xrefs = FALSE; /* set once per session */ # endif #endif #ifndef USE_XOVER FILE *ov_in; #endif /* Process the data in the group's news-overview file. */ bool ov_data(first, last, cheating) ART_NUM first, last; bool_int cheating; { register ARTICLE *ap; ART_NUM artnum, an; char *line, *last_buf = buf; MEM_SIZE last_buflen = LBUFLEN; int cachemask; bool success = TRUE; #ifdef USE_XOVER ART_NUM real_first = first, real_last = last; setspin(cheating? SPIN_BACKGROUND : SPIN_FOREGROUND); beginning: for (ap = article_ptr(first); first <= last && (ap->flags & AF_CACHED); ) first++, ap++; if (first > last) goto exit; if (last - first > OV_CHUNK_SIZE + OV_CHUNK_SIZE/2 - 1) last = first + OV_CHUNK_SIZE - 1; for (ap = article_ptr(last); last > first && (ap->flags & AF_CACHED); ) last--, ap--; sprintf(ser_line, "XOVER %ld-%ld", (long)first, (long)last); nntp_command(ser_line); if (nntp_check(FALSE) != NNTP_CLASS_OK) { success = FALSE; goto exit; } #ifdef VERBOSE IF(verbose) if (!ov_opened) printf("\nGetting overview file."), fflush(stdout); #endif #else /* !USE_XOVER */ if (!ov_opened) { if ((ov_in = fopen(ov_name(ngname), "r")) == Nullfp) { return FALSE; } #ifdef VERBOSE IF(verbose) printf("\nReading overview file."), fflush(stdout); #endif } setspin(cheating? SPIN_BACKGROUND : SPIN_FOREGROUND); #endif /* !USE_XOVER */ ov_opened = TRUE; artnum = first-1; for (;;) { #ifdef USE_XOVER line = nntp_get_a_line(last_buf, last_buflen, last_buf != buf); if (!line || NNTP_LIST_END(line)) break; #else line = get_a_line(last_buf, last_buflen, last_buf != buf, ov_in); if (!line) break; #endif last_buf = line; last_buflen = buflen_last_line_got; artnum = atol(line); spin(100); if (artnum < first) continue; if (artnum > last) { artnum = last; #ifdef USE_XOVER continue; #else break; #endif } if ((ap = ov_parse(line, artnum)) != Nullart) { #ifndef OV_XREFS if (ov_files_have_xrefs) { if (!ap->xrefs) ap->xrefs = nullstr; } else if (ap->xrefs) { register ART_UNREAD i; register ARTICLE *ap2; ap2 = article_ptr(first); for (i = artnum-first; i; ap2++, i--) ap2->xrefs = nullstr; ov_files_have_xrefs = TRUE; } #endif if (ThreadedGroup) { if (valid_article(ap)) thread_article(ap); } else if (!(ap->flags & AF_CACHED)) cache_article(ap); check_poster(ap); } #ifdef USE_XOVER if (int_count) { int_count = 0; success = FALSE; } #else if (int_count) { int_count = 0; success = FALSE; break; } if (cheating) { if (input_pending()) { success = FALSE; break; } if (curr_artp != sentinel_artp) { pushchar('\f' | 0200); success = FALSE; break; } } #endif /* !USE_XOVER */ } cachemask = (ThreadedGroup? AF_THREADED : AF_CACHED); #ifdef USE_XOVER an = real_first; #else an = first; #endif for (ap = article_ptr(an); an <= artnum; an++, ap++) { if (!(ap->flags & cachemask)) { #ifdef USE_NNTP onemissing(ap); #else (void) parseheader(an); #endif } } if (artnum > last_cached && artnum >= first) last_cached = artnum; #ifdef USE_XOVER exit: if (int_count || !success) { int_count = 0; success = FALSE; } else if (cheating && curr_artp != sentinel_artp) { pushchar('\f' | 0200); success = FALSE; } else if (last < real_last) { if (!cheating || !input_pending()) { first = last+1; last = real_last; goto beginning; } success = FALSE; } if (success && real_first <= first_cached) { first_cached = real_first; cached_all_in_range = TRUE; } #else if (success && first <= first_cached) { first_cached = first; cached_all_in_range = TRUE; } if (!cheating) fseek(ov_in, 0L, 0); /* rewind it for the cheating phase */ #endif setspin(SPIN_POP); if (last_buf != buf) free(last_buf); return success; } static ARTICLE * ov_parse(line, artnum) register char *line; ART_NUM artnum; { register int nf; register ARTICLE *article; char *fields[OV_OTHERS+1], *cp; article = article_ptr(artnum); if (article->flags & AF_THREADED) return Nullart; if (len_last_line_got > 0 && line[len_last_line_got-1] == '\n') #ifdef USE_NNTP if (len_last_line_got > 1 && line[len_last_line_got-2] == '\r') line[len_last_line_got-2] = '\0'; else #endif line[len_last_line_got-1] = '\0'; cp = line; for (nf = 0; ; nf++) { fields[nf] = cp; if (nf == OV_OTHERS) break; if (!(cp = index(cp, '\t'))) { if (nf < OV_OTHERS-1) /* only "others" field is optional */ return Nullart; /* skip this line */ break; } *cp++ = '\0'; } if (!article->subj) set_subj_line(article, fields[OV_SUBJ], strlen(fields[OV_SUBJ])); if (!article->msgid) article->msgid = savestr(fields[OV_MSGID]); if (!article->from) article->from = savestr(fields[OV_FROM]); if (!article->date) article->date = parsedate(fields[OV_DATE]); references = fields[OV_REFS]; #ifdef OV_XREFS # ifdef OV_LAX_XREFS if (!strncasecmp("xref: ", fields[OV_XREFS], 6)) article->xrefs = savestr(fields[OV_XREFS]+6); else article->xrefs = savestr(fields[OV_XREFS]); # else article->xrefs = savestr(fields[OV_XREFS]); # endif #else /* check the "others" field for an optional xrefs header */ if (nf == OV_OTHERS && !article->xrefs) { register char *fld; cp = fields[OV_OTHERS]; while (cp && *cp) { fld = cp; if ((cp = index(cp, '\t')) != Nullch) *cp++ = '\0'; if (!strncasecmp("xref: ", fld, 6)) { article->xrefs = savestr(fld+6); break; } } } #endif return article; } #ifndef USE_XOVER /* Change a newsgroup name into the name of the overview data file. We ** subsitute any '.'s in the group name into '/'s, prepend the path, and ** append the '/.overview' or '.ov') on to the end. */ static char * ov_name(group) char *group; { register char *cp; cp = strcpy(buf, overviewdir) + strlen(overviewdir); *cp++ = '/'; strcpy(cp, group); while ((cp = index(cp, '.'))) *cp = '/'; strcat(buf, OV_FILE_NAME); return buf; } #endif void ov_close() { if (ov_opened) { #ifndef USE_XOVER (void) fclose(ov_in); #endif ov_opened = FALSE; } } trn-3.6/INSTALL0000664000000000000000000001362705557021033010117 0ustar Installation Instructions for trn 3.0: 1) Decide what package you want to use to solve your news database needs. There's currently two supported by trn: mthreads and news overview. Mthreads creates smaller files that load faster each time you enter a group, however it is more taxing on your cpu and disk bandwidth because it keeps the files in thread order. Overview files are larger, but they are less taxing on your system because the format is pretty "raw" and the files are maintained by your news software (either a modified C news or INN 1.3 and above). Though they load slower than .thread files, they have subject and author headers that are not truncated (unlike mthreads headers) which means that trn won't have to "freshen" these headers during its idle time. The final choice is to not store a database. If you go this route it will take significantly longer to start up a group in threaded mode, but you could chose to read one or more groups unthreaded. 2) If you are going to make a version of trn that uses NNTP to get its news remotely, be sure you have installed some version of inews that sends articles to your news server. The reference NNTP (v1.5.11) comes with a version of inews that you can use, and C news and INN both have versions that can be used remotely. 3) Run Configure. This will figure out various things about your system. Some things Configure will figure out for itself, other things it will ask you about. It will then proceed to make config.h, config.sh, the Makefile, and a bunch of shell scripts. You might possibly have to trim # comments from the front of Configure if your sh doesn't handle them, but all other # comments will be taken care of. NOTE: This package allows the build to be performed in a directory other than where the sources are. You will need a "make" that supports VPATH to do this. Just create a directory where you want to do the build, cd into it, and run "path/Configure" from there where "path" is either a relative or absolute path reference to the trn source directory. 4) Glance through config.h and common.h to make sure system dependencies are correct. Most of them should have been taken care of by running the Configure script. If you have any additional changes to make to the C definitions, they can be done in the Makefile, in config.h, or in common.h. If you have strange mailboxes on your system you should modify mbox.saver to correctly append an article to a mailbox or folder. If you are on a machine with limited address space, you probably don't want to be using trn. Feel free to give it a try, though -- there are some defines in common.h that can be turned off to try to make trn fit (see the System Dependencies section). You might run a "make depend" afterward just to be safe. 5) make This will attempt to make trn in the current directory. 6) make install This will put trn, newsetup, newsgroups, Pnews, Rnmail, trn-artchk, and nntplist into a public directory (normally /usr/local/bin), and put a number of files into the private trn library (e.g. /usr/local/lib/trn). It also tries to put the trn man page in a reasonable place. One of the installed scripts is "newsetup", which creates a .newsrc for a user that doesn't have one. This script reads the subscriptions file from your news library directory or sends a LIST SUBSCRIPTIONS command to your server to get the default list of newsgroups for a new user. If you don't like this, customize the script as desired. NOTE: if you have an old installation of trn that put artcheck and getactive into the trn library directory, once you ensure that all your installed scripts are using the new files (trn-artchk and nntplist) you can delete the old ones from the lib directory. 7) Read the manual entry before running trn, or at least read the file NEW if you are already familiar with trn. Those that are brand new to trn can get a quick idea of what's different from rn by reading the WHAT'S NEW section of the man page. Also check out the HINTS file for some ways to get the most out of using trn. 8) Try trn, and play with some of the switches (use -x and -X if you told Configure to leave these options off by default). Any options you find that you want to make the default for everyone can be placed into the INIT file in the trn library. Personal defaults can be put in a file and the environment variable TRNINIT defined to point to it. 9) Once trn is running ok, make sure any database software you've installed is going ok and interfacing properly with trn. Read the documentation that comes with the package of your choosing. 10) IMPORTANT! Help save the world! Communicate any problems and suggested patches to Wayne Davison so we can keep the world in sync. If you have a problem, there's someone else out there who either has had or will have the same problem. If the problem affects regular rn, code, I will pass it on to Stan Barber. If possible, send in patches such that the patch program will apply them. Unified or regular context diffs are the best, then normal diffs. Don't send ed scripts--I've probably changed my copy since the version you have. Watch for trn patches in news.software.readers. Patches will always be in the unified context diff format, for application by the patch program. If you don't have a patch program that handles unified context diffs, you'll probably want to get one (such as patch version 12g8). Otherwise, you can use the (included) filter "unipatch", which can be generated with the command "make unipatch". To apply patches, use the command: unipatch newsnews < !GROK!THIS! $eunicefix newsnews # # $Id: newsnews.SH,v 3.0 1991/09/09 20:23:31 davison Trn $ # # This software is Copyright 1991 by Stan Barber. # # Permission is hereby granted to copy, reproduce, redistribute or otherwise # use this software as long as: there is no monetary profit gained # specifically from the use or reproduction of this software, it is not # sold, rented, traded or otherwise marketed, and this copyright notice is # included prominently in any copy made. # # The author make no claims as to the fitness or correctness of this software # for any use whatsoever, and it is provided as is. Any use of this software # is at the user's own risk. trn-3.6/respond.h0000664000000000000000000000220505560330131010673 0ustar /* $Id: respond.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT char *savedest INIT(Nullch); /* value of %b */ EXT char *extractdest INIT(Nullch); /* value of %E */ EXT char *extractprog INIT(Nullch); /* value of %e */ EXT ART_POS savefrom INIT(0); /* value of %B */ EXT char *headname INIT(Nullch); #define SAVE_ABORT 0 #define SAVE_DONE 1 void respond_init _((void)); int save_article _((void)); int cancel_article _((void)); int supersede_article _((void)); void reply _((void)); void forward _((void)); void followup _((void)); void invoke _((char*,char*)); trn-3.6/rcstuff.c0000664000000000000000000006256707424610174010722 0ustar /* $Id: rcstuff.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "cache.h" #include "bits.h" #include "ngdata.h" #include "term.h" #include "final.h" #include "trn.h" #include "intrp.h" #include "only.h" #include "rcln.h" #include "nntp.h" #include "autosub.h" #include "hash.h" #include "INTERN.h" #include "rcstuff.h" char *rcname INIT(Nullch); /* path name of .newsrc file */ char *rctname INIT(Nullch); /* path name of temp .newsrc file */ char *rcbname INIT(Nullch); /* path name of backup .newsrc file */ char *softname INIT(Nullch); /* path name of .rnsoft file */ FILE *rcfp INIT(Nullfp); /* .newsrc file pointer */ static void grow_rc_arrays _((int)); static void parse_rcline _((NG_NUM)); #ifdef HASHNG static HASHTABLE *rc_hash; static int rcline_cmp _((char*,int,HASHDATUM)); static void del_rc_line _((HASHDATUM*,int)); static void ins_rc_line _((HASHDATUM*,int)); #endif bool rcstuff_init() { register NG_NUM newng; register int i; register bool foundany = FALSE; char *some_buf; long length; /* make filenames */ #ifdef USE_NNTP if (getenv("NEWSRC")) printf("NEWSRC environment variable ignored -- use DOTDIR to set the directory.\n") FLUSH; #endif rcname = savestr(filexp(RCNAME)); rctname = savestr(filexp(RCTNAME)); rcbname = savestr(filexp(RCBNAME)); softname = savestr(filexp(SOFTNAME)); /* make sure the .newsrc file exists */ newsrc_check(); /* open .rnsoft file containing soft ptrs to active file */ tmpfp = fopen(softname,"r"); if (tmpfp == Nullfp) writesoft = TRUE; /* allocate memory for rc file globals */ grow_rc_arrays(1500); /* read in the .newsrc file */ for (nextrcline = 0; (some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != Nullch; nextrcline++) /* for each line in .newsrc */ { char tmpbuf[10]; newng = nextrcline; /* get it into a register */ length = len_last_line_got; /* side effect of get_a_line */ if (length <= 1) { /* only a newline??? */ nextrcline--; /* compensate for loop increment */ continue; } if (newng >= maxrcline) /* check for overflow */ grow_rc_arrays(maxrcline + 500); if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch) softptr[newng] = atol(tmpbuf); else softptr[newng] = 0; if (some_buf[--length] == '\n') some_buf[length] = '\0'; /* wipe out newline */ if (checkflag) /* no extra mallocs for -c */ rcline[newng] = some_buf; else if (some_buf == buf) rcline[newng] = savestr(some_buf); /* make semipermanent copy */ else { /*NOSTRICT*/ #ifndef lint some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1)); #endif rcline[newng] = some_buf; } if (*some_buf == ' ' || *some_buf == '\t' || strnEQ(some_buf,"options",7)) { /* non-useful line? */ toread[newng] = TR_JUNK; rcchar[newng] = ' '; rcnums[newng] = 0; continue; } parse_rcline(newng); if (rcchar[newng] == NEGCHAR) { toread[newng] = TR_UNSUB; continue; } /* now find out how much there is to read */ if (!inlist(buf) || (suppress_cn && foundany && !paranoid)) toread[newng] = TR_NONE; /* no need to calculate now */ else set_toread(newng); #ifdef VERBOSE if (!checkflag && softmisses == 1) { softmisses++; /* lie a little */ fputs("(Revising soft pointers -- be patient.)\n",stdout) FLUSH; } #endif if (toread[newng] > TR_NONE) { /* anything unread? */ if (!foundany) { starthere = newng; foundany = TRUE; /* remember that fact*/ } if (suppress_cn) { /* if no listing desired */ if (checkflag) { /* if that is all they wanted */ finalize(1); /* then bomb out */ } } else { #ifdef VERBOSE IF(verbose) printf("Unread news in %-40s %5ld article%s\n", rcline[newng],(long)toread[newng], toread[newng]==TR_ONE ? nullstr : "s") FLUSH; ELSE #endif #ifdef TERSE printf("%s: %ld article%s\n", rcline[newng],(long)toread[newng], toread[newng]==TR_ONE ? nullstr : "s") FLUSH; #endif if (int_count) { countdown = 1; int_count = 0; } if (countdown) { if (!--countdown) { fputs("etc.\n",stdout) FLUSH; if (checkflag) finalize(1); suppress_cn = TRUE; } } } } } fclose(rcfp); /* close .newsrc */ if (tmpfp != Nullfp) fclose(tmpfp); /* close .rnsoft */ if (checkflag) /* were we just checking? */ finalize(foundany); /* tell them what we found */ if (paranoid) cleanup_rc(); #ifdef HASHNG rc_hash = hashcreate((int)nextrcline+50, rcline_cmp); for (i = 0; i < nextrcline; i++) if (toread[i] >= TR_UNSUB) sethash(i); #endif return foundany; } static void parse_rcline(ngnum) NG_NUM ngnum; { char *s; int len; for (s=rcline[ngnum]; *s && *s!=':' && *s!=NEGCHAR && !isspace(*s); s++) ; len = s - rcline[ngnum]; if ((!*s || isspace(*s)) && !checkflag) { #ifndef lint rcline[ngnum] = saferealloc(rcline[ngnum],(MEM_SIZE)len + 3); #endif s = rcline[ngnum] + len; strcpy(s, ": "); } if (*s == ':' && s[1] && s[2] == '0') { rcchar[ngnum] = '0'; s[2] = '1'; } else rcchar[ngnum] = *s; /* salt away the : or ! */ rcnums[ngnum] = (char)len + 1; /* remember where the numbers are */ *s = '\0'; /* null terminate newsgroup name */ } void abandon_ng(ngnum) NG_NUM ngnum; { char *some_buf = Nullch; /* open .oldnewsrc and try to find the prior value for the group. */ if ((rcfp = fopen(rcbname, "r")) != Nullfp) { int length = rcnums[ngnum] - 1; while ((some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != Nullch) { if (len_last_line_got <= 0) continue; some_buf[len_last_line_got-1] = '\0'; /* wipe out newline */ if ((some_buf[length] == ':' || some_buf[length] == NEGCHAR) && strnEQ(rcline[ngnum], some_buf, length)) { break; } if (some_buf != buf) free(some_buf); } fclose(rcfp); } else if (errno != ENOENT) { printf("Unable to open %s.\n", rcbname) FLUSH; return; } if (some_buf == Nullch) { some_buf = rcline[ngnum] + rcnums[ngnum]; if (*some_buf == ' ') some_buf++; *some_buf = '\0'; abs1st[ngnum] = 0; /* force group to be re-calculated */ } else { free(rcline[ngnum]); if (some_buf == buf) { rcline[ngnum] = savestr(some_buf); } else { /*NOSTRICT*/ #ifndef lint some_buf = saferealloc(some_buf, (MEM_SIZE)(len_last_line_got)); #endif /* lint */ rcline[ngnum] = some_buf; } } parse_rcline(ngnum); if (rcchar[ngnum] == NEGCHAR) rcchar[ngnum] = ':'; set_toread(ngnum); } /* try to find or add an explicitly specified newsgroup */ /* returns TRUE if found or added, FALSE if not. */ /* assumes that we are chdir'ed to NEWSSPOOL */ bool get_ng(what,flags) char *what; int flags; { char *ntoforget; #define PBLEN 240 char promptbuf[PBLEN+1]; int autosub; #ifdef VERBOSE IF(verbose) ntoforget = "Type n to forget about this newsgroup.\n"; ELSE #endif #ifdef TERSE ntoforget = "n to forget it.\n"; #endif if (index(what,'/')) { dingaling(); printf("\nBad newsgroup name.\n") FLUSH; check_fuzzy_match: #ifdef EDIT_DISTANCE if (fuzzyGet && (flags & GNG_FUZZY)) { flags &= ~GNG_FUZZY; if (find_close_match()) what = ngname; else return FALSE; } else #endif return FALSE; } set_ngname(what); ng = find_ng(ngname); if (ng == nextrcline) { /* not in .newsrc? */ if (ng >= maxrcline) /* check for overflow */ grow_rc_arrays(maxrcline + 25); if ((softptr[ng] = findact(buf,ngname,strlen(ngname),0L)) < 0) { dingaling(); #ifdef VERBOSE IF(verbose) printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo %s!\n",ngname) FLUSH; #endif if (novice_delays) sleep(2); goto check_fuzzy_match; } if (mode != 'i' || !(autosub = auto_subscribe(ngname))) autosub = addnewbydefault; if (autosub) { if (append_unsub) { printf("(Adding %s to end of your .newsrc %ssubscribed)\n", ngname, (autosub == ADDNEW_SUB) ? "" : "un") FLUSH; ng = add_newsgroup(ngname, autosub); } else { if (autosub == ADDNEW_SUB) { printf("(Subscribing to %s)\n", ngname) FLUSH; ng = add_newsgroup(ngname, autosub); } else { printf("(Ignoring %s)\n", ngname) FLUSH; return FALSE; } } flags &= ~GNG_RELOC; } else { #ifdef VERBOSE IF(verbose) snprintf(promptbuf,PBLEN,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname); ELSE #endif #ifdef TERSE snprintf(promptbuf,PBLEN,"\nSubscribe %s?",ngname); #endif reask_add: in_char(promptbuf,'A',"ynYN"); #ifdef VERIFY printcmd(); #endif putchar('\n') FLUSH; if (*buf == 'h') { #ifdef VERBOSE IF(verbose) printf("Type y or SP to subscribe to %s.\n\ Type Y to subscribe to this and all remaining new groups.\n\ Type N to leave all remaining new groups unsubscribed.\n", ngname) FLUSH; ELSE #endif #ifdef TERSE fputs("y or SP to subscribe, Y to subscribe all new groups, N to unsubscribe all\n",stdout) FLUSH; #endif fputs(ntoforget,stdout) FLUSH; goto reask_add; } else if (*buf == 'n' || *buf == 'q') { if (append_unsub) { ng = add_newsgroup(ngname, NEGCHAR); } return FALSE; } else if (*buf == 'y') { ng = add_newsgroup(ngname, ':'); flags |= GNG_RELOC; } else if (*buf == 'Y') { addnewbydefault = ADDNEW_SUB; if (append_unsub) printf("(Adding %s to end of your .newsrc subscribed)\n", ngname) FLUSH; else printf("(Subscribing to %s)\n", ngname) FLUSH; ng = add_newsgroup(ngname, ':'); flags &= ~GNG_RELOC; } else if (*buf == 'N') { addnewbydefault = ADDNEW_UNSUB; if (append_unsub) { printf("(Adding %s to end of your .newsrc unsubscribed)\n", ngname) FLUSH; ng = add_newsgroup(ngname, NEGCHAR); flags &= ~GNG_RELOC; } else { printf("(Ignoring %s)\n", ngname) FLUSH; return FALSE; } } else { fputs(hforhelp,stdout) FLUSH; settle_down(); goto reask_add; } } } else if (mode == 'i') /* adding new groups during init? */ return FALSE; else if (rcchar[ng] == NEGCHAR) { /* unsubscribed? */ #ifdef VERBOSE IF(verbose) snprintf(promptbuf, PBLEN, "\nNewsgroup %s is unsubscribed -- resubscribe?",ngname) FLUSH; ELSE #endif #ifdef TERSE snprintf(promptbuf,PBLEN,"\nResubscribe %s?",ngname) FLUSH; #endif reask_unsub: in_char(promptbuf,'R',"yn"); #ifdef VERIFY printcmd(); #endif putchar('\n') FLUSH; if (*buf == 'h') { #ifdef VERBOSE IF(verbose) printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH; ELSE #endif #ifdef TERSE fputs("y or SP to resubscribe.\n",stdout) FLUSH; #endif fputs(ntoforget,stdout) FLUSH; goto reask_unsub; } else if (*buf == 'n' || *buf == 'q') { return FALSE; } else if (*buf == 'y') { register char *cp = rcline[ng] + rcnums[ng]; rcchar[ng] = (*cp && cp[1] == '0' ? '0' : ':'); flags &= ~GNG_RELOC; } else { fputs(hforhelp,stdout) FLUSH; settle_down(); goto reask_unsub; } } /* now calculate how many unread articles in newsgroup */ set_toread(ng); #ifdef RELOCATE if (flags & GNG_RELOC) { ng = relocate_newsgroup(ng,-1); if (ng < 0) return FALSE; } #endif return toread[ng] >= TR_NONE; } /* add a newsgroup to the .newsrc file (eventually) */ NG_NUM add_newsgroup(ngn, c) char *ngn; char_int c; { register NG_NUM newng = nextrcline++; /* increment max rcline index */ if (newng >= maxrcline) /* check for overflow */ grow_rc_arrays(maxrcline + 25); rcnums[newng] = strlen(ngn) + 1; rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 2)); strcpy(rcline[newng],ngn); /* and copy over the name */ strcpy(rcline[newng]+rcnums[newng], " "); rcchar[newng] = c; /* subscribe or unsubscribe */ toread[newng] = TR_NONE; /* just for prettiness */ #ifdef HASHNG sethash(newng); /* so we can find it again */ #endif return newng; } #ifdef RELOCATE NG_NUM relocate_newsgroup(ngx,newng) NG_NUM ngx; NG_NUM newng; { char *dflt = (ngx!=current_ng ? "$^.Lq" : "$^Lq"); char *tmprcline; ART_UNREAD tmptoread; char tmprcchar; char tmprcnums; ACT_POS tmpsoftptr; register NG_NUM i; ART_NUM tmpngmax; ART_NUM tmpabs1st; starthere = 0; /* Disable this optimization */ writesoft = TRUE; /* Update soft pointer file */ if (ngx < nextrcline-1) { #ifdef HASHNG if (rc_hash) hashwalk(rc_hash, del_rc_line, ngx); #endif tmprcline = rcline[ngx]; tmptoread = toread[ngx]; tmprcchar = rcchar[ngx]; tmprcnums = rcnums[ngx]; tmpsoftptr = softptr[ngx]; tmpngmax = ngmax[ngx]; tmpabs1st = abs1st[ngx]; for (i=ngx+1; i ngx) current_ng--; if (newng < 0) { reask_reloc: unflush_output(); /* disable any ^O in effect */ #ifdef VERBOSE IF(verbose) printf("\nPut newsgroup where? [%s] ", dflt); ELSE #endif #ifdef TERSE printf("\nPut where? [%s] ", dflt); #endif fflush(stdout); reinp_reloc: eat_typeahead(); getcmd(buf); if (errno || *buf == '\f') { /* if return from stop signal */ goto reask_reloc; /* give them a prompt again */ } setdef(buf,dflt); #ifdef VERIFY printcmd(); #endif if (*buf == 'h') { #ifdef VERBOSE IF(verbose) { printf("\n\n\ Type ^ to put the newsgroup first (position 0).\n\ Type $ to put the newsgroup last (position %d).\n", nextrcline-1); printf("\ Type . to put it before the current newsgroup (position %d).\n", current_ng); printf("\ Type -newsgroup name to put it before that newsgroup.\n\ Type +newsgroup name to put it after that newsgroup.\n\ Type a number between 0 and %d to put it at that position.\n", nextrcline-1); printf("\ Type L for a listing of newsgroups and their positions.\n\ Type q to abort the current action.\n") FLUSH; } ELSE #endif #ifdef TERSE { printf("\n\n\ ^ to put newsgroup first (pos 0).\n\ $ to put last (pos %d).\n", nextrcline-1); printf("\ . to put before current newsgroup (pos %d).\n", current_ng); printf("\ -newsgroup to put before newsgroup.\n\ +newsgroup to put after.\n\ number in 0-%d to put at that pos.\n", nextrcline-1); printf("\ L for list of .newsrc.\n\ q to abort\n") FLUSH; } #endif goto reask_reloc; } else if (*buf == 'q') return -1; else if (*buf == 'L') { putchar('\n') FLUSH; list_newsgroups(); goto reask_reloc; } else if (isdigit(*buf)) { if (!finish_command(TRUE)) /* get rest of command */ goto reinp_reloc; newng = atol(buf); if (newng < 0) newng = 0; if (newng >= nextrcline) return nextrcline-1; } else if (*buf == '^') { putchar('\n') FLUSH; newng = 0; } else if (*buf == '$') { newng = nextrcline-1; } else if (*buf == '.') { putchar('\n') FLUSH; newng = current_ng; } else if (*buf == '-' || *buf == '+') { if (!finish_command(TRUE)) /* get rest of command */ goto reinp_reloc; newng = find_ng(buf+1); if (newng == nextrcline) { fputs("Not found.",stdout) FLUSH; goto reask_reloc; } if (*buf == '+') newng++; } else { printf("\n%s",hforhelp) FLUSH; settle_down(); goto reask_reloc; } } if (newng < nextrcline-1) { #ifdef HASHNG if (rc_hash) hashwalk(rc_hash, ins_rc_line, newng); #endif tmprcline = rcline[nextrcline-1]; tmptoread = toread[nextrcline-1]; tmprcchar = rcchar[nextrcline-1]; tmprcnums = rcnums[nextrcline-1]; tmpsoftptr = softptr[nextrcline-1]; tmpngmax = ngmax[nextrcline-1]; tmpabs1st = abs1st[nextrcline-1]; for (i=nextrcline-2; i>=newng; i--) { rcline[i+1] = rcline[i]; toread[i+1] = toread[i]; rcchar[i+1] = rcchar[i]; rcnums[i+1] = rcnums[i]; softptr[i+1] = softptr[i]; ngmax[i+1] = ngmax[i]; abs1st[i+1] = abs1st[i]; } rcline[newng] = tmprcline; toread[newng] = tmptoread; rcchar[newng] = tmprcchar; rcnums[newng] = tmprcnums; softptr[newng] = tmpsoftptr; ngmax[newng] = tmpngmax; abs1st[newng] = tmpabs1st; } if (current_ng >= newng) current_ng++; return newng; } #endif /* List out the newsrc with annotations */ void list_newsgroups() { register NG_NUM i; char tmpbuf[2048]; static char *status[] = {"(READ)","(UNSUB)","(BOGUS)","(JUNK)"}; int cmd; page_init(); print_lines("\ # Status Newsgroup\n\ ",STANDOUT); for (i=0; i= 0) set_toread(i); *(rcline[i] + rcnums[i] - 1) = RCCHAR(rcchar[i]); if (toread[i] > 0) sprintf(tmpbuf,"%3d %6ld ",i,(long)toread[i]); else sprintf(tmpbuf,"%3d %7s ",i,status[-toread[i]]); safecpy(tmpbuf+13,rcline[i],2034); *(rcline[i] + rcnums[i] - 1) = '\0'; if (cmd = print_lines(tmpbuf,NOMARKING)) { if (cmd > 0) pushchar(cmd); break; } } int_count = 0; } /* find a newsgroup in .newsrc */ NG_NUM find_ng(ngnam) char *ngnam; { #ifdef HASHNG HASHDATUM data; assert(rc_hash != 0); data = hashfetch(rc_hash, ngnam, strlen(ngnam)); if (!data.dat_ptr) return nextrcline; /* = notfound */ return data.dat_len; #else /* just do linear search */ register NG_NUM ngnum; for (ngnum = 0; ngnum < nextrcline; ngnum++) { if (strEQ(rcline[ngnum],ngnam)) break; } return ngnum; #endif } void cleanup_rc() { register NG_NUM ngx; register NG_NUM bogosity = 0; #ifdef VERBOSE IF(verbose) fputs("Checking out your .newsrc -- hang on a second...\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Checking .newsrc -- hang on...\n",stdout) FLUSH; #endif for (ngx = 0; ngx < nextrcline; ngx++) { if (toread[ngx] >= TR_UNSUB) { set_toread(ngx); /* this may reset newsgroup */ /* or declare it bogus */ } if (toread[ngx] == TR_BOGUS) bogosity++; } for (ngx = nextrcline-1; ngx >= 0 && toread[ngx] == TR_BOGUS; ngx--) bogosity--; /* discount already moved ones */ if (nextrcline > 5 && bogosity > nextrcline / 2) { fputs( "It looks like the active file is messed up. Contact your news administrator,\n\ ",stdout); fputs( "leave the \"bogus\" groups alone, and they may come back to normal. Maybe.\n\ ",stdout) FLUSH; } #ifdef RELOCATE else if (bogosity) { #ifdef VERBOSE IF(verbose) fputs("Moving bogus newsgroups to the end of your .newsrc.\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Moving boguses to the end.\n",stdout) FLUSH; #endif for (; ngx >= 0; ngx--) { if (toread[ngx] == TR_BOGUS) relocate_newsgroup(ngx,nextrcline-1); } #ifdef DELBOGUS reask_bogus: in_char("Delete bogus newsgroups?", 'D', "ny"); #ifdef VERIFY printcmd(); #endif putchar('\n') FLUSH; if (*buf == 'h') { #ifdef VERBOSE IF(verbose) fputs("\ Type y to delete bogus newsgroups.\n\ Type n or SP to leave them at the end in case they return.\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("y to delete, n to keep\n",stdout) FLUSH; #endif goto reask_bogus; } else if (*buf == 'n' || *buf == 'q') ; else if (*buf == 'y') { while (toread[nextrcline-1] == TR_BOGUS && nextrcline > 0) --nextrcline; /* real tough, huh? */ } else { fputs(hforhelp,stdout) FLUSH; settle_down(); goto reask_bogus; } #endif } #else #ifdef VERBOSE IF(verbose) fputs("You should edit bogus newsgroups out of your .newsrc.\n", stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Edit boguses from .newsrc.\n",stdout) FLUSH; #endif #endif paranoid = FALSE; } #ifdef HASHNG /* make an entry in the hash table for the current newsgroup */ void sethash(thisng) NG_NUM thisng; { HASHDATUM data; data.dat_ptr = nullstr; data.dat_len = thisng; hashstore(rc_hash, rcline[thisng], rcnums[thisng]-1, data); } static int rcline_cmp(key, keylen, data) char *key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, rcline[data.dat_len], keylen); } static void del_rc_line(data, ngnum) HASHDATUM *data; int ngnum; { if (data->dat_len == ngnum) data->dat_len = nextrcline-1; else if (data->dat_len > ngnum) data->dat_len--; } static void ins_rc_line(data, ngnum) HASHDATUM *data; int ngnum; { if (data->dat_len == nextrcline-1) data->dat_len = ngnum; else if (data->dat_len >= ngnum) data->dat_len++; } #endif void newsrc_check() { rcfp = fopen(rcname,"r"); /* open it */ if (rcfp == Nullfp) { /* not there? */ #ifdef VERBOSE IF(verbose) fputs("\nTrying to set up a .newsrc file -- running newsetup...\n\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("Setting up .newsrc...\n",stdout) FLUSH; #endif if (doshell(sh,filexp(NEWSETUP)) || (rcfp = fopen(rcname,"r")) == Nullfp) { #ifdef VERBOSE IF(verbose) fputs("\nCan't create a .newsrc -- you must do it yourself.\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("(Fatal)\n",stdout) FLUSH; #endif finalize(1); } get_anything(); putchar('\n') FLUSH; } else { /* File exists; if zero length and backup isn't, complain */ if (fstat(fileno(rcfp),&filestat) < 0) { perror(rcname); finalize(1); } if (filestat.st_size == 0 && stat(rcbname,&filestat) >= 0 && filestat.st_size > 0) { printf("Warning: %s is zero length but %s is not.\n",rcname,rcbname); printf("Either recover your newsrc or else remove the backup copy.\n"); finalize(1); } UNLINK(rcbname); /* unlink backup file name */ safelink(rcname,rcbname); /* and backup current name */ } } /* checkpoint the .newsrc */ void checkpoint_rc() { #ifdef DEBUG if (debug & DEB_CHECKPOINTING) { fputs("(ckpt)",stdout); fflush(stdout); } #endif if (doing_ng) bits_to_rc(); /* do not restore M articles */ if (rc_changed) write_rc(); #ifdef DEBUG if (debug & DEB_CHECKPOINTING) { fputs("(done)",stdout); fflush(stdout); } #endif } /* write out the (presumably) revised .newsrc */ void write_rc() { register NG_NUM tmpng; register char *delim; rcfp = fopen(rctname, "w"); /* open .newnewsrc */ if (rcfp == Nullfp) { printf(cantrecreate,".newsrc") FLUSH; finalize(1); } if (stat(rcname,&filestat)>=0) { /* preserve permissions */ chmod(rctname,filestat.st_mode&0666); chown(rctname,filestat.st_uid,filestat.st_gid); /* if possible */ } /* write out each line*/ for (tmpng = 0; tmpng < nextrcline; tmpng++) { if (rcnums[tmpng]) { delim = rcline[tmpng] + rcnums[tmpng] - 1; *delim = RCCHAR(rcchar[tmpng]); if (rcchar[tmpng] == '0' && delim[2] == '1') delim[2] = '0'; } else delim = Nullch; #ifdef DEBUG if (debug & DEB_NEWSRC_LINE) printf("%s\n",rcline[tmpng]) FLUSH; #endif if (fprintf(rcfp,"%s\n",rcline[tmpng]) < 0) { write_error: printf(cantrecreate,".newsrc") FLUSH; fclose(rcfp); /* close .newnewsrc */ UNLINK(rctname); finalize(1); } if (delim) { *delim = '\0'; /* might still need this line */ if (rcchar[tmpng] == '0' && delim[2] == '0') delim[2] = '1'; } } fflush(rcfp); /* fclose is the only sure test for full disks via NFS */ if (ferror(rcfp) || fclose(rcfp) == EOF) goto write_error; UNLINK(rcname); #ifdef HAS_RENAME rename(rctname,rcname); #else safelink(rctname,rcname); UNLINK(rctname); #endif if (writesoft) { tmpfp = fopen(filexp(softname), "w"); /* open .rnsoft */ if (tmpfp == Nullfp) { printf(cantcreate,filexp(softname)) FLUSH; return; } for (tmpng = 0; tmpng < nextrcline; tmpng++) { fprintf(tmpfp,"%ld\n",(long)softptr[tmpng]); } fclose(tmpfp); } } void get_old_rc() { UNLINK(rctname); #ifdef HAS_RENAME rename(rcname,rctname); rename(rcbname,rcname); #else safelink(rcname,rctname); UNLINK(rcname); safelink(rcbname,rcname); UNLINK(rcbname); #endif } static void grow_rc_arrays(newsize) int newsize; { abs1st = (ART_NUM*)saferealloc((char*)abs1st, (MEM_SIZE)newsize * sizeof (ART_NUM)); ngmax = (ART_NUM*)saferealloc((char*)ngmax, (MEM_SIZE)newsize * sizeof (ART_NUM)); rcline = (char**)saferealloc((char*)rcline, (MEM_SIZE)newsize * sizeof (char*)); toread = (ART_UNREAD*)saferealloc((char*)toread, (MEM_SIZE)newsize * sizeof(ART_UNREAD)); rcchar = (char *) saferealloc(rcchar, (MEM_SIZE)newsize * sizeof (char)); rcnums = (char*)saferealloc(rcnums, (MEM_SIZE)newsize * sizeof (char)); softptr = (ACT_POS*)saferealloc((char*)softptr, (MEM_SIZE)newsize * sizeof (ACT_POS)); bzero((char*)(abs1st+maxrcline), (newsize-maxrcline) * sizeof (ART_NUM)); bzero((char*)(ngmax+maxrcline), (newsize-maxrcline) * sizeof (ART_NUM)); maxrcline = newsize; return; } trn-3.6/art.c0000664000000000000000000007050005663312254010017 0ustar /* $Id: art.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "trn.h" #include "ngstuff.h" #include "ngdata.h" #include "cache.h" #include "bits.h" #include "head.h" #include "help.h" #include "search.h" #include "artio.h" #include "ng.h" #include "final.h" #include "artstate.h" #include "rcstuff.h" #include "term.h" #include "sw.h" #include "util.h" #include "util2.h" #include "nntp.h" #include "backpage.h" #include "intrp.h" #include "rthread.h" #include "rt-select.h" #include "rt-util.h" #include "rt-wumpus.h" #include "charsubst.h" #include "INTERN.h" #include "art.h" /* page_switch() return values */ #define PS_NORM 0 #define PS_ASK 1 #define PS_RAISE 2 #define PS_TOEND 3 bool special = FALSE; /* is next page special length? */ int slines = 0; /* how long to make page when special */ char *restart = Nullch; /* if nonzero, the place where last */ /* line left off on line split */ char *blinebeg; /* where in buffer current line began */ ART_POS alinebeg; /* where in file current line began */ #ifdef INNERSEARCH ART_LINE isrchline = 0; /* last line to display */ COMPEX gcompex; /* in article search pattern */ #endif bool firstpage; /* is this the 1st page of article? */ char art_buf[LBUFLEN]; /* place for article lines */ void art_init() { #ifdef INNERSEARCH init_compex(&gcompex) #endif ; } #ifdef MIMESHOW #define VERY_LONG_STRING 200 int display_mime() { int code = 1; if (!getenv("NOMIME")) { char oldmode = mode; fputs("Process MIME article? [yn]",stdout); fflush(stdout); eat_typeahead(); #ifdef PENDING cache_until_key(); #endif mode = 'p'; getcmd(buf); setdef(buf,"y"); mode = oldmode; #ifdef VERIFY printcmd(); #endif carriage_return(); erase_eol(); /* erase the prompt */ carriage_return(); /* Resets kernel's tab column counter to 0 */ if (*buf == 'y') { putchar('\n'); fflush(stdout); #ifdef USE_NNTP nntp_finishbody(FB_OUTPUT); #endif termlib_reset(); resetty(); interp(cmd_buf,(sizeof cmd_buf),getval("MIMESHOW",MIMESHOW)); code = doshell(SH,cmd_buf); noecho(); crmode(); termlib_init(); } } return code; } #endif int do_article() { register char *s; bool hide_this_line = FALSE; /* hidden header line? */ ART_LINE linenum; /* line # on page, 1 origin */ #ifdef ULSMARTS bool under_lining = FALSE; /* are we underlining a word? */ #endif register char *bufptr = art_buf; /* pointer to input buffer */ register int outpos; /* column position of output */ static char prompt_buf[64]; /* place to hold prompt */ bool notesfiles = FALSE; /* might there be notesfiles junk? */ char oldmode = mode; char *ctime(); #ifdef MIMESHOW bool tried_display_mime = FALSE; #endif #ifdef INNERSEARCH register int outputok = TRUE; #endif #if defined(MIMESHOW) || defined(MIMESTORE) mime_article = FALSE; #endif #ifndef USE_NNTP if (fstat(fileno(artfp),&filestat)) /* get article file stats */ return DA_CLEAN; if (!S_ISREG(filestat.st_mode)) return DA_NORM; artsize = filestat.st_size; /* from that get article size */ #endif #ifdef CHARSUBST sprintf(prompt_buf, "%%sEnd of article %ld (of %ld) %%s-- what next? [%%s]", (long)art,(long)lastart); /* format prompt string */ #else sprintf(prompt_buf, "%%sEnd of article %ld (of %ld) -- what next? [%%s]", (long)art,(long)lastart); /* format prompt string */ #endif prompt = prompt_buf; int_count = 0; /* interrupt count is 0 */ if ((firstpage = (topline < 0)) != 0) { parseheader(art); seekart(htype[PAST_HEADER].ht_minpos); } for (;;) { /* for each page */ if (ThreadedGroup && max_tree_lines) init_tree(); /* init tree display */ assert(art == openart); if (do_fseek) { parseheader(art); /* make sure header is ours */ artpos = vrdary(artline); if (artpos < 0) artpos = -artpos; /* labs(), anyone? */ if (firstpage) artpos = (ART_POS)0; if (artpos < htype[PAST_HEADER].ht_minpos) { in_header = SOME_LINE; seekart(htype[PAST_HEADER].ht_minpos); } else seekart(artpos); do_fseek = FALSE; restart = Nullch; } linenum = 1; if (firstpage) { if (firstline) { interp(art_buf, (sizeof art_buf), firstline); linenum += tree_puts(art_buf,linenum+topline,0); } else { ART_NUM i; int selected, unseen; selected = (curr_artp->flags & AF_SEL); unseen = !was_read(art); sprintf(art_buf,"%s%s #%ld",ngname,moderated,(long)art); if (selected_only) { i = selected_count - (unseen && selected); sprintf(art_buf+strlen(art_buf)," (%ld + %ld more)", (long)i,(long)toread[ng] - selected_count - (!selected && unseen)); } else if ((i = (ART_NUM)(toread[ng] - unseen)) != 0 || (!ThreadedGroup && dmcount)) sprintf(art_buf+strlen(art_buf)," (%ld more)",(long)i); if (!ThreadedGroup && dmcount) sprintf(art_buf+strlen(art_buf)-1," + %ld Marked to return)", (long)dmcount); linenum += tree_puts(art_buf,linenum+topline,0); } start_header(art); forcelast = FALSE; /* we will have our day in court */ restart = Nullch; artline = 0; /* start counting lines */ artpos = 0; vwtary(artline,artpos); /* remember pos in file */ } for (; /* linenum already set */ in_header || ( #ifdef INNERSEARCH innersearch ? innermore() : #endif linenum<(firstpage?initlines:(special?slines:LINES)) ); linenum++) { /* for each line on page */ if (int_count) { /* exit via interrupt? */ putchar('\n') FLUSH; /* get to left margin */ int_count = 0; /* reset interrupt count */ mode = oldmode; special = FALSE; #ifdef USE_NNTP nntp_finishbody(FB_OUTPUT); #endif return DA_NORM; /* skip out of loops */ } if (restart) { /* did not finish last line? */ bufptr = restart; /* then start again here */ restart = Nullch; /* and reset the flag */ } else if (in_header && headbuf[artpos] != '\0') { bufptr = index(headbuf+artpos,'\n') + 1; bcopy(headbuf+artpos,art_buf,bufptr-headbuf-artpos); art_buf[bufptr-headbuf-artpos] = '\0'; bufptr = art_buf; } else { if (readart(art_buf,LBUFLEN) == Nullch) { break; } bufptr = art_buf; /* so start at beginning */ art_buf[LBUFLEN-1] = '\0'; /* make sure string ends */ } blinebeg = bufptr; /* remember where we began */ alinebeg = artpos; /* both in buffer and file */ if (in_header && bufptr == art_buf) { hide_this_line = parseline(art_buf,do_hiding,hide_this_line); if (!in_header) { linenum += finish_tree(linenum+topline); end_header(); } } else if (notesfiles && do_hiding && bufptr == art_buf && *art_buf == '#' && isupper(art_buf[1]) && art_buf[2] == ':' ) { readart(art_buf,sizeof(art_buf)); if (index(art_buf,'!') != Nullch) readart(art_buf,sizeof(art_buf)); htype[PAST_HEADER].ht_minpos = tellart(); /* exclude notesfiles droppings */ hide_this_line = TRUE; /* and do not print either */ notesfiles = FALSE; } #ifdef CUSTOMLINES if (hideline && bufptr == art_buf && execute(&hide_compex,art_buf) ) hide_this_line = TRUE; #endif if (in_header && htype[in_header].ht_flags & HT_MAGIC) { if (in_header == NGS_LINE) { if ((s = index(art_buf,'\n')) != Nullch) *s = '\0'; hide_this_line = (index(art_buf,',') == Nullch) && strEQ(art_buf+12, ngname); if (s != Nullch) *s = '\n'; } else if (in_header == EXPIR_LINE) { if (!(htype[EXPIR_LINE].ht_flags & HT_HIDE)) hide_this_line = ((int)strlen(art_buf) < 10); } else if (in_header == FROM_LINE) { if (do_hiding && (s = extract_name(art_buf+6)) != Nullch) strcpy(art_buf+6,s); } #ifdef HAS_STRFTIME else if (in_header == DATE_LINE) { if (do_hiding && curr_artp->date != -1) strftime(art_buf+6, sizeof(art_buf)-6, getval("LOCALTIMEFMT", LOCALTIMEFMT), localtime(&curr_artp->date)); } #endif #if defined(MIMESHOW) || defined(MIMESTORE) else if (!isspace(*art_buf)) { int nontext _((char *)); int nonprint _((char *)); if (in_header == CONTENT_LINE) mime_article = mime_article || nontext(art_buf+14); else if (in_header == CONTXFER_LINE) mime_article = mime_article || nonprint(art_buf+27); } #endif } if (in_header == SUBJ_LINE && htype[SUBJ_LINE].ht_flags & HT_MAGIC) { /* is this the subject? */ int length; length = strlen(art_buf)-1; artline++; art_buf[length] = '\0'; /* wipe out newline */ #ifdef NOFIREWORKS no_ulfire(); #endif notesfiles = (instr(&art_buf[length-10]," - (nf", TRUE) != Nullch); /* tree_puts(, ,1) underlines subject */ linenum += tree_puts(art_buf,linenum+topline,1)-1; } else if (hide_this_line && do_hiding) { /* do not print line? */ linenum--; /* compensate for linenum++ */ if (!in_header) hide_this_line = FALSE; } else if (in_header) { artline++; linenum += tree_puts(art_buf,linenum+topline,0)-1; } else { /* just a normal line */ #ifdef MIMESHOW if (mime_article && do_hiding && !tried_display_mime) { if (display_mime() == 0) return DA_NORM; tried_display_mime = TRUE; } #endif #ifdef CLEAREOL #ifdef INNERSEARCH if (outputok) #endif if (can_home_clear) erase_eol(); #endif /* CLEAREOL */ if (highlight==artline) { /* this line to be highlit? */ if (marking == STANDOUT) { #ifdef NOFIREWORKS if (erase_screen) no_sofire(); #endif standout(); } else { #ifdef NOFIREWORKS if (erase_screen) no_ulfire(); #endif underline(); } if (*bufptr == '\n') putchar(' '); } #ifdef INNERSEARCH outputok = !hide_everything; /* get it into register, hopefully */ #endif #ifdef CUSTOMLINES if (pagestop && bufptr == art_buf && execute(&page_compex,art_buf) ) linenum = 32700; #endif for (outpos = 0; outpos < COLS; ) { /* while line has room */ if (*(unsigned char*)bufptr >= ' ') { /* normal char? */ #ifdef ULSMARTS if (*bufptr == '_') { if (bufptr[1] == '\b') { #ifdef INNERSEARCH if (outputok) #endif if (!under_lining && highlight != artline) { under_lining++; if (UG) { if (bufptr != buf && bufptr[-1]==' ') { outpos--; backspace(); } } underline(); } bufptr += 2; } } else { if (under_lining) { under_lining = 0; un_underline(); if (UG) { outpos++; if (*bufptr == ' ') goto skip_put; } } } #endif #ifdef ROTATION if (rotate && !in_header && isalpha(*bufptr)) { # ifdef INNERSEARCH if (outputok) # endif if ((*bufptr & 31) <= 13) putchar(*bufptr+13); else putchar(*bufptr-13); outpos++; } else #endif { #ifdef CHARSUBST register int i; # ifdef INNERSEARCH i = putsubstchar(*bufptr, COLS - outpos, outputok); # else i = putsubstchar(*bufptr, COLS - outpos, TRUE); # endif if (i < 0) { outpos += -i - 1; break; } outpos += i; #else # ifdef INNERSEARCH if (outputok) # endif putchar(*bufptr); outpos++; #endif /* CHARSUBST */ } if (*UC && ((highlight==artline && marking == STANDOUT) #ifdef ULSMARTS || under_lining #endif )) { backspace(); underchar(); } skip_put: bufptr++; } else if (*bufptr == '\n' || !*bufptr) { /* newline? */ #ifdef ULSMARTS if (under_lining) { under_lining = 0; un_underline(); } #endif #ifdef DEBUG if (debug & DEB_INNERSRCH && outpos < COLS - 6) { standout(); printf("%4d",artline); un_standout(); } #endif #ifdef INNERSEARCH if (outputok) #endif putchar('\n') FLUSH; restart = 0; outpos = 1000; /* signal normal \n */ } else if (*bufptr == '\t') { /* tab? */ int incpos = 8 - outpos % 8; #ifdef INNERSEARCH if (outputok) #endif if (GT) putchar(*bufptr); else while (incpos--) putchar(' '); bufptr++; outpos += 8 - outpos % 8; } else if (*bufptr == '\f') { /* form feed? */ if (outpos+2 > COLS) break; #ifdef INNERSEARCH if (outputok) #endif fputs("^L",stdout); if (bufptr == blinebeg && highlight != artline) linenum = 32700; /* how is that for a magic number? */ bufptr++; outpos += 2; } else { /* other control char */ if (dont_filter_control) { #ifdef INNERSEARCH if (outputok) #endif putchar(*bufptr); outpos++; } else if (*bufptr != '\r' || bufptr[1] != '\n') { if (outpos+2 > COLS) break; #ifdef INNERSEARCH if (outputok) #endif { putchar('^'); if (highlight == artline && *UC && marking == STANDOUT) { backspace(); underchar(); putchar(*bufptr+64); backspace(); underchar(); } else putchar(*bufptr+64); } outpos += 2; } bufptr++; } } /* end of column loop */ if (outpos < 1000) { /* did line overflow? */ restart = bufptr; /* restart here next time */ if (!AM || XN || outpos < COLS) { #ifdef INNERSEARCH if (outputok) #endif putchar('\n') FLUSH; } if (*bufptr == '\n') /* skip the newline */ restart = 0; } /* handle normal end of output line formalities */ if (highlight == artline) { /* were we highlighting line? */ if (marking == STANDOUT) un_standout(); else un_underline(); highlight = -1; /* no more we are */ /* in case terminal highlighted rest of line earlier */ /* when we did an eol with highlight turned on: */ if (can_home_clear) erase_eol(); } artline++; /* count the line just printed */ if (artline - LINES + 1 > topline) /* did we just scroll top line off? */ topline = artline - LINES + 1; /* then recompute top line # */ } /* determine actual position in file */ if (restart) /* stranded somewhere in the buffer? */ artpos += restart - blinebeg; /* just calculate position */ else if (in_header) artpos = index(headbuf+artpos, '\n') - headbuf + 1; else /* no, ftell will do */ artpos = tellart(); vwtary(artline,artpos); /* remember pos in file */ } /* end of line loop */ #ifdef INNERSEARCH innersearch = 0; if (hide_everything) { *buf = hide_everything; hide_everything = 0; goto fake_command; } #endif if (linenum >= 32700)/* did last line have formfeed? */ vwtary(artline-1,-vrdary(artline-1)); /* remember by negating pos in file */ special = FALSE; /* end of page, so reset page length */ firstpage = FALSE; /* and say it is not 1st time thru */ highlight = -1; /* extra loop bombout */ #ifdef USE_NNTP artsize = nntp_artsize(); recheck_pager: #endif if (artpos == artsize) {/* did we just now reach EOF? */ mode = oldmode; return DA_NORM; /* avoid --MORE--(100%) */ } /* not done with this article, so pretend we are a pager */ reask_pager: unflush_output(); /* disable any ^O in effect */ standout(); /* enter standout mode */ #ifdef USE_NNTP if (artsize == -1) strcpy(buf,"?"); else #endif sprintf(buf,"%ld",(long)(artpos*100/artsize)); #ifdef CHARSUBST printf("%s--MORE--(%s%%)",current_charsubst(),buf); #else printf("--MORE--(%s%%)",buf); #endif un_standout(); /* leave standout mode */ #ifdef CLEAREOL maybe_eol(); #endif fflush(stdout); eat_typeahead(); #ifdef DEBUG if (debug & DEB_CHECKPOINTING) { printf("(%d %d %d)",checkcount,linenum,artline); fflush(stdout); } #endif if (checkcount >= docheckwhen && linenum == LINES && (artline > 40 || checkcount >= docheckwhen+10) ) { /* while he is reading a whole page */ /* in an article he is interested in */ checkcount = 0; checkpoint_rc(); /* update .newsrc */ } cache_until_key(); #ifdef USE_NNTP if (artsize == -1 && (artsize = nntp_artsize()) != -1) { carriage_return(); goto recheck_pager; } #endif mode = 'p'; getcmd(buf); if (errno) { if (LINES < 100 && !int_count) *buf = '\f';/* on CONT fake up refresh */ else { *buf = 'q'; /* on INTR or paper just quit */ } } carriage_return(); #ifdef CLEAREOL if (erase_screen && can_home_clear) clear_rest(); else #endif erase_eol(); /* and erase the prompt */ carriage_return(); /* Resets kernel's tab column counter to 0 */ fflush(stdout); fake_command: /* used by innersearch */ output_chase_phrase = TRUE; /* parse and process pager command */ switch (page_switch()) { case PS_ASK: /* reprompt "--MORE--..." */ goto reask_pager; case PS_RAISE: /* reparse on article level */ mode = oldmode; return DA_RAISE; case PS_TOEND: /* fast pager loop exit */ mode = oldmode; return DA_TOEND; case PS_NORM: /* display more article */ break; } } /* end of page loop */ } /* process pager commands */ int page_switch() { register char *s; switch (*buf) { case '!': /* shell escape */ escapade(); return PS_ASK; #ifdef INNERSEARCH case Ctl('i'): { ART_LINE i = artline; ART_POS pos; gline = 3; s = blinebeg; while (*s == '\n' && i >= topline) { pos = vrdary(--i); if (pos < 0) pos = -pos; if (pos < htype[PAST_HEADER].ht_minpos) break; seekart((long)pos); if (readart(s = buf+1, sizeof buf - 1) == Nullch) { s = blinebeg; break; } } sprintf(cmd_buf,"^[^%c\n]",*s); compile(&gcompex,cmd_buf,TRUE,TRUE); goto caseG; } case Ctl('g'): gline = 3; compile(&gcompex,"^Subject:",TRUE,TRUE); goto caseG; case 'g': /* in-article search */ if (!finish_command(FALSE))/* get rest of command */ return PS_ASK; s = buf+1; if (isspace(*s)) s++; if ((s = compile(&gcompex,s,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; return PS_ASK; } carriage_return(); erase_eol(); /* erase the prompt */ carriage_return(); /* Resets kernel's tab column counter to 0 */ /* FALL THROUGH */ caseG: case 'G': { ART_POS start_where; if (gline < 0 || gline > LINES-2) gline = LINES-2; #ifdef DEBUG if (debug & DEB_INNERSRCH) printf("Start here? %d >=? %d\n",topline + gline + 1,artline) FLUSH; #endif if (*buf == Ctl('i') || topline+gline+1 >= artline) start_where = artpos; /* in case we had a line wrap */ else { start_where = vrdary(topline+gline+1); if (start_where < 0) start_where = -start_where; } if (start_where < htype[PAST_HEADER].ht_minpos) start_where = htype[PAST_HEADER].ht_minpos; seekart((long)start_where); innerlight = 0; innersearch = 0; /* assume not found */ while (readart(buf, sizeof buf) != Nullch) { #ifdef DEBUG if (debug & DEB_INNERSRCH) printf("Test %s",buf) FLUSH; #endif if (execute(&gcompex,buf) != Nullch) { innersearch = tellart(); break; } } if (!innersearch) { seekart(artpos); fputs("(Not found)",stdout) FLUSH; return PS_ASK; } #ifdef DEBUG if (debug & DEB_INNERSRCH) printf("On page? %ld <=? %ld\n",(long)innersearch,(long)artpos) FLUSH; #endif if (innersearch <= artpos) { /* already on page? */ if (innersearch < artpos) { artline = topline+1; while (vrdary(artline) < innersearch) artline++; } highlight = artline - 1; #ifdef DEBUG if (debug & DEB_INNERSRCH) printf("@ %d\n",highlight) FLUSH; #endif topline = highlight - gline; if (topline < -1) topline = -1; *buf = '\f'; /* fake up a refresh */ innersearch = 0; return page_switch(); } else { /* who knows how many lines it is? */ do_fseek = TRUE; hide_everything = Ctl('l'); } return PS_NORM; } #else case 'g': case 'G': case Ctl('g'): notincl("g"); return PS_ASK; #endif case '\n': /* one line down */ special = TRUE; slines = 2; return PS_NORM; #ifdef ROTATION case 'X': rotate = !rotate; /* FALL THROUGH */ #endif case 'l': case '\f': /* refresh screen */ refresh_screen: #ifdef DEBUG if (debug & DEB_INNERSRCH) { printf("Topline = %d",topline) FLUSH; fgets(buf, sizeof buf, stdin); } #endif clear(); carriage_return(); /* Resets kernel's tab column counter to 0 */ do_fseek = TRUE; artline = topline; if (artline < 0) artline = 0; firstpage = (topline < 0); return PS_NORM; #ifdef INNERSEARCH case Ctl('e'): #ifdef USE_NNTP nntp_finishbody(FB_OUTPUT); artsize = nntp_artsize(); #endif innerlight = artline - 1; innersearch = artsize; gline = 0; hide_everything = 'b'; return PS_NORM; #endif case 'B': /* one line up */ if (topline < 0) break; if (*IL && *HO) { ART_POS pos; home_cursor(); insert_line(); carriage_return(); /* Resets kernel's tab column counter to 0 */ pos = vrdary(topline-1); if (pos < 0) pos = -pos; if (pos >= htype[PAST_HEADER].ht_minpos) { seekart((long)pos); if (readart(s = buf, sizeof buf) != Nullch) { fputs(buf, stdout) FLUSH; topline--; artpos = vrdary(--artline); if (artpos < 0) artpos = -artpos; seekart(artpos); goto_line(1, LINES-1); erase_eol(); carriage_return(); return PS_ASK; } } } /* FALL THROUGH */ case 'b': case '\b': case Ctl('b'): { /* back up a page */ ART_LINE target; #ifdef CLEAREOL if (can_home_clear) /* if we can let home do it */ home_cursor(); else #endif clear(); carriage_return(); /* Resets kernel's tab column counter to 0 */ do_fseek = TRUE; /* reposition article file */ if (*buf == 'B') target = topline - 1; else { target = topline - (LINES - 2); if (marking && (marking_areas & BACKPAGE_MARKING)) highlight = topline; } artline = topline; if (artline >= 0) do { artline--; } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0); topline = artline; /* remember top line of screen */ /* (line # within article file) */ if (artline < 0) artline = 0; firstpage = (topline < 0); return PS_NORM; } case 'h': { /* help */ int cmd; if ((cmd = help_page()) > 0) pushchar(cmd); return PS_ASK; } case 't': /* output thread data */ page_line = 1; entire_tree(curr_artp); return PS_ASK; case '_': if (!finish_dblchar()) return PS_ASK; switch (buf[1] & 0177) { #ifdef CHARSUBST case 'C': if (!*(++charsubst)) charsubst = charsets; goto refresh_screen; #endif default: goto leave_pager; } break; case '\177': case '\0': /* treat del,break as 'n' */ *buf = 'n'; /* FALL THROUGH */ case 'k': case 'K': case 'J': case 'n': case 'N': case Ctl('n'): case 's': case 'S': case 'e': case 'u': case 'w': case 'W': case '|': mark_as_read(); /* mark article as read */ /* FALL THROUGH */ case 'U': case ',': case '<': case '>': case '[': case ']': case '{': case '}': case '(': case ')': case ':': case '+': case '#': case '$': case '&': case '-': case '.': case '/': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '=': case '?': case 'A': case 'T': case 'c': case 'C': #ifdef DEBUG case 'D': #endif case 'E': case 'f': case 'F': case Ctl('f'): case 'j': case Ctl('k'): case 'm': case 'M': case 'p': case 'P': case Ctl('p'): case 'Q': case 'r': case 'R': case Ctl('r'): case 'v': case 'Y': #ifndef ROTATION case 'x': case 'X': #endif case Ctl('x'): case 'z': case 'Z': case '^': case Ctl('^'): leave_pager: #ifdef ROTATION rotate = FALSE; #endif reread = FALSE; do_hiding = TRUE; #ifdef USE_NNTP nntp_finishbody(FB_OUTPUT); #endif if (index("nNpP\016\020",*buf) == Nullch && index("wWsSe:!&|/?123456789.",*buf) != Nullch) { setdfltcmd(); standout(); /* enter standout mode */ interp(cmd_buf, sizeof cmd_buf, mailcall); #ifdef CHARSUBST printf(prompt,cmd_buf,current_charsubst(),dfltcmd); #else printf(prompt,cmd_buf,dfltcmd); #endif /* print prompt, whatever it is */ un_standout(); /* leave standout mode */ putchar(' '); fflush(stdout); } return PS_RAISE; /* and pretend we were at end */ #ifdef ROTATION case 'x': rotate = TRUE; /* FALL THROUGH */ #endif case 'd': /* half page */ case Ctl('d'): special = TRUE; slines = LINES / 2 + 1; /* no divide-by-zero, thank you */ if (LINES > 2 && (LINES & 1) && artline % (LINES-2) >= LINES/2 - 1) slines++; goto go_forward; case 'y': case Ctl('v'): /* Leaving it undocumented in case */ /* I want to steal the key--LAW */ case ' ': /* continue current article */ if (erase_screen) { /* -e? */ #ifdef CLEAREOL if (can_home_clear) /* if we can let home do it */ home_cursor(); else #endif clear(); /* clear screen */ carriage_return(); /* Resets kernel's tab column counter to 0 */ fflush(stdout); } else { special = TRUE; slines = LINES; } go_forward: if (*blinebeg != '\f' #ifdef CUSTOMLINES && (!pagestop || blinebeg != art_buf || !execute(&page_compex,blinebeg)) #endif ) { if (!special || (marking && (*buf!='d' || (marking_areas&HALFPAGE_MARKING)))) { restart = blinebeg; artline--; /* restart this line */ artpos = alinebeg; if (special) up_line(); if (marking) highlight = artline; } else slines--; } return PS_NORM; case 'q': /* quit this article? */ do_hiding = TRUE; #ifdef USE_NNTP nntp_finishbody(FB_OUTPUT); #endif return PS_TOEND; default: fputs(hforhelp,stdout) FLUSH; settle_down(); return PS_ASK; } return PS_ASK; } #ifdef INNERSEARCH bool innermore() { if (artpos < innersearch) { /* not even on page yet? */ #ifdef DEBUG if (debug & DEB_INNERSRCH) printf("Not on page %ld < %ld\n",(long)artpos,(long)innersearch) FLUSH; #endif return TRUE; } if (artpos == innersearch) { /* just got onto page? */ isrchline = artline; /* remember first line after */ if (innerlight) highlight = innerlight; else highlight = artline - 1; #ifdef DEBUG if (debug & DEB_INNERSRCH) printf("There it is %ld = %ld, %d @ %d\n",(long)artpos, (long)innersearch,hide_everything,highlight) FLUSH; #endif if (hide_everything) { /* forced refresh? */ topline = artline - gline - 1; if (topline < -1) topline = -1; return FALSE; /* let refresh do it all */ } } #ifdef DEBUG if (debug & DEB_INNERSRCH) printf("Not far enough? %d = s && isspace(*t)) t--; *++t = '\0'; if (notplain(s)) return 1; return 0; } /* return true if this isn't "text" or "text/plain" */ int notplain(s) char *s; { char *t; if (!s) return 1; while (isspace(*s)) s++; t = s + strlen(s) - 1; while (t >= s && isspace(*t)) t--; *++t = '\0'; if (t - s == 4 && !strncasecmp(s, "text", 4)) return 0; if (strncasecmp(s, "text/plain", 10)) return 1; t = index(s, ';'); while (t) { t++; while (isspace(*t)) t++; if (!strncasecmp(t, "charset", 7)) { s = index(t, '='); if (s) { s++; while (isspace(*s)) s++; if (!strncasecmp(s, "us-ascii", 8)) return 0; } return 1; } t = index(t, ';'); } return 0; /* no charset, was text/plain */ } /* return true if this isn't "7bit", "8bit", or "binary" */ int nonprint(s) char *s; { while (isspace(*s)) s++; if (strncasecmp(s, "7bit", 4) == 0) s += 4; else if (strncasecmp(s, "8bit", 4) == 0) s += 4; else if (strncasecmp(s, "binary", 6) == 0) s += 6; else return 1; return !(*s == '\0' || isspace(*s) || *s == ';' || *s == '('); /*)*/ } #endif trn-3.6/backpage.h0000664000000000000000000000200605420174060010757 0ustar /* $Id: backpage.h,v 3.0 1991/09/09 20:18:23 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ /* things for doing the 'back page' command */ EXT int varyfd INIT(0); /* virtual array file for storing */ /* file offsets */ EXT ART_POS varybuf[VARYSIZE]; /* current window onto virtual array */ EXT long oldoffset INIT(-1); /* offset to block currently in window */ void backpage_init _((void)); ART_POS vrdary _((ART_LINE)); void vwtary _((ART_LINE,ART_POS)); trn-3.6/ngstuff.h0000664000000000000000000000167405663312262010717 0ustar /* $Id: ngstuff.h,v 3.0 1991/09/09 20:23:31 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #define NN_NORM 0 #define NN_INP 1 #define NN_REREAD 2 #define NN_ASK 3 EXT bool one_command INIT(FALSE); /* no ':' processing in perform() */ void ngstuff_init _((void)); int escapade _((void)); int switcheroo _((void)); int numnum _((void)); int perform _((char*,int)); int thread_perform _((void)); trn-3.6/cache.c.orig0000664000000000000000000004773107041327652011245 0ustar /* $Id: cache.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "INTERN.h" #include "cache.h" #include "EXTERN.h" #include "intrp.h" #include "search.h" #include "ng.h" #include "trn.h" #include "ngdata.h" #include "term.h" #include "final.h" #include "artsrch.h" #include "head.h" #include "bits.h" #include "rcstuff.h" #include "hash.h" #include "nntp.h" #include "rthread.h" #include "rt-ov.h" #include "rt-page.h" #include "rt-process.h" #include "rt-select.h" #include "rt-util.h" #include "util.h" #include "util2.h" #ifdef PENDING # ifdef ARTSEARCH COMPEX srchcompex; /* compiled regex for searchahead */ # endif #endif HASHTABLE *subj_hash = 0; HASHTABLE *shortsubj_hash = 0; int subject_cmp _((char *,int,HASHDATUM)); void cache_init() { #ifdef PENDING # ifdef ARTSEARCH init_compex(&srchcompex) # endif #endif ; } NG_NUM cached_ng = -1; time_t cached_time = 0; ART_NUM cached_cnt = 0; ART_NUM cached_absfirst = 0; void build_cache() { if (cached_ng == ng && cached_absfirst == absfirst && time((time_t*)NULL) < cached_time + 6*60*60L) { grow_cache(lastart); rc_to_bits(); if (sel_mode == SM_ARTICLE) set_selector(sel_mode, sel_artsort); else set_selector(sel_threadmode, sel_threadsort); thread_grow(); return; } close_cache(); cached_ng = ng; cached_absfirst = absfirst; cached_time = time((time_t*)NULL); cached_cnt = lastart-absfirst+2 + 10; article_list = (ARTICLE*) safemalloc((MEM_SIZE)(cached_cnt * sizeof (ARTICLE))); bzero((char*)article_list, cached_cnt * sizeof (ARTICLE)); subj_hash = hashcreate(201, subject_cmp); /*TODO: pick a better size */ rc_to_bits(); /* sets firstart */ first_cached = thread_always? absfirst : firstart; last_cached = first_cached-1; cached_all_in_range = FALSE; #ifdef PENDING subj_to_get = xref_to_get = firstart; #endif #ifndef USE_NNTP setmissingbits(); #endif /* Cache as much data in advance as possible, possibly threading ** articles as we go. */ thread_open(); } #define FIXPTR(p) if ((p) && (p) >= oldlist_start && (p) <= oldlist_end)\ (p) = (article_list + ((p) - oldlist_start)); else void grow_cache(newlast) ART_NUM newlast; { ART_NUM new_cnt = newlast-absfirst+2; if (new_cnt > cached_cnt) { ARTICLE *oldlist_start = article_list; ARTICLE *oldlist_end = oldlist_start + cached_cnt; new_cnt += 10; article_list = (ARTICLE*)saferealloc((char*)article_list, (MEM_SIZE)(new_cnt * sizeof (ARTICLE))); bzero((char*)(article_list+cached_cnt), (new_cnt-cached_cnt) * sizeof (ARTICLE)); if (article_list != oldlist_start) { register ARTICLE *ap; register SUBJECT *sp; for (sp = first_subject; sp; sp = sp->next) { FIXPTR(sp->thread); FIXPTR(sp->articles); if (sp->thread) { for (ap = sp->thread; ap; ap = bump_art(ap)) { FIXPTR(ap->child1); FIXPTR(ap->parent); FIXPTR(ap->sibling); FIXPTR(ap->subj_next); } } else { for (ap = sp->articles; ap; ) { FIXPTR(ap->subj_next); ap = ap->subj_next; } } } FIXPTR(artp); FIXPTR(curr_artp); FIXPTR(recent_artp); } cached_cnt = new_cnt; } cached_time = time((time_t*)NULL); } void close_cache() { SUBJECT *sp, *next; ARTICLE *ap; ART_NUM i; if (subj_hash) { hashdestroy(subj_hash); subj_hash = 0; } if (shortsubj_hash) { hashdestroy(shortsubj_hash); shortsubj_hash = 0; } /* Free all the subjects. */ for (sp = first_subject; sp; sp = next) { next = sp->next; free(sp->str); free((char*)sp); } first_subject = last_subject = Nullsubj; subject_count = 0; /* just to be sure */ parsed_art = 0; if (artptr_list) { free((char*)artptr_list); artptr_list = Null(ARTICLE**); } artptr = Null(ARTICLE**); thread_close(); if (cached_cnt) { for (i = 0, ap = article_list; i < cached_cnt; i++, ap++) clear_article(ap); free((char*)article_list); cached_cnt = 0; } cached_ng = -1; } /* The article has all it's data in place, so add it to the list of articles ** with the same subject. */ void cache_article(ap) register ARTICLE *ap; { register ARTICLE *next, *ap2; if (!(next = ap->subj->articles) || ap->date < next->date) ap->subj->articles = ap; else { while ((next = (ap2 = next)->subj_next) && next->date <= ap->date) ; ap2->subj_next = ap; } ap->subj_next = next; ap->flags |= AF_CACHED; if (!(ap->flags & AF_READ) ^ sel_rereading) { if (ap->subj->flags & SF_WASSELECTED) select_article(ap, 0); else ap->subj->flags |= SF_VISIT; } if (join_subject_len != 0) check_for_near_subj(ap); } void check_for_near_subj(ap) ARTICLE *ap; { register SUBJECT *sp; if (!shortsubj_hash) { shortsubj_hash = hashcreate(201, subject_cmp); /*TODO: pick a better size */ sp = first_subject; } else { sp = ap->subj; if (sp->next) sp = 0; } while (sp) { if (strlen(sp->str+4) >= join_subject_len && sp->thread) { SUBJECT *sp2; HASHDATUM data; data = hashfetch(shortsubj_hash, sp->str+4, join_subject_len); if (!(sp2 = (SUBJECT*)data.dat_ptr)) { data.dat_ptr = (char*)sp; hashstorelast(data); } else if (sp->thread != sp2->thread) { merge_threads(sp2, sp); } } sp = sp->next; } } void change_join_subject_len(len) int len; { if (join_subject_len != len) { if (shortsubj_hash) { hashdestroy(shortsubj_hash); shortsubj_hash = 0; } join_subject_len = len; if (len && first_subject && first_subject->articles) check_for_near_subj(first_subject->articles); } } void check_poster(ap) register ARTICLE *ap; { if (auto_select_postings && !(ap->flags & AF_MISSING) && ap->from) { if (ap->flags & AF_FROMTRUNCED) { strcpy(cmd_buf,realname); if (strEQ(ap->from,compress_name(cmd_buf,16))) { untrim_cache = TRUE; fetchfrom(article_num(ap),FALSE); untrim_cache = FALSE; } } if (!(ap->flags & AF_FROMTRUNCED)) { char *s = cmd_buf, *u, *h; strcpy(s,ap->from); if ((h=index(s,'<')) != Nullch) { /* grab the good part */ s = h+1; if ((h=index(s,'>')) != Nullch) *h = '\0'; } else if ((h=index(s,'(')) != Nullch) { while (h-- != s && *h == ' ') ; h[1] = '\0'; /* or strip the comment */ } if ((h=index(s,'%'))!=Nullch || (h=index(s,'@'))!=Nullch) { *h++ = '\0'; u = s; } else if ((u=rindex(s,'!')) != Nullch) { *u++ = '\0'; h = s; } else h = u = s; if (strEQ(u,loginName)) { if (instr(h,phostname,FALSE)) { switch (auto_select_postings) { case '.': select_subthread(ap,AUTO_SELECT); break; case '+': select_arts_thread(ap,AUTO_SELECT); break; case 'p': if (ap->parent) select_subthread(ap->parent,AUTO_SELECT); else select_subthread(ap,AUTO_SELECT); break; } } else { #ifdef REPLYTO_POSTER_CHECKING char *reply_buf = fetchlines(article_num(ap),REPLY_LINE); if (instr(reply_buf,loginName,TRUE)) select_subthread(ap,AUTO_SELECT); free(reply_buf); #endif } } } } } /* The article turned out to be a duplicate, so remove it from the cached ** list and possibly destroy the subject (should only happen if the data ** was corrupt and the duplicate id got a different subject). */ void uncache_article(ap, remove_empties) register ARTICLE *ap; bool_int remove_empties; { register ARTICLE *next; if (ap->subj) { if ((ap->flags & (AF_CACHED|AF_MISSING)) == AF_CACHED) { if ((next = ap->subj->articles) == ap) ap->subj->articles = ap->subj_next; else { register ARTICLE *ap2; while (next) { if ((ap2 = next->subj_next) == ap) { next->subj_next = ap->subj_next; break; } next = ap2; } } } if (remove_empties && !ap->subj->articles) { register SUBJECT *sp = ap->subj; if (sp == first_subject) first_subject = sp->next; else sp->prev->next = sp->next; if (sp == last_subject) last_subject = sp->prev; else sp->next->prev = sp->prev; hashdelete(subj_hash, sp->str+4, strlen(sp->str+4)); free((char*)sp); ap->subj = Nullsubj; subject_count--; } } onemissing(ap); } /* get the header line from an article's cache or parse the article trying */ char * fetchcache(artnum,which_line,fill_cache) ART_NUM artnum; int which_line; bool_int fill_cache; { register char *s; register ARTICLE *ap; register bool cached = (htype[which_line].ht_flags & HT_CACHED); /* find_article() returns a Nullart if the artnum value is invalid */ if (!(ap = find_article(artnum)) || (ap->flags & AF_MISSING)) return nullstr; if (cached && (s=get_cached_line(ap,which_line,untrim_cache)) != Nullch) return s; if (!fill_cache) return Nullch; if (!parseheader(artnum)) return nullstr; if (cached) return get_cached_line(ap,which_line,untrim_cache); return Nullch; } /* Return a pointer to a cached header line for the indicated article. ** Truncated headers (e.g. from a .thread file) are optionally ignored. */ char * get_cached_line(ap, which_line, no_truncs) register ARTICLE *ap; int which_line; bool_int no_truncs; { register char *s; switch (which_line) { case SUBJ_LINE: if (!ap->subj || (no_truncs && (ap->subj->flags & SF_SUBJTRUNCED))) s = Nullch; else s = ap->subj->str + ((ap->flags & AF_HAS_RE) ? 0 : 4); break; case FROM_LINE: if (no_truncs && (ap->flags & AF_FROMTRUNCED)) s = Nullch; else s = ap->from; break; #ifdef DBM_XREFS case NGS_LINE: #else case XREF_LINE: #endif s = ap->xrefs; break; case MESSID_LINE: s = ap->msgid; break; default: s = Nullch; break; } return s; } void set_subj_line(ap, subj, size) register ARTICLE *ap; register char *subj; /* not yet allocated, so we can tweak it first */ register int size; { HASHDATUM data; SUBJECT *sp; char *newsubj, *subj_start; char *t, *f; int i; while (*subj && *subj != '\n' && (unsigned char)*subj <= ' ') subj++; if (subj != (subj_start = get_subject_start(subj))) { if ((size -= subj_start - subj) < 0) size = 0; ap->flags |= AF_HAS_RE; } if (ap->subj && strnEQ(ap->subj->str+4, subj_start, size)) return; newsubj = safemalloc(size + 4 + 1); strcpy(newsubj, "Re: "); for (t = newsubj + 4, f = subj_start, i = size; i--; ) { if ((unsigned char)*f <= ' ') { while (i && ((unsigned char)*++f <= ' ')) i--, size--; *t++ = ' '; } else if (*f != '\n') *t++ = *f++; else f++; } while (size > 4 && t[-1] == ' ') t--, size--; *t = '\0'; if (ap->subj) { /* This only happens when we freshen truncated subjects */ hashdelete(subj_hash, ap->subj->str+4, strlen(ap->subj->str+4)); free(ap->subj->str); ap->subj->str = newsubj; data.dat_ptr = (char*)ap->subj; hashstore(subj_hash, newsubj + 4, size, data); } else { data = hashfetch(subj_hash, newsubj + 4, size); if (!(sp = (SUBJECT*)data.dat_ptr)) { sp = (SUBJECT*)safemalloc(sizeof (SUBJECT)); bzero((char*)sp, sizeof (SUBJECT)); subject_count++; if ((sp->prev = last_subject) != NULL) sp->prev->next = sp; else first_subject = sp; last_subject = sp; sp->str = newsubj; sp->thread_link = sp; sp->flags = 0; data.dat_ptr = (char*)sp; hashstorelast(data); } else free(newsubj); ap->subj = sp; } } void set_cached_line(ap, which_line, s) register ARTICLE *ap; register int which_line; register char *s; /* already allocated, ready to save */ { char *cp; /* SUBJ_LINE is handled specially above */ switch (which_line) { case FROM_LINE: ap->flags &= ~AF_FROMTRUNCED; if (ap->from) free(ap->from); ap->from = s; break; #ifdef DBM_XREFS case NGS_LINE: if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); if (!index(s, ',')) { /* if no comma, no Xref! */ free(s); s = nullstr; } ap->xrefs = s; break; #else case XREF_LINE: if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); /* Exclude an xref for just this group or "(none)". */ cp = index(s, ':'); if (!cp || !index(cp+1, ':')) { free(s); s = nullstr; } ap->xrefs = s; break; #endif case MESSID_LINE: if (ap->msgid) free(ap->msgid); ap->msgid = s; break; } } int subject_cmp(key, keylen, data) char *key; int keylen; HASHDATUM data; { /* We already know that the lengths are equal, just compare the strings */ return bcmp(key, ((SUBJECT*)data.dat_ptr)->str+4, keylen); } /* see what we can do while they are reading */ #ifdef PENDING void look_ahead() { #ifdef ARTSEARCH register char *h, *s; #ifdef DEBUG if (debug && srchahead) { printf("(%ld)",(long)srchahead); fflush(stdout); } #endif #endif if (ThreadedGroup) { artp = curr_artp; inc_art(selected_only,FALSE); if (artp) parseheader(art); } else #ifdef ARTSEARCH if (srchahead && srchahead < art) { /* in ^N mode? */ char *pattern; pattern = buf+1; strcpy(pattern,": *"); h = pattern + strlen(pattern); interp(h,(sizeof buf) - (h-buf),"%\\s"); { /* compensate for notesfiles */ register int i; for (i = 24; *h && i--; h++) if (*h == '\\') h++; *h = '\0'; } #ifdef DEBUG if (debug & DEB_SEARCH_AHEAD) { fputs("(hit CR)",stdout); fflush(stdout); fgets(buf+128, sizeof buf-128, stdin); printf("\npattern = %s\n",pattern); } #endif if ((s = compile(&srchcompex,pattern,TRUE,TRUE)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s) FLUSH; srchahead = 0; } if (srchahead) { srchahead = art; for (;;) { srchahead++; /* go forward one article */ if (srchahead > lastart) { /* out of articles? */ #ifdef DEBUG if (debug) fputs("(not found)",stdout); #endif break; } if (!was_read(srchahead) && wanted(&srchcompex,srchahead,0)) { /* does the shoe fit? */ #ifdef DEBUG if (debug) printf("(%ld)",(long)srchahead); #endif parseheader(srchahead); break; } if (input_pending()) break; } fflush(stdout); } } else #endif /* ARTSEARCH */ { if (art+1 <= lastart) /* how about a pre-fetch? */ parseheader(art+1); /* look for the next article */ } } #endif /* PENDING */ /* see what else we can do while they are reading */ void cache_until_key() { #ifdef PENDING if (!in_ng || input_pending()) return; untrim_cache = TRUE; sentinel_artp = curr_artp; #ifdef USE_NNTP if (nntp_finishbody(FB_BACKGROUND)) return; #endif /* Prioritize our caching based on what mode we're in */ if (mode == 't') { if (cache_subjects()) if (cache_xrefs()) if (chase_xrefs(TRUE)) if (ThreadedGroup) cache_all_arts(); else cache_unread_arts(); } else { if (!ThreadedGroup || cache_all_arts()) if (cache_subjects()) if (cache_unread_arts()) if (cache_xrefs()) chase_xrefs(TRUE); } setspin(SPIN_OFF); untrim_cache = FALSE; #endif } #ifdef PENDING bool cache_subjects() { register ARTICLE *ap; register ART_NUM an; if (subj_to_get > lastart) return TRUE; setspin(SPIN_BACKGROUND); for (an = subj_to_get, ap = article_ptr(an); an <= lastart; ap++, an++) { if (input_pending()) break; if (!(ap->flags & AF_READ)) fetchsubj(an,FALSE); } subj_to_get = an; return subj_to_get > lastart; } bool cache_xrefs() { register ARTICLE *ap; register ART_NUM an; if (olden_days || xref_to_get > lastart) return TRUE; setspin(SPIN_BACKGROUND); for (an = xref_to_get, ap = article_ptr(an); an <= lastart; ap++, an++) { if (input_pending()) break; if (!(ap->flags & AF_READ)) fetchxref(an,FALSE); } xref_to_get = an; return xref_to_get > lastart; } bool cache_all_arts() { int old_last_cached = last_cached; if (!cached_all_in_range) last_cached = first_cached-1; if (last_cached >= lastart && first_cached <= absfirst) return TRUE; /* turn it on as late as possible to avoid fseek()ing openart */ setspin(SPIN_BACKGROUND); if (last_cached < lastart) { if (ov_opened) ov_data(last_cached+1, lastart, TRUE); if (!art_data(last_cached+1, lastart, TRUE, TRUE)) { last_cached = old_last_cached; return FALSE; } cached_all_in_range = TRUE; } if (first_cached > absfirst) { if (ov_opened) ov_data(absfirst, first_cached-1, TRUE); else art_data(absfirst, first_cached-1, TRUE, TRUE); /* If we got interrupted, make a quick exit */ if (first_cached > absfirst) { last_cached = old_last_cached; return FALSE; } } /* We're all done threading the group, so if the current article is ** still in doubt, tell them it's missing. */ if (curr_artp && !(curr_artp->flags & AF_CACHED) && !input_pending()) pushchar('\f' | 0200); /* A completely empty group needs a count & a sort */ if (mode != 't' && !article_count && !selected_only) thread_grow(); return TRUE; } bool cache_unread_arts() { if (last_cached >= lastart) return TRUE; setspin(SPIN_BACKGROUND); return art_data(last_cached+1, lastart, TRUE, FALSE); } #endif bool art_data(first, last, cheating, all_articles) ART_NUM first, last; bool_int cheating; bool_int all_articles; { register ARTICLE *ap; register ART_NUM i; int cachemask = (ThreadedGroup ? AF_THREADED : AF_CACHED) + (all_articles? 0 : AF_READ); setspin(cheating? SPIN_BACKGROUND : SPIN_FOREGROUND); assert(first >= absfirst && last <= lastart); for (i = first, ap = article_ptr(first); i <= last; i++, ap++) { if (ap->flags & cachemask) continue; if (int_count) { int_count = 0; break; } if (cheating) { if (input_pending()) break; /* If the current article is no longer a '?', let them know. */ if (curr_artp != sentinel_artp) { pushchar('\f' | 0200); break; } } /* This parses the header which will cache/thread the article */ (void) parseheader(i); } setspin(SPIN_POP); if (--i > last_cached) last_cached = i; if (i == last) { if (first < first_cached) first_cached = first; return TRUE; } return FALSE; } bool cache_range(first,last) ART_NUM first; ART_NUM last; { bool success = TRUE; bool all_arts = (sel_rereading || thread_always); ART_NUM count = 0; if (sel_rereading && !cached_all_in_range) { first_cached = first; last_cached = first-1; } if (first < first_cached) count = first_cached-first; if (last > last_cached) count += last-last_cached; if (!count) return TRUE; if (first_cached > last_cached) { if (sel_rereading) { if (first_subject) count -= toread[ng]; } else if (first == firstart && last == lastart && !all_arts) count = toread[ng]; } printf("\n%sing %ld article%s.", ThreadedGroup? "Thread" : "Cach", (long)count, count==1? nullstr : "s") FLUSH; setspin(SPIN_FOREGROUND); if (first < first_cached) { if (ov_opened) { ov_data(absfirst,first_cached-1,FALSE); if ((success = (first_cached == absfirst)) != FALSE) ov_close(); } else { success = art_data(first, first_cached-1, FALSE, all_arts); cached_all_in_range = (all_arts && success); } } if (success && last_cached < last) { if (ov_opened) ov_data(last_cached+1, last, FALSE); success = art_data(last_cached+1, last, FALSE, all_arts); cached_all_in_range = (all_arts && success); } setspin(SPIN_POP); return success; } void clear_article(ap) register ARTICLE *ap; { if (ap->from) free(ap->from); if (ap->msgid) free(ap->msgid); if (ap->xrefs && ap->xrefs != nullstr) free(ap->xrefs); } trn-3.6/newsetup.SH0000664000000000000000000000635007041327652011175 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting newsetup (with variable substitutions)" $spitshell >newsetup <>newsetup <<'!NO!SUBS!' dotdir="${DOTDIR-${HOME-$LOGDIR}}" newsrc="$dotdir/.newsrc" # tmp="${TMPDIR-/tmp}" subs="$lib/subscriptions" tmpsubs=`tempfile -p trnsubs` # "$tmp/trnsubs.$$" $cat <<'EOH' Welcome to trn. Here's some important things to remember: o Trn is an extension of rn and has a similar command syntax. o To access all the new features, specify the options -x and -X. These options MAY be on by default, but it won't hurt to be redundant. o Single-character commands don't require a carriage return -- only commands that let you type in an argument. o At ANY prompt, you may type 'h' for help. There are many different help menus, depending on the context. Also, typing h in the middle of a multi-character command will list escape substitutions. o Typing a space to any prompt means to do the normal thing. You could spend all day reading news and never hit anything but the space bar. o If you have never used the news system before, you may find the articles in news.announce.newusers to be helpful. o Please consult the man page for complete information. EOH $rm -f $newsrc-old $echo "Creating $newsrc to be used by news programs." if $test -s "$newsrc"; then $echo "Saving your current one as $newsrc-old..." $mv -f $newsrc $newsrc-old fi if $test -r /etc/news/subscriptions; then $cp /etc/news/subscriptions $newsrc elif $test -r $subs; then $cp $subs $newsrc else ($nntplist -o $tmpsubs subscriptions) >/dev/null 2>&1 if $test -s $tmpsubs; then $cp $tmpsubs $newsrc else $cat <$newsrc news.announce.newusers: EOM fi $rm -f $tmpsubs fi $cat <<'EOH' Done. To add new group use "a pattern" or "g newsgroup.name". To get rid of newsgroups you aren't interested in, use the 'u' command. EOH !NO!SUBS! $eunicefix newsetup chmod 755 newsetup trn-3.6/nntpclient.c0000664000000000000000000001113005654303432011377 0ustar /* $Id: nntpclient.c,v 3.0 1991/11/22 04:12:21 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #ifdef USE_NNTP #include "INTERN.h" #include "nntpclient.h" void finalize _((int)); #ifdef NNTP_HANDLE_TIMEOUT char nntp_handle_timeout _((bool_int)); #endif #ifdef NNTP_HANDLE_AUTH_ERR char nntp_handle_auth_err _((bool_int)); #endif #define CANTPOST \ "NOTE: This machine does not have permission to post articles.\n\n" #define CANTUSE \ "This machine does not have permission to use the %s news server.\n\n" int nntp_connect(verbose) bool_int verbose; { char *server, filebuf[128]; int response; if ((server = getenv("NNTPSERVER")) == Nullch) server = SERVER_NAME; if (server[0] == '/') { register FILE *fp; if ((fp = fopen(server, "r")) != Nullfp) { server = Nullch; while (fgets(filebuf, sizeof filebuf, fp) != Nullch) { if (*filebuf == '\n' || *filebuf == '#') continue; if ((server = index(filebuf, '\n')) != Nullch) *server = '\0'; server = filebuf; break; } fclose(fp); } else server = Nullch; if (server == Nullch) { sprintf(ser_line, "\ Couldn't get name of news server from %s\n\ Either fix this file, or put NNTPSERVER in your environment.\n", SERVER_NAME); nntp_init_error(ser_line); return 0; } } switch (response = server_init(server)) { case NNTP_GOODBYE_VAL: if (atoi(ser_line) == response) { char tmpbuf[LBUFLEN]; sprintf(tmpbuf,"News server %s unavailable: %s\n",server,&ser_line[4]); nntp_init_error(tmpbuf); return 0; } case -1: sprintf(ser_line,"News server %s unavailable, try again later.\n",server); nntp_init_error(ser_line); return 0; case NNTP_ACCESS_VAL: sprintf(ser_line,CANTUSE,server); nntp_init_error(ser_line); return 0; case NNTP_NOPOSTOK_VAL: if (verbose) { nntp_advise(CANTPOST); } /* FALL THROUGH */ case NNTP_POSTOK_VAL: break; default: sprintf(ser_line,"Unknown response code %d from %s.\n", response, server); nntp_init_error(ser_line); return 0; } return 1; } void nntp_command(buf) char *buf; { #if defined(DEBUG) && defined(FLUSH) if (debug & DEB_NNTP) printf(">%s\n", buf) FLUSH; #endif #if defined(NNTP_HANDLE_TIMEOUT) || defined(NNTP_HANDLE_AUTH_ERR) strcpy(last_command, buf); #endif fprintf(ser_wr_fp, "%s\r\n", buf); fflush(ser_wr_fp); } #ifdef NNTP_HANDLE_TIMEOUT extern char *instr(); #endif char nntp_check(strict) bool_int strict; { int n; #ifdef HAS_SIGHOLD sighold(SIGINT); #endif n = (fgets(ser_line, sizeof ser_line, ser_rd_fp) == NULL)? -1 : 0; #ifdef HAS_SIGHOLD sigrelse(SIGINT); #endif if (n < 0) { nntp_close(FALSE); nntp_error("\nUnexpected close of server socket.\n"); #ifdef NNTP_ERROR_IS_FATAL finalize(1); #else return NNTP_CLASS_FATAL; #endif } n = strlen(ser_line); if (n >= 2 && ser_line[n-1] == '\n' && ser_line[n-2] == '\r') ser_line[n-2] = '\0'; #if defined(DEBUG) && defined(FLUSH) if (debug & DEB_NNTP) printf("<%s\n", ser_line) FLUSH; #endif #ifdef NNTP_HANDLE_AUTH_ERR if (atoi(ser_line) == NNTP_AUTH_NEEDED_VAL) { return nntp_handle_auth_err(strict); } #endif #ifdef NNTP_HANDLE_TIMEOUT if (atoi(ser_line) == NNTP_TMPERR_VAL && instr(ser_line,"timeout",FALSE)) { /* See if this was really a timeout */ return nntp_handle_timeout(strict); } #endif if (strict && *ser_line == NNTP_CLASS_FATAL) { /* Fatal error */ char tmpbuf[LBUFLEN]; sprintf(tmpbuf,"\n%s\n",ser_line); nntp_error(tmpbuf); #ifdef NNTP_ERROR_IS_FATAL finalize(1); #endif } return *ser_line; } int nntp_gets(buf, len) char *buf; int len; { int n; #ifdef HAS_SIGHOLD sighold(SIGINT); #endif n = (fgets(buf, len, ser_rd_fp) == NULL)? -1 : 0; #ifdef HAS_SIGHOLD sigrelse(SIGINT); #endif if (n < 0) { nntp_close(FALSE); nntp_error("\nUnexpected close of server socket.\n"); #ifdef NNTP_ERROR_IS_FATAL finalize(1); #else return -1; #endif } n = strlen(buf); if (n >= 2 && buf[n-1] == '\n' && buf[n-2] == '\r') { buf[n-2] = '\0'; return 1; } return 0; } void nntp_close(send_quit) bool_int send_quit; { if (send_quit && ser_wr_fp != NULL && ser_rd_fp != NULL) { nntp_command("QUIT"); nntp_check(FALSE); } /* the nntp_check() above might have closed these already. */ if (ser_wr_fp != NULL && ser_rd_fp != NULL) { fclose(ser_wr_fp); ser_wr_fp = NULL; fclose(ser_rd_fp); ser_rd_fp = NULL; } } #endif /* USE_NNTP */ trn-3.6/ngsrch.h0000664000000000000000000000200105420174102010476 0ustar /* $Id: ngsrch.h,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifdef NGSEARCH #define NGS_ABORT 0 #define NGS_FOUND 1 #define NGS_INTR 2 #define NGS_NOTFOUND 3 #define NGS_ERROR 4 EXT bool ng_doread INIT(FALSE); /* search read newsgroups? */ #endif void ngsrch_init _((void)); #ifdef NGSEARCH int ng_search _((char*,int)); bool ng_wanted _((void)); #endif #ifdef NGSORONLY char *ng_comp _((COMPEX*,char*,bool_int,bool_int)); #endif trn-3.6/charsubst.c0000664000000000000000000001302205656522666011237 0ustar /* $Id: charsubst.c,v 3.0 1993/12/21 21:54:40 davison Trn $ */ /* * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "artstate.h" #include "INTERN.h" #include "charsubst.h" #ifdef CHARSUBST /* TeX encoding table - gives ISO char for "x (x=32..127) */ static unsigned char textbl[96] = { 0, 0,'"', 0, 0, 0, 0,'"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,214, 0, 0, 0, 0, 0,220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '"',228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,246, 0, 0, 0,223, 0,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static void Latin1toASCII _((unsigned char*,unsigned char*,int)); static char texchar = '\0'; int putsubstchar(c, limit, outputok) int c; int limit; bool_int outputok; { unsigned char d, oc[2], nc[5]; int t, i = 0; switch (*charsubst) { case 'm': t = 1; goto doconvert; case 'a': t = 2; /* FALL THROUGH */ doconvert: oc[0] = (unsigned char)c; oc[1] = '\0'; Latin1toASCII(oc, nc, t); if ((i = strlen((char*)nc)) <= limit) { if (outputok) for (t = 0; t < i; t++) putchar((char)nc[t]); } else i = -1; break; case 't': if (c == '\\' || c == '"') { if (texchar && (c == '\\' || texchar != '\\')) { if (outputok) putchar(texchar); i++; } texchar = (char)c; break; } else if (texchar == '\\') { if (outputok) putchar('\\'); if (limit == 1) { i = -2; break; } i++; } else if (texchar == '"') { if (c < 32 || c > 128) d = '\0'; else d = textbl[c-32]; texchar = '\0'; if (d) c = d; else { if (outputok) putchar('"'); if (limit == 1) { i = -2; break; } i++; } } /* FALL THROUGH */ default: if (outputok) putchar(c); i++; break; } return i; } char * current_charsubst() { static char *show; switch(*charsubst) { case 'm': #ifdef VERBOSE IF(verbose) show = "[ISO->monosp] "; ELSE #endif #ifdef TERSE show = "[M] "; #endif break; case 'a': #ifdef VERBOSE IF(verbose) show = "[ISO->ASCII] "; ELSE #endif #ifdef TERSE show = "[A] "; #endif break; case 't': #ifdef VERBOSE IF(verbose) show = "[TeX->ISO] "; ELSE #endif #ifdef TERSE show = "[T] "; #endif break; default: show = ""; break; } return show; } void strcharsubst(inb, outb) char *inb, *outb; { switch(*charsubst) { case 'a': Latin1toASCII((unsigned char*)inb, (unsigned char*)outb, 2); break; default: strcpy(outb, inb); } } /* The following is an adapted version of iso2asc by Markus Kuhn, University of Erlangen, Germany */ #define ISO_TABLES 2 /* originally: 7 */ /* Conversion tables for displaying the G1 set (0xa0-0xff) of ISO Latin 1 (ISO 8859-1) with 7-bit ASCII characters. Version 1.2 -- error corrections are welcome Table Purpose 0 universal table for many languages 1 single-spacing universal table 2 table for Danish, Dutch, German, Norwegian and Swedish 3 table for Danish, Finnish, Norwegian and Swedish using the appropriate ISO 646 variant. 4 table with RFC 1345 codes in brackets 5 table for printers that allow overstriking with backspace Markus Kuhn */ /* In this version, I have taken out all tables except 1 and 2 -ot */ #define SUB NULL /* used if no reasonable ASCII string is possible */ static char *iso2asc[ISO_TABLES][96] = { { " ","!","c",SUB,SUB,"Y","|",SUB,"\"","c","a","<","-","-","R","-", " ",SUB,"2","3","'","u","P",".",",","1","o",">",SUB,SUB,SUB,"?", "A","A","A","A","A","A","A","C","E","E","E","E","I","I","I","I", "D","N","O","O","O","O","O","x","O","U","U","U","U","Y","T","s", "a","a","a","a","a","a","a","c","e","e","e","e","i","i","i","i", "d","n","o","o","o","o","o",":","o","u","u","u","u","y","t","y" },{ " ","!","c",SUB,SUB,"Y","|",SUB,"\"","(c)","a","<<","-","-","(R)","-", " ","+/-","2","3","'","u","P",".",",","1","o",">>"," 1/4"," 1/2"," 3/4","?", "A","A","A","A","Ae","Aa","AE","C","E","E","E","E","I","I","I","I", "D","N","O","O","O","O","Oe","x","Oe","U","U","U","Ue","Y","Th","ss", "a","a","a","a","ae","aa","ae","c","e","e","e","e","i","i","i","i", "d","n","o","o","o","o","oe",":","oe","u","u","u","ue","y","th","ij" } }; /* * Transform an 8-bit ISO Latin 1 string iso into a 7-bit ASCII string asc * readable on old terminals using conversion table t. * * worst case: strlen(iso) == 4*strlen(asc) */ static void Latin1toASCII(iso, asc, t) unsigned char *iso, *asc; int t; { char *p, **tab; if (iso == NULL || asc == NULL) return; t--; /* offset correction -ot */ tab = iso2asc[t] - 0xa0; while (*iso) { if (*iso > 0x9f) { p = tab[*iso++]; if (p) { while (*p) *asc++ = *p++; } } else *asc++ = *iso++; } *asc = 0; } #endif trn-3.6/config.h.SH0000664000000000000000000003410207205276715011017 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting config.h (with variable substitutions)" sed <config.h -e 's!^#undef\(.*\)/\*!/\*#define\1\*//\*!' /* * This file was produced by running the config.h.SH script, which * gets its values from config.sh, which is generally produced by * running Configure. * * Feel free to modify any of this as the need arises. Note, however, * that running config.h.SH again will wipe out any changes you've made. * For a more permanent change edit config.sh and rerun config.h.SH. * * \$Id: Config_h.U,v 3.0.1.2 1993/08/24 12:13:20 ram Exp $ */ /* Configuration time: $cf_time * Configured by: $cf_by * Target system: $myuname */ #ifndef _config_h_ #define _config_h_ /* EUNICE: * This symbol, if defined, indicates that the program is being compiled * under the EUNICE package under VMS. The program will need to handle * things like files that don't go away the first time you unlink them, * due to version numbering. It will also need to compensate for lack * of a respectable link() command. */ /* VMS: * This symbol, if defined, indicates that the program is running under * VMS. It is currently only set in conjunction with the EUNICE symbol. */ #$d_eunice EUNICE /**/ #$d_eunice VMS /**/ /* HAS_GETPWENT: * This symbol, if defined, indicates that the getpwent() routine * should be used instead of the getpw() routine. */ #$d_getpwent HAS_GETPWENT /**/ /* HAS_TERMLIB: * This symbol, when defined, indicates that termlib-style routines * are available. There is nothing to include. */ #$d_havetlib HAS_TERMLIB /**/ /* INTERNET: * This symbol, if defined, indicates that there is a mailer available * which supports internet-style addresses (user@site.domain). */ #$d_internet INTERNET /**/ /* HAS_MEMCMP: * This symbol, if defined, indicates that the memcmp routine is available * to compare blocks of memory. */ #$d_memcmp HAS_MEMCMP /**/ /* HAS_MEMCPY: * This symbol, if defined, indicates that the memcpy routine is available * to copy blocks of memory. */ #$d_memcpy HAS_MEMCPY /**/ /* HAS_MEMSET: * This symbol, if defined, indicates that the memset routine is available * to set blocks of memory. */ #$d_memset HAS_MEMSET /**/ /* NEWS_ADMIN: * This symbol, if defined, contains the login name of the news * administrator. */ #$d_newsadm NEWS_ADMIN "$newsadmin" /**/ /* NOLINEBUF: * This symbol, if defined, indicates that stdout is not buffered, so that * the program can call setbuf() or setlinebuf() for efficiency. */ #$d_nolnbuf NOLINEBUF /**/ /* NORMSIG: * This symbol, if defined, indicates that normal signal handling routines * should be used, as opposed to the ones in 4.1bsd (sigset, etc.). */ #$d_normsig NORMSIG /**/ /* HAS_RDCHK: * This symbol, if defined, indicates that the rdchk routine is available * to find out if there is input pending on an IO channel. Generally * the routine is used only if FIONREAD and O_NDELAY aren't available. */ #$d_rdchk HAS_RDCHK /**/ /* HAS_RENAME: * This symbol, if defined, indicates that the rename routine is available * to rename files. Otherwise you should do the unlink(), link(), unlink() * trick. */ #$d_rename HAS_RENAME /**/ /* HAS_SIGBLOCK: * This symbol, if defined, indicates that the sigblock routine is * available to block signal reception. */ #$d_sigblock HAS_SIGBLOCK /**/ /* HAS_SIGHOLD: * This symbol, if defined, indicates that the sighold routine is * available to hold signals. */ #$d_sighold HAS_SIGHOLD /**/ /* HAS_STRCASECMP: * This symbol, if defined, indicates that the strcasecmp() routine is * available for case-insensitive string compares. */ #$d_strccmp HAS_STRCASECMP /**/ /* HAS_VFORK: * This symbol, if defined, indicates that vfork() exists. */ #$d_vfork HAS_VFORK /**/ /* Signal_t: * This symbol's value is either "void" or "int", corresponding to the * appropriate return type of a signal handler. Thus, you can declare * a signal handler using "Signal_t (*handler)()", and define the * handler using "Signal_t handler(sig)". */ #define Signal_t $signal_t /* Signal handler's return type */ /* DEFEDITOR: * This symbol contains the full pathname of the default editor. */ #define DEFEDITOR "$defeditor" /**/ /* I_DIRENT: * This symbol, if defined, indicates to the C program that it should * include . Using this symbol also triggers the definition * of the Direntry_t define which ends up being 'struct dirent' or * 'struct direct' depending on the availability of . */ #$i_dirent I_DIRENT /**/ #ifdef I_DIRENT #define Direntry_t struct dirent #else #define Direntry_t struct direct #endif /* I_STDLIB: * This symbol, if defined, indicates that exists and should * be included. */ #$i_stdlib I_STDLIB /**/ /* I_STRING: * This symbol, if defined, indicates to the C program that it should * include (USG systems) instead of (BSD systems). */ #$i_string I_STRING /**/ /* I_SYS_DIR: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_sysdir I_SYS_DIR /**/ /* I_SYS_IOCTL: * This symbol, if defined, indicates that exists and should * be included. Otherwise, include or . */ /* I_SYS_FILIO: * This symbol, if defined, indicates that exists and * should be included instead of . */ #$i_sysioctl I_SYS_IOCTL /**/ #$i_sysfilio I_SYS_FILIO /**/ /* I_SYS_NDIR: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_sysndir I_SYS_NDIR /**/ /* I_TERMIO: * This symbol, if defined, indicates that the program should include * rather than . There are also differences in * the ioctl() calls that depend on the value of this symbol. */ /* I_TERMIOS: * This symbol, if defined, indicates that the program should include * the POSIX termios.h rather than sgtty.h or termio.h. * There are also differences in the ioctl() calls that depend on the * value of this symbol. */ /* I_SGTTY: * This symbol, if defined, indicates that the program should include * rather than . There are also differences in * the ioctl() calls that depend on the value of this symbol. */ #$i_termio I_TERMIO /**/ #$i_termios I_TERMIOS /**/ #$i_sgtty I_SGTTY /**/ /* I_UNISTD: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_unistd I_UNISTD /**/ /* I_VFORK: * This symbol, if defined, indicates to the C program that it should * include vfork.h. */ #$i_vfork I_VFORK /**/ /* MAILFILE: * This symbol contains the interpretable name of the mail spool file * for the current user. The program must be prepared to substitute * the HOME directory for %~, and the login id for %L. */ #define MAILFILE "$mailfile" /**/ /* MBOXCHAR: * This symbol contains a character which will match the beginning * of a mailbox file. */ #define MBOXCHAR '$mboxchar' /**/ /* PASSNAMES: * This symbol, if defined, indicates that full names are stored in * the /etc/passwd file. */ /* BERKNAMES: * This symbol, if defined, indicates that full names are stored in * the /etc/passwd file in Berkeley format (name first thing, everything * up to first comma, with & replaced by capitalized login id, yuck). */ #$d_passnames PASSNAMES /* (undef to take name from ~/.fullname) */ #$d_berknames BERKNAMES /* (that is, ":name,stuff:") */ /* ORGNAME: * This symbol contains either the organizaton name or the full pathname * of a file containing the organization name, which the program must * be prepared to open and substitute the contents of. */ #define ORGNAME "$orgname" /**/ /* PREFSHELL: * This symbol contains the full name of the preferred user shell on this * system. Usual values are /bin/csh, /bin/ksh, /bin/sh. */ #define PREFSHELL "$prefshell" /**/ /* ROOTID: * This symbol contains the uid of root, normally 0. */ #define ROOTID $rootid /**/ /* ACTIVE: * The name of the active file for the news system. This file contains * the list of active newsgroups. The name may have ~ on the front. */ /* ACTIVE_TIMES: * The name of the active.times file for the news system. */ #define ACTIVE "$active" /**/ #$d_acttimes ACTIVE_TIMES "$acttimes" /**/ /* HAS_FTIME: * This symbol, if defined, indicates that the ftime() routine exists. * It is basically a sub-second accuracy clock, but is less accurate * than gettimeofday(2) anyway. The type "Timeval" should be used to * refer to "struct timeb". */ #$d_ftime HAS_FTIME /**/ /* HAS_GETHOSTNAME: * This symbol, if defined, indicates that the C program may use the * gethostname() routine to derive the host name. See also HAS_UNAME * and PHOSTCMD. */ /* HAS_UNAME: * This symbol, if defined, indicates that the C program may use the * uname() routine to derive the host name. See also HAS_GETHOSTNAME * and PHOSTCMD. */ /* PHOSTCMD: * This symbol, if defined, indicates that the C program may use the * contents of PHOSTCMD as a command to feed to the popen() routine * to derive the host name. See also HAS_GETHOSTNAME and HAS_UNAME. * Note that the command uses a fully qualified path, so that it is safe * even if used by a process with super-user privileges. */ #$d_gethname HAS_GETHOSTNAME /**/ #$d_uname HAS_UNAME /**/ #$d_phostcmd PHOSTCMD "$aphostcmd" /* How to get the host name */ /* HAS_GETWD: * This symbol, if defined, indicates that the getwd routine is * available to get the working directory. */ /* HAS_GETCWD: * This symbol, if defined, indicates that the getcwd routine is * available. This is never defined if getwd is found first. */ #$d_getwd HAS_GETWD /**/ #$d_getcwd HAS_GETCWD /**/ /* IGNOREORG: * This symbol, if defined, indicates that the ORGANIZATION environment * variable does not contain an organization name. */ #$d_ignoreorg IGNOREORG /**/ /* MIMESHOW: * This symbol points to the program to run to show a mime article. */ /* MIMESTORE: * This symbol points to the program to run to store a mime article. */ #$d_mimeshow MIMESHOW "$mimeshow" /**/ #$d_mimestore MIMESTORE "$mimestore" /**/ /* USE_NNTP: * This symbol, if defined, indicates that NNTP should be used. */ /* USE_GENAUTH: * This symbol, if defined, indicates that authinfo generic * authentication is to be supported. */ /* SERVER_NAME: * When using NNTP, this symbol indicates the server name or a * file to open to read the server name. */ /* USE_XTHREAD: * This symbol, if defined, indicates that .thread files can be * gotten via NNTP. It is never defined if USE_NNTP is undefined. */ /* USE_XOVER: * This symbol, if defined, indicates that .overview files can be * gotten via NNTP. It is never defined if USE_NNTP is undefined. */ #$d_nntp USE_NNTP /**/ #$d_genauth USE_GENAUTH /**/ #define SERVER_NAME "$servername" /**/ #$d_xdata USE_XTHREAD /**/ #$d_xdata USE_XOVER /**/ /* void: * This symbol is used for void functions. On implementations which * support void appropriately, its value is "void". Otherwise, its * value should be set to "int". */ #$d_novoid void int /**/ /* size_t: * This symbol is defined as an int if no size_t definition exists. */ #$d_sizet size_t int /**/ /* HAS_STRCHR: * This symbol is defined to indicate that the strchr()/strrchr() * functions are available for string searching. If not, try the * index()/rindex() pair. */ #$d_strchr HAS_STRCHR /**/ /* HAS_STRFTIME: * This symbol, if defined, indicates that the strftime routine is * available to format locale-specific times. */ #$d_strftime HAS_STRFTIME /**/ /* EMULATE_NDIR: * This symbol, if defined, indicates that the program should compile * the ndir.c code provided with the package. */ /* I_NDIR: * This symbol, if defined, indicates that the program should include the * system's version of ndir.h, rather than the one with this package. */ #$d_usendir EMULATE_NDIR /**/ #$d_libndir I_NDIR /**/ /* I_PTEM: * This symbol, if defined, indicates to the C program that it should * include ptem.h. */ #$i_ptem I_PTEM /**/ /* I_TIME: * This symbol, if defined, indicates to the C program that it should * include . */ /* I_SYS_TIME: * This symbol, if defined, indicates to the C program that it should * include . */ #$i_time I_TIME /**/ #$i_systime I_SYS_TIME /**/ /* NEWSLIB: * This symbol contains the name of the directory serving as the news * library. The program must be prepared to do ~ expansion on it. */ #define NEWSLIB "$newslib" /**/ /* NEWSSPOOL: * This symbol contains the directory name where news articles are * spooled. The program must be prepared to do ~ expansion on it. */ #define NEWSSPOOL "$newsspool" /**/ /* PHOSTNAME: * This symbol contains the posting host's name or a file from which * to read its name. */ #define PHOSTNAME "$phost" /**/ /* PRIVLIB: * This symbol contains the name of the private library for this package. * The library is private in the sense that it needn't be in anyone's * execution path, but it should be accessible by the world. The program * should be prepared to do ~ expansion. */ #define PRIVLIB "$privlib" /**/ /* THREAD_DIR: * This symbol indicates where the thread files go. */ /* OVERVIEW_DIR: * This symbol indicates where the overview files go. */ /* USE_MT: * This symbol indicates if thread file support is desired. */ /* USE_OV: * This symbol indicates if overview file support is desired. */ #define THREAD_DIR "$threaddir" /**/ #define OVERVIEW_DIR "$overviewdir" /**/ #$d_usemt USE_MT /**/ #$d_useov USE_OV /**/ /* THREAD_INIT: * This symbol indicates we act like trn no matter what our name is. */ /* SELECT_INIT: * This symbol indicates we default to the selector for group entry. */ #define THREAD_INIT $trn_init #define SELECT_INIT $trn_select /*#define LONG_THREAD_NAMES *//**/ #define CANCEL "$inewsloc -h <%h" #define SPEED_OVER_MEM /* use more memory to run faster */ /*#define ANCIENT_NEWS *//* if your B news system is <= 2.10.1 */ /*Added for Debian GNU/Linux. Does anyone else use this trn version? :*/ #define NEWSETUP "$bin/newsetup" #define NEWSGROUPS "$bin/newsgroups" #define MAILPOSTER "QUOTECHARS=%I $bin/Rnmail -h %h" #define NEWSPOSTER "QUOTECHARS=%I $bin/Pnews -h %h" #endif !GROK!THIS! trn-3.6/common.h0000664000000000000000000006633610332162114010525 0ustar /* $Id: common.h,v 3.0 1992/02/23 21:25:39 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include #include #include #ifndef S_ISDIR #define S_ISDIR(m) ( ((m) & S_IFMT) == S_IFDIR ) #endif #ifndef S_ISCHR #define S_ISCHR(m) ( ((m) & S_IFMT) == S_IFCHR ) #endif #ifndef S_ISREG #define S_ISREG(m) ( ((m) & S_IFMT) == S_IFREG ) #endif #include #include "config.h" /* generated by installation script */ #ifndef isalnum # define isalnum(c) (isalpha(c) || isdigit(c)) #endif #include #include #ifdef I_SYS_FILIO # include #else # ifdef I_SYS_IOCTL # include # endif #endif #ifdef I_VFORK # include #endif #include #ifdef I_TERMIO # include #else # ifdef I_TERMIOS # include # if !defined (O_NDELAY) # define O_NDELAY O_NONBLOCK /* Posix-style non-blocking i/o */ # endif # else # include # endif #endif #ifdef HAS_GETPWENT # include #endif #ifdef I_PTEM #include #include #endif #ifdef I_UNISTD #include #endif #ifdef I_STDLIB #include #else char *malloc(); char *realloc(); char *getenv(); #endif #ifdef I_STRING #include #else #include #endif #ifdef I_TIME #include #endif #ifdef I_SYS_TIME #include #endif #define BITSPERBYTE 8 #define LBUFLEN 2048 /* line buffer length */ /* (don't worry, .newsrc lines can exceed this) */ #define CBUFLEN 512 /* command buffer length */ #define PUSHSIZE 256 #define MAXFILENAME 512 #define LONGKEY 25 /* longest keyword */ /* (currently "Content-Transfer-Encoding") */ #define FINISHCMD 0177 /* some handy defs */ #define bool char #define bool_int int #define char_int int #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #define Null(t) ((t)0) #define Nullch Null(char*) #define Nullfp Null(FILE*) #define Ctl(ch) (ch & 037) #define strNE(s1,s2) (strcmp(s1,s2)) #define strEQ(s1,s2) (!strcmp(s1,s2)) #define strnNE(s1,s2,l) (strncmp(s1,s2,l)) #define strnEQ(s1,s2,l) (!strncmp(s1,s2,l)) /* Things we can figure out ourselves */ #ifdef SIGTSTP # define BERKELEY /* include job control signals? */ #endif #if defined(FIONREAD) || defined(HAS_RDCHK) || defined(O_NDELAY) # define PENDING #endif #ifdef EUNICE # define LINKART /* add 1 level of possible indirection */ # define UNLINK(victim) while (!unlink(victim)) #else # define UNLINK(victim) unlink(victim) #endif /* Valid substitutions for strings marked with % comment are: * %a Current article number * %A Full name of current article (%P/%c/%a) * (if LINKART defined, is the name of the real article) * %b Destination of a save command, a mailbox or command * %B The byte offset to the beginning of the article for saves * with or without the header * %c Current newsgroup, directory form * %C Current newsgroup, dot form * %d %P/%c * %D Old Distribution: line * %e Extract program * %E Extract destination directory * %f Old From: line or Reply-To: line * %F Newsgroups to followup to from Newsgroups: and Followup-To: * %h Name of header file to pass to mail or news poster * %H Host name (yours) * %i Old Message-I.D.: line, with <> * %I Inclusion indicator * %l News administrator login name * %L Login name (yours) * %m The current mode of trn. * %M Number of articles marked with M * %n Newsgroups from source article * %N Full name (yours) * %o Organization (yours) * %O Original working directory (where you ran trn from) * %p Your private news directory (-d switch) * %P Public news spool directory (NEWSSPOOL) * %r Last reference (parent article id) * %q The last quoted input (via %"). * %R New references list * %s Subject, with all Re's and (nf)'s stripped off * %S Subject, with one Re stripped off * %t New To: line derived from From: and Reply-To (Internet always) * %T New To: line derived from Path: * %u Number of unread articles * %U Number of unread articles disregarding current article * %v Number of unselected articles disregarding current article * %W The thread directory root * %x News library directory, usually /usr/lib/news * %X Rn library directory, usually %x/rn * %y The tmp directory to use * %z Size of current article in bytes. * %Z Number of selected threads. * %~ Home directory * %. Directory containing . files * %# count of articles saved in current command (from 1 to n) * %$ current process number * %{name} Environment variable "name". %{name-default} form allowed. * %[name] Header line beginning with "Name: ", without "Name: " * %"prompt" * Print prompt and insert what is typed. * %`command` * Insert output of command. * %(test_text=pattern?if_text:else_text) * Substitute if_text if test_text matches pattern, otherwise * substitute else_text. Use != for negated match. * % substitutions are done on test_text, if_text, and else_text. * (Note: %() only works if CONDSUB defined.) * %digit Substitute the text matched by the nth bracket in the last * pattern that had brackets. %0 matches the last bracket * matched, in case you had alternatives. * %? Insert a space unless the entire result is > 79 chars, in * which case the space becomes a newline. * * Put ^ in the middle to capitalize the first letter: %^C = Rec.humor * Put _ in the middle to capitalize last component: %_c = net/Jokes * Put \ in the middle to quote regexp and % characters in the result * Put > in the middle to return the address portion of a name. * Put ) in the middle to return the comment portion of a name. * Put ' in the middle to protect "'"s in arguments you've put in "'"s. * Put :FMT in the middle to format the result: %:-30.30t * * ~ interpretation in filename expansion happens after % expansion, so * you could put ~%{NEWSLOGNAME-news} and it will expand correctly. */ /* *** System Dependent Stuff *** */ /* NOTE: many of these are defined in the config.h file */ /* name of organization */ #ifndef ORGNAME # define ORGNAME "ACME Widget Company, Widget Falls, Southern North Dakota" #endif #ifndef MBOXCHAR # define MBOXCHAR 'F' /* how to recognize a mailbox by 1st char */ #endif #ifndef ROOTID # define ROOTID 0 /* uid of superuser */ #endif #ifdef NORMSIG # define sigset signal # define sigignore(sig) signal(sig,SIG_IGN) #endif #ifndef LOGDIRFIELD # define LOGDIRFIELD 6 /* Which field (origin 1) is the */ /* login directory in /etc/passwd? */ /* (If it is not kept in passwd, */ /* but getpwnam() returns it, */ /* define the symbol HAS_GETPWENT) */ #endif #ifndef GCOSFIELD # define GCOSFIELD 5 #endif #ifndef NEGCHAR # define NEGCHAR '!' #endif /* Space conservation section */ /* To save D space, cut down size of NGMAX and VARYSIZE. */ #define NGMAX 100 /* number of newsgroups allowed on command line */ /* undefine ONLY symbol to disable "only" feature */ #define VARYSIZE 256 /* this makes a block 1024 bytes long in DECville */ /* (used by virtual array routines) */ /* Undefine any of the following features to save both I and D space */ /* In general, earlier ones are easier to get along without */ #define CUSTOMLINES /* include code for HIDELINE and PAGESTOP */ #define WORDERASE /* enable ^W to erase a word */ #define MAILCALL /* check periodically for mail */ #define CLEAREOL /* use clear to end-of-line instead of clear screen */ #define NOFIREWORKS /* keep whole screen from flashing on certain */ /* terminals such as older Televideos */ #define VERIFY /* echo the command they just typed */ #define HASHNG /* hash newsgroup lines for fast lookup-- */ /* linear search used if not defined */ #define CONDSUB /* allow %(cond?text:text) */ #define BACKTICK /* allow %`command` */ #define PROMPTTTY /* allow %"prompt" */ #define ULSMARTS /* catch _^H in text and do underlining */ #define TERMMOD /* allow terminal type modifier on switches */ #define BAUDMOD /* allow baudrate modifier on switches */ #define GETLOGIN /* use getlogin() routine as backup to environment */ /* variables USER or LOGNAME */ #define ORGFILE /* if organization begins with /, look up in file */ #define TILDENAME /* allow ~logname expansion */ #define SETENV /* allow command line environment variable setting */ #define MAKEDIR /* use our makedir() instead of shell script */ #define MEMHELP /* keep help messages in memory */ #define VERBOSE /* compile in more informative messages */ #define TERSE /* compile in shorter messages */ /* (Note: both VERBOSE and TERSE can be defined; -t * sets terse mode. One or the other MUST be defined. */ #define ROTATION /* enable x, X and ^X commands to work */ #define CHARSUBST /* enable the _C command */ #define DELBOGUS /* ask if bogus newsgroups should be deleted */ #define RELOCATE /* allow newsgroup rearranging */ #define ESCSUBS /* escape substitutions in multi-character commands */ #undef MCHASE /* unmark xrefed articles on m or M */ #define MUNGHEADER /* allow alternate header formatting via */ /* environment variable ALTHEADER (not impl) */ #define ASYNC_PARSE /* allow parsing headers asyncronously to reading */ /* used by MCHASE and MUNGHEADER */ #define FINDNEWNG /* check for new newsgroups on startup */ #define FASTNEW /* do optimizations on FINDNEWNG for faster startup */ /* (this optimization can make occasional mistakes */ /* if a group is removed and another group of the */ /* same length is added, and if no softpointers are */ /* affected by said change.) */ #define INNERSEARCH /* search command 'g' with article */ #define CATCHUP /* catchup command at newsgroup level */ #define NGSEARCH /* newsgroup pattern matching */ #define ONLY /* newsgroup restrictions by pattern */ #define KILLFILES /* automatic article killer files */ #define ARTSEARCH /* pattern searches among articles */ /* /, ?, ^N, ^P, k, K */ #define EDIT_DISTANCE /* Allow -G to specify a fuzzy 'go' command */ #undef VALIDATE_XREF_SITE /* are xrefs possibly invalid? */ /* some dependencies among options */ #ifndef ARTSEARCH # undef KILLFILES # undef INNERSEARCH #endif #ifndef SETUIDGID # define eaccess access #endif #ifdef ONLY /* idiot lint doesn't grok #if */ # define NGSORONLY #else # ifdef NGSEARCH # define NGSORONLY # endif #endif #ifdef VERBOSE # ifdef TERSE # define IF(c) if (c) # define ELSE else # else # define IF(c) # define ELSE # endif #else /* !VERBOSE */ # ifndef TERSE # define TERSE # endif # define IF(c) "IF" outside of VERBOSE??? # define ELSE "ELSE" outside of VERBOSE??? #endif #ifdef DEBUG # define assert(ex) {if (!(ex)){fprintf(stderr,"Assertion failed: file %s, line %d\n", __FILE__, __LINE__);sig_catcher(0);}} #else # define assert(ex) ; #endif /* If you're strapped for space use the help messages in shell scripts */ /* if {NG,ART,PAGER,SUBS}HELP is undefined, help messages are in memory */ #ifdef MEMHELP /* undef MEMHELP above to get them all as sh scripts */ # undef NGHELP # undef ARTHELP # undef PAGERHELP # undef SUBSHELP #else # ifndef NGHELP /* % and ~ */ # define NGHELP "%X/ng.help" # endif # ifndef ARTHELP /* % and ~ */ # define ARTHELP "%X/art.help" # endif # ifndef PAGERHELP /* % and ~ */ # define PAGERHELP "%X/pager.help" # endif # ifndef SUBSHELP /* % and ~ */ # define SUBSHELP "%X/subs.help" # endif #endif #define TCSIZE 512 /* capacity for termcap strings */ #ifdef EDIT_DISTANCE # define MIN_DIST 7 /* Maximum error count for acceptable match */ #endif /* Additional ideas: * Make the do_newsgroup() routine a separate process. * Keep .newsrc on disk instead of in memory. * Overlays, if you have them. * Get a bigger machine. */ /* End of Space Conservation Section */ /* More System Dependencies */ /* news library */ #ifndef NEWSLIB /* ~ and %l only ("~%l" is permissable) */ # define NEWSLIB "/usr/lib/news" #endif /* path to private executables */ #ifndef PRIVLIB /* ~, %x and %l only */ # define PRIVLIB "%x/trn" #endif /* system-wide RNINIT switches */ #ifndef GLOBINIT # define GLOBINIT "%X/INIT" #endif /* where to find news files */ #ifndef NEWSSPOOL /* % and ~ */ # define NEWSSPOOL "/usr/spool/news" #endif #ifndef THREAD_DIR # undef LONG_THREAD_NAMES #endif /* default characters to use in the selection menu */ #ifndef SELECTCHARS # define SELECTCHARS "abdefgijlorstuvwxyz1234567890BCFGHIKVW" #endif /* file containing list of active newsgroups and max article numbers */ #ifdef USE_NNTP # undef ACTIVE # define ACTIVE "%P/rrnact.%$" #else # ifndef ACTIVE /* % and ~ */ # define ACTIVE "%x/active" # endif #endif #ifndef DBINIT # define DBINIT "%W/db.init" #endif #ifdef USE_NNTP # ifndef ACTIVE_TIMES # define APPEND_UNSUB # endif #else # ifdef USE_XTHREAD # undef USE_XTHREAD # endif # ifdef USE_XOVER # undef USE_XOVER # endif #endif /* location of history file */ #ifndef ARTFILE /* % and ~ */ # define ARTFILE "%x/history" #endif /* command to setup a new .newsrc */ #ifndef NEWSETUP /* % and ~ */ # define NEWSETUP "newsetup" #endif /* command to display a list of un-subscribed-to newsgroups */ #ifndef NEWSGROUPS /* % and ~ */ # define NEWSGROUPS "newsgroups" #endif /* preferred shell for use in doshell routine */ /* ksh or sh would be okay here */ #ifndef PREFSHELL # define PREFSHELL "/bin/csh" #endif /* path to fastest starting shell */ #ifndef SH # define SH "/bin/sh" #endif /* default unshar'ing program */ #ifndef UNSHAR # define UNSHAR "/bin/sh" #endif /* path to default editor */ #ifndef DEFEDITOR # define DEFEDITOR "/usr/ucb/vi" #endif /* location of macro file for trn and rn modes */ #ifndef TRNMACRO # define TRNMACRO "%./.trnmac" #endif #ifndef RNMACRO # define RNMACRO "%./.rnmac" #endif /* location of full name */ #ifndef FULLNAMEFILE # ifndef PASSNAMES # define FULLNAMEFILE "%./.fullname" # endif #endif /* virtual array file name template */ #ifndef VARYNAME /* % and ~ */ # define VARYNAME "%y/rnvary.%$" #endif /* where to compile a new newsgroup list */ #ifndef RNEWNAME # define RNEWNAME "%y/rnew.%$" #endif /* file to pass header to followup article poster */ #ifndef HEADNAME /* % and ~ */ # define HEADNAME "%./.rnhead" /* or alternately #define HEADNAME "%y/rnhead.%$" */ #endif #ifndef MAKEDIR /* shell script to make n-deep subdirectories */ # ifndef DIRMAKER /* % and ~ */ # define DIRMAKER "%X/makedir" # endif #endif /* location of newsrc file */ #ifndef RCNAME /* % and ~ */ # define RCNAME "%./.newsrc" #endif /* temporary newsrc file in case we crash while writing out */ #ifndef RCTNAME /* % and ~ */ # define RCTNAME "%./.newnewsrc" #endif /* newsrc file at the beginning of this session */ #ifndef RCBNAME /* % and ~ */ # define RCBNAME "%./.oldnewsrc" #endif /* if existent, contains process number of current or crashed trn */ #ifndef LOCKNAME /* % and ~ */ # define LOCKNAME "%./.rnlock" #endif /* information from last invocation of trn */ #ifndef LASTNAME /* % and ~ */ # define LASTNAME "%./.rnlast" #endif /* file with soft pointers into the active file */ #ifndef SOFTNAME /* % and ~ */ # define SOFTNAME "%./.rnsoft" #endif /* list of article numbers to mark as unread later (see M and Y cmmands) */ #ifndef RNDELNAME /* % and ~ */ # define RNDELNAME "%./.rndelay" #endif /* a motd-like file for trn */ #ifndef NEWSNEWSNAME /* % and ~ */ # define NEWSNEWSNAME "%X/newsnews" #endif /* command to send a reply */ #ifndef MAILPOSTER /* % and ~ */ # define MAILPOSTER "QUOTECHARS=%I Rnmail -h %h" #endif #ifdef INTERNET # ifndef MAILHEADER /* % */ # ifdef CONDSUB # define MAILHEADER "To: %t\nSubject: %(%i=^$?:Re: %S\n%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)Newsgroups: %n\nIn-Reply-To: %i)\n%(%[references]=^$?:References: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # else # define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n" # endif # endif #else # ifndef MAILHEADER /* % */ # ifdef CONDSUB # define MAILHEADER "To: %T\nSubject: %(%i=^$?:Re: %S\n%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)Newsgroups: %n\nIn-Reply-To: %i)\n%(%[references]=^$?:References: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # else # define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n" # endif # endif #endif #ifndef YOUSAID /* % */ # define YOUSAID "In article %i you write:" #endif /* command to forward an article */ #define FORWARDPOSTER MAILPOSTER #ifdef INTERNET # ifndef FORWARDHEADER /* % */ # ifdef CONDSUB # define FORWARDHEADER "To: %\"\n\nTo: \"\nSubject: %(%i=^$?:%[subject] (fwd\\)\n%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)Newsgroups: %n\nIn-Reply-To: %i)\n%(%[references]=^$?:References: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # else # define FORWARDHEADER "To: \nSubject: %[subject] (fwd)\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n" # endif # endif #else # ifndef FORWARDHEADER /* % */ # ifdef CONDSUB # define FORWARDHEADER "To: %\"\n\nTo: \"\nSubject: %(%i=^$?:%[subject] (fwd\\)\n%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)Newsgroups: %n\nIn-Reply-To: %i)\n%(%[references]=^$?:References: %[references]\n)Organization: %o\nCc: \nBcc: \n\n" # else # define FORWARDHEADER "To: \nSubject: %[subject] (fwd)\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n" # endif # endif #endif #ifndef FORWARDMSG /* % */ # define FORWARDMSG "------- start of forwarded message -------" #endif #ifndef FORWARDMSGEND /* % */ # define FORWARDMSGEND "------- end of forwarded message -------" #endif /* command to submit a followup article */ #ifndef NEWSPOSTER /* % and ~ */ # define NEWSPOSTER "QUOTECHARS=%I Pnews -h %h" #endif #ifndef NEWSHEADER /* % */ # ifdef CONDSUB # define NEWSHEADER "%(%[followup-to]=^$?:%(%[followup-to]=^%n$?:X-ORIGINAL-NEWSGROUPS: %n\n))Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \n%(%{REPLYTO}=^$?:Reply-To: %{REPLYTO}\n)Distribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\nCc: %(%F=poster?%t:%(%F!=@?:%F))\n\n" # else # define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\nCc: \n\n" # endif #endif #ifndef ATTRIBUTION /* % */ # define ATTRIBUTION "In article %i,%?%)f <%>f> wrote:" #endif #ifndef PIPESAVER /* % */ # ifdef CONDSUB # define PIPESAVER "%(%B=^0$?<%A:tail +%Bc %A |) %b" # else # define PIPESAVER "tail +%Bc %A | %b" # endif #endif #ifndef EXSAVER # define EXSAVER "tail +%Bc %A | %e" #endif #ifdef MIMESTORE # ifndef EXMIMESAVER # define EXMIMESAVER "%e %A" # endif #endif #ifndef NORMSAVER /* % and ~ */ # define NORMSAVER "%X/norm.saver %A %P %c %a %B %C \"%b\"" #endif #ifndef MBOXSAVER /* % and ~ */ # ifndef ANCIENT_NEWS # define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %t %`LANG= date`\"" # else # ifdef CONDSUB # define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %t %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\"" /* header munging with a vengeance */ # else # define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %t %[posted]\"" # endif # endif #endif #ifdef MKDIRS # ifndef SAVEDIR /* % and ~ */ # define SAVEDIR "%p/%c" # endif # ifndef SAVENAME /* % */ # define SAVENAME "%a" # endif #else # ifndef SAVEDIR /* % and ~ */ # define SAVEDIR "%p" # endif # ifndef SAVENAME /* % */ # define SAVENAME "%^C" # endif #endif #ifndef KILLGLOBAL /* % and ~ */ # define KILLGLOBAL "%p/KILL" #endif #ifndef KILLLOCAL /* % and ~ */ # define KILLLOCAL "%p/%c/KILL" #endif /* how to cancel an article */ #ifndef CANCEL # ifdef BNEWS # define CANCEL "%x/inews -h < %h" # else # define CANCEL "inews -h < %h" # endif #endif /* how to cancel an article, continued */ #ifndef CANCELHEADER # define CANCELHEADER "Newsgroups: %n\nSubject: cancel\nControl: cancel %i\nDistribution: %D\n\n%i was cancelled from within trn.\n" #endif /* how to supersede an article */ #ifndef SUPERSEDEHEADER # define SUPERSEDEHEADER "Newsgroups: %n\nSubject: %S\nSummary: %[summary]\nExpires: %[expires]\nReferences: %[references]\nSupersedes: %i\nSender: %[sender]\nFollowup-To: %[followup-to]\nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n" #endif #ifndef LOCALTIMEFMT # define LOCALTIMEFMT "%a %b %d %X %Z %Y" #endif /* where to find the mail file */ #ifndef MAILFILE # define MAILFILE "/usr/spool/mail/%L" #endif /* how to open binary format files */ #ifndef FOPEN_RB # define FOPEN_RB "r" #endif #ifndef FOPEN_WB # define FOPEN_WB "w" #endif /* what to do with ansi prototypes -- '()' == ignore, 'x' == use */ #ifndef _ # ifdef __STDC__ # define _(x) x # ifndef CONST # define CONST const # endif # else # define _(x) () # ifndef CONST # define CONST # endif # endif #endif /* how many characters is a newline in a text file? */ #ifndef NL_SIZE # define NL_SIZE 1 #endif /* some important types */ typedef int NG_NUM; /* newsgroup number */ typedef long ART_NUM; /* article number */ typedef long ART_UNREAD; /* could be short to save space */ typedef long ART_POS; /* char position in article file */ typedef int ART_LINE; /* line position in article file */ typedef long ACT_POS; /* char position in active file */ typedef unsigned int MEM_SIZE; /* for passing to malloc */ /* some slight-of-hand for compatibility issues */ #ifdef HAS_STRCHR # ifndef index # define index strchr # endif # ifndef rindex # define rindex strrchr # endif #endif #ifdef HAS_MEMCMP # ifndef bcmp # define bcmp(s,d,l) memcmp((s),(d),(l)) # endif #endif #ifdef HAS_MEMCPY # ifndef bcopy # define bcopy(s,d,l) memcpy((d),(s),(l)) # endif #endif #ifdef HAS_MEMSET # ifndef bzero # define bzero(s,l) memset((s),0,(l)) # endif #endif #ifndef HAS_VFORK # define vfork fork #endif /* *** end of the machine dependent stuff *** */ /* GLOBAL THINGS */ /* file statistics area */ EXT struct stat filestat; /* various things of type char */ #ifdef SUPPLEMENT_STRING_H char *index(); char *rindex(); char *strcat(); char *strcpy(); #endif EXT char buf[LBUFLEN+1]; /* general purpose line buffer */ EXT char cmd_buf[CBUFLEN]; /* buffer for formatting system commands */ EXT char *indstr INIT(">"); /* indent for old article embedded in followup */ EXT char *cwd INIT(Nullch); /* current working directory */ EXT char *dfltcmd INIT(Nullch); /* 1st char is default command */ /* switches */ #ifdef DEBUG EXT int debug INIT(0); /* -D */ # define DEB_COREDUMPSOK 2 # define DEB_HEADER 4 # define DEB_INTRP 8 # define DEB_NNTP 16 # define DEB_INNERSRCH 32 # define DEB_FILEXP 64 # define DEB_HASH 128 # define DEB_XREF_MARKER 256 # define DEB_CTLAREA_BITMAP 512 # define DEB_SOFT_POINTERS 1024 # define DEB_NEWSRC_LINE 2048 # define DEB_SEARCH_AHEAD 4096 # define DEB_CHECKPOINTING 8192 # define DEB_FEED_XREF 16384 #endif #ifdef ARTSEARCH EXT int scanon INIT(0); /* -S */ #endif EXT bool use_threads INIT(THREAD_INIT); /* -x */ EXT int max_tree_lines INIT(6); EXT char select_order[4] INIT("lms"); EXT int select_on INIT(SELECT_INIT); /* -X */ EXT char end_select INIT('Z'); EXT char page_select INIT('>'); EXT bool dont_filter_control INIT(FALSE); /* -j */ EXT int join_subject_len INIT(0); /* -J */ EXT bool kill_thru_kludge INIT(TRUE); /* -k */ EXT bool keep_the_group_static INIT(FALSE); /* -K */ EXT bool mbox_always INIT(FALSE); /* -M */ EXT bool norm_always INIT(FALSE); /* -N */ EXT bool thread_always INIT(FALSE); /* -a */ EXT bool auto_arrow_macros INIT(TRUE); /* -B */ EXT bool breadth_first INIT(FALSE); /* -b */ EXT bool bkgnd_spinner INIT(FALSE); /* -B */ EXT bool novice_delays INIT(TRUE); /* +f */ EXT int olden_days INIT(FALSE); /* -o */ EXT char auto_select_postings INIT(0); /* -p */ EXT bool checkflag INIT(FALSE); /* -c */ EXT bool suppress_cn INIT(FALSE); /* -s */ EXT int countdown INIT(5); /* how many lines to list before invoking -s */ EXT bool muck_up_clear INIT(FALSE); /* -loco */ EXT bool erase_screen INIT(FALSE); /* -e */ EXT bool can_home INIT(FALSE); #ifdef CLEAREOL EXT bool can_home_clear INIT(FALSE); /* fancy -e */ #endif EXT bool findlast INIT(FALSE); /* -r */ EXT bool typeahead INIT(FALSE); /* -T */ #ifdef EDIT_DISTANCE EXT bool fuzzyGet INIT(FALSE); /* -G */ #endif #ifdef VERBOSE # ifdef TERSE EXT bool verbose INIT(TRUE); /* +t */ # endif #endif EXT bool unbroken_subjects INIT(FALSE); /* -u */ EXT bool unsafe_rc_saves INIT(FALSE); /* -U */ #ifdef VERIFY EXT bool verify INIT(FALSE); /* -v */ #endif EXT bool quickstart INIT(FALSE); /* -q */ EXT time_t actFetchTime /* -z */ #ifdef USE_NNTP INIT(5*60); #else INIT(0); #endif EXT int try_ov /* -Z */ #ifdef USE_OV INIT(1); #else INIT(0); #endif EXT int try_mt #ifdef USE_MT INIT(1); #else INIT(0); #endif #define NOMARKING 0 #define STANDOUT 1 #define UNDERLINE 2 EXT int marking INIT(NOMARKING); /* -m */ #define HALFPAGE_MARKING 1 #define BACKPAGE_MARKING 2 EXT int marking_areas INIT(HALFPAGE_MARKING); EXT ART_LINE initlines INIT(0); /* -i */ EXT bool initlines_specified INIT(FALSE); #ifdef APPEND_UNSUB EXT bool append_unsub INIT(1); /* -I */ #else EXT bool append_unsub INIT(0); #endif /* miscellania */ #ifndef __STDC__ int fseek(); long atol(), ftell(); extern int errno; #endif EXT bool in_ng INIT(FALSE); /* current state of trn */ EXT char mode INIT('i'); /* current state of trn */ EXT FILE *tmpfp INIT(Nullfp); /* scratch fp used for .rnlock, .rnlast, etc. */ EXT NG_NUM nextrcline INIT(0); /* 1st unused slot in rcline array */ /* startup to avoid checking twice in a row */ /* Factored strings */ EXT char nullstr[1] INIT(""); EXT char sh[] INIT(SH); EXT char defeditor[] INIT(DEFEDITOR); EXT char hforhelp[] INIT("Type h for help.\n"); #ifdef STRICTCR EXT char badcr[] INIT("\nUnnecessary CR ignored.\n"); #endif EXT char readerr[] INIT("rn read error"); EXT char unsubto[] INIT("\n\nUnsubscribed to newsgroup %s\n"); EXT char cantopen[] INIT("Can't open %s\n"); EXT char cantcreate[] INIT("Can't create %s\n"); EXT char cantrecreate[] INIT("Can't recreate %s -- restoring older version.\n"); #ifdef VERBOSE EXT char nocd[] INIT("Can't chdir to directory %s\n"); #else EXT char nocd[] INIT("Can't find %s\n"); #endif #if defined(MIMESHOW) || defined(MIMESTORE) EXT bool mime_article INIT(FALSE); #endif #ifdef NOLINEBUF #define FLUSH ,fflush(stdout) #else #define FLUSH #endif #ifdef lint #undef FLUSH #define FLUSH #undef putchar #define putchar(c) #endif #define nntp_advise(str) fputs(str,stdout) #define nntp_init_error(str) fputs(str,stderr) #define nntp_error(str) fputs(str,stderr) #define NNTP_ERROR_IS_FATAL #define NNTP_HANDLE_TIMEOUT #define NNTP_HANDLE_AUTH_ERR trn-3.6/README0000664000000000000000000001005005575136525007746 0ustar Trn Kit, Version 3.5 Copyright (c) 1993, Wayne Davison Based on rn, Version 4.4 Copyright (c) 1985, Larry Wall Copyright (c) 1991, Stan Barber You may copy the trn kit in whole or in part as long as you don't try to make money off it, or pretend that you wrote it. -------------------------------------------------------------------------- See the file INSTALL for installation instructions. Failure to do so may void your warranty. :-) After you have unpacked your kit, you should have all the files listed in MANIFEST (Configure checks this for you). If you're unsure if you have the latest release, check ftp.uu.net: ftp.uu.net:networking/news/readers/trn/trn.tar.gz -> [latest.version].gz [A .Z version is also available and mthreads resides in the same spot, if you need it.] What is trn? ------------ Trn is Threaded RN -- a newsreader that uses an article's references to order the discussions in a very natural, reply-ordered sequence called threads. Having the replies associated with their parent articles not only makes following the discussion easier, but also makes it easy to back- track and (re-)read a specific discussion from the beginning. Trn also has a visual representation of the current thread in the upper right corner of the header, which will give you a feel for how the discussion is going and how the current article is related to the last one you read. In addition, a thread selector makes it easy to browse through a large group looking for interesting articles. You can even browse through the articles you've already read and select the one(s) you wish to read again. Other nice features include the extract commands for the source and binary groups, thread-oriented kill directives, a better newgroup finding strategy, and lots more. See the file NEW for a list of the things that are new to trn 3.0 from previous versions. To make trn work faster you will probably want to create an auxiliary news database that summarises the available articles. Trn know how to use two different kinds (so far): thread files, which are maintained by the mthreads package and typically requires 3-5% of your newsspool size in disk storage; and overview files, which are maintained by INN v1.3 (or greater) or a modified version of C news and typically requires 8-10% of your newsspool size in disk storage. (Note that the space that mthreads saves you on your disk is paid for by a higher demand on your cpu and disks while updating the files.) See the package of your choice for details on how to setup the adjunct database, but it is not necessary to do this before trying out trn. Trn supports local news groups and news accessed remotely via NNTP. If you opt for remote access you will probably want to make the adjunct database available too. You can do this in a variety of way, but I recommend that you send the database from the server to the client via NNTP. To do this you either need to use INN or a modified reference NNTP -- version 1.5.11-t5 is the latest as of this writing. See ftp.uu.net:networking/news/nntp for the file nntp-t5.tar.gz. This version supports the XOVER command (to send overview files), the XTHREAD command (to send thread files), and the XINDEX command (though trn doesn't support using it). The alternative is to either mount the disk containing your database via NFS, or build it locally. See the mthreads package for details on how to do this. Note that trn is based on rn, and so it does a great job of pretending to be rn for those people that simply don't like to change their newsreading habits. It is possible to install trn as both rn and trn linked together and have it act as both newsreaders, thus saving you the hassle of maint- aining two separate newsreaders. A Configuration question will ask you if you want trn to check its name on startup. Where to send bug reports ------------------------- Mail your bug reports to Wayne Davison . If you use the 'v'ersion command from the newsgroup selection level of trn you will be reminded of this address should you forget. trn-3.6/rthread.h0000664000000000000000000000544105560330134010662 0ustar /* $Id: rthread.h,v 3.0 1992/12/14 00:14:15 davison Trn $ */ /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ EXT ART_NUM article_count INIT(0); EXT int subject_count INIT(0); EXT bool output_chase_phrase; EXT char *references; EXT bool ov_opened INIT(FALSE); void thread_init _((void)); void thread_open _((void)); void thread_grow _((void)); void thread_close _((void)); void top_article _((void)); ARTICLE *first_art _((SUBJECT*)); ARTICLE *last_art _((SUBJECT*)); ARTICLE *bump_art _((ARTICLE*)); ARTICLE *next_art _((ARTICLE*)); ARTICLE *prev_art _((ARTICLE*)); void inc_art _((bool_int,bool_int)); void dec_art _((bool_int,bool_int)); bool next_art_with_subj _((void)); bool prev_art_with_subj _((void)); SUBJECT *next_subj _((SUBJECT*,int)); SUBJECT *prev_subj _((SUBJECT*,int)); void select_article _((ARTICLE*,int)); void select_arts_subject _((ARTICLE*,int)); void select_subject _((SUBJECT*,int)); void select_arts_thread _((ARTICLE*,int)); void select_thread _((ARTICLE*,int)); void select_subthread _((ARTICLE*,int)); void deselect_article _((ARTICLE*)); void deselect_arts_subject _((ARTICLE*)); void deselect_subject _((SUBJECT*)); void deselect_arts_thread _((ARTICLE*)); void deselect_thread _((ARTICLE*)); void deselect_all _((void)); void kill_arts_subject _((ARTICLE*,int)); void kill_subject _((SUBJECT*,int)); void kill_arts_thread _((ARTICLE*,int)); void kill_thread _((ARTICLE*,int)); void kill_subthread _((ARTICLE*,int)); void unkill_subject _((SUBJECT*)); void unkill_thread _((ARTICLE*)); void unkill_subthread _((ARTICLE*)); void clear_subject _((SUBJECT*)); void clear_thread _((ARTICLE*)); void clear_subthread _((ARTICLE*)); #define KF_UNSELECTED 0 /* leave selected articles, no KILL file */ #define KF_ALL 0x0001 /* Affect both selected and unselected */ #define KF_KILLFILE 0x0002 /* Save the command to the KILL file */ ARTICLE *subj_art _((SUBJECT*)); void visit_next_thread _((void)); void visit_prev_thread _((void)); bool find_parent _((bool_int)); bool find_leaf _((bool_int)); bool find_prev_sib _((void)); bool find_next_sib _((void)); void sort_subjects _((void)); void count_subjects _((int)); #define CS_RETAIN 0 #define CS_NORM 1 #define CS_RESELECT 2 #define CS_UNSELECT 3 #define CS_UNSEL_STORE 4 int subjorder_date _((SUBJECT**, SUBJECT**)); int subjorder_str _((SUBJECT**, SUBJECT**)); int threadorder_date _((SUBJECT**, SUBJECT**)); int threadorder_str _((SUBJECT**, SUBJECT**)); void sort_articles _((void)); int artorder_date _((ARTICLE**, ARTICLE**)); int artorder_str _((ARTICLE**, ARTICLE**)); time_t parsedate _((char*)); /* Stuff local to rthread.c. */ #ifdef DOINIT static void build_artptrs _((void)); #endif trn-3.6/Rnmail.10000664000000000000000000000773505424541445010404 0ustar .\" $Id: Rnmail.1,v 4.4.3.1 1992/02/01 03:17:20 sob PATCH_3 sob $ .\" .\" This software is Copyright 1991 by Stan Barber. .\" .\" Permission is hereby granted to copy, reproduce, redistribute or otherwise .\" use this software as long as: there is no monetary profit gained .\" specifically from the use or reproduction of this software, it is not .\" sold, rented, traded or otherwise marketed, and this copyright notice is .\" included prominently in any copy made. .\" .\" The authors make no claims as to the fitness or correctness of this software .\" for any use whatsoever, and it is provided as is. Any use of this software .\" is at the user's own risk. .\" .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. .\" unbreakable dash. .tr \(*W-|\(bv\*(Tr .ie n \{\ .ds -- \(*W- .if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH RNMAIL 1 LOCAL .SH NAME Rnmail - a program for replying via mail .SH SYNOPSIS .B Rnmail destination_list .br or .br .B Rnmail -h headerfile [oldarticle] .br or .br .B Rnmail .SH DESCRIPTION Rnmail is a friendly interface for mailing replies to news articles. It will ask several questions, then allow you to enter your letter, and then mail it off. If you type h and a carriage return at any point, .I Rnmail will tell you what it wants to know. .PP If you put a .I .signature file in your home directory, Rnmail will append it to your message after you are done editing it. If you prefer to see your signature while you are editing, rename your \&.signature file to be .mail_sig and it will be appended before you begin to edit. Note that both .mail_sig and .signature obey the setting of DOTDIR unless Rnmail was modified to take the .signature appending out of Rnmail's control and that if both files exist you get two signatures appended. .PP The -h form is used when invoked from .I trn or .IR rn . If your editor can edit multiple files, and you want the article to which you are replying to show up as an alternate file, define the environment variable MAILPOSTER as \*(L"Rnmail -h %h %A\*(R". You can also modify the the MAILHEADER environment variable to change the header file that .I [t]rn passes to Rnmail. .SH ENVIRONMENT .IP DOTDIR 8 If defined, specifies a place other than your home directory where 'dot' files may be stored. This is primarily for accounts which are shared by more than one person. .Sp Default: $HOME .IP EDITOR 8 The editor you want to use, if VISUAL is undefined. .Sp Default: whatever your news administrator installed, usually vi. .IP HOME 8 Your home directory. .Sp Default: $LOGDIR .IP LOGDIR 8 Your home directory if HOME is undefined. .IP LOGNAME 8 Your login name, if USER is undefined. .Sp Default: value of \*(L"whoami\*(R". .IP MAILRECORD 8 If defined, contains the name of a file to which the finished message will be appended. .Sp Default: message not saved .IP NEWSORG 8 Either the name of your organization, or the name of a file containing the name of your organization. (For use at organizations where the ORGANIZATION environmental variable is in use for other purposes). If both NEWSORG and ORGANIZATION are set, NEWSORG will override ORGANIZATION. .Sp Default: whatever your news administrator chose. .IP ORGANIZATION 8 Either the name of your organization, or the name of a file containing the name of your organization. .Sp Default: whatever your news administrator chose. .IP REPLYTO 8 The contents of a \*(L"Reply-To:\*(R" header line to insert into your message. .Sp Default: header line not inserted. .IP USER 8 Your login name. .Sp Default: $LOGNAME .IP VISUAL 8 The editor you want to use. .Sp Default: $EDITOR .SH FILES $DOTDIR/.letter .br $DOTDIR/.signature .br $DOTDIR/.mail_sig .br ~/dead.letter .SH SEE ALSO trn(1), rn(1), Pnews(1), mail(1) .SH DIAGNOSTICS .SH BUGS Uses /bin/mail in the absence of sendmail. trn-3.6/charsubst.h0000664000000000000000000000155005652030035011223 0ustar /* $Id: charsubst.h,v 3.0 1993/12/21 21:54:40 davison Trn $ */ /* * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #ifdef CHARSUBST /* Conversions are: plain, ISO->ascii, TeX -> ISO, ISO->ascii monospaced */ EXT char *charsets INIT("patm"); EXT char *charsubst; int putsubstchar _((int, int, bool_int)); char *current_charsubst _((void)); void strcharsubst _((char*,char*)); #endif trn-3.6/hints/0000775000000000000000000000000007205300754010204 5ustar trn-3.6/hints/svr4.sh0000664000000000000000000000017505422563250011442 0ustar cc='/bin/cc' test -f $cc || cc='/usr/ccs/bin/cc' mansrc='/usr/share/man/man1' libswanted='malloc socket nsl' d_strchr=define trn-3.6/hints/dec_osf1.sh0000664000000000000000000000020405422563242012221 0ustar libpth="$libpth /usr/shlib" # Use the shared libraries if possible libc='/usr/shlib/libc.so' # The archive version is /lib/libc.a trn-3.6/hints/i386.sh0000664000000000000000000000003005422563243011225 0ustar ldflags='-L/usr/ucblib' trn-3.6/hints/osf1.sh0000664000000000000000000000001505422563245011411 0ustar libswanted=m trn-3.6/hints/convexos.sh0000664000000000000000000000021305423137131012375 0ustar i_sgtty=undef i_termios=define ccflags="-D__STDC__ -ext -tm c1" d_voidsig='define' signal_t='void' d_strchr='undef' libc='/usr/lib/libc.a' trn-3.6/hints/isc_3_2_2.sh0000664000000000000000000000012605422563244012205 0ustar set `echo $libswanted | sed -e 's/ x//' -e 's/malloc //'` libswanted="inet malloc $*" trn-3.6/hints/mips.sh0000664000000000000000000000002305422563245011510 0ustar cc=cc nm_opts='-B' trn-3.6/hints/hpux.sh0000664000000000000000000000016205422563243011526 0ustar case `(uname -r) 2>/dev/null` in *2.1*) libswanted=`echo $libswanted | sed 's/malloc //'` ;; esac d_strchr=define trn-3.6/hints/solaris_2_3.sh0000664000000000000000000000014105657552732012671 0ustar d_sigblock='undef' d_getcwd='define' d_getwd='undef' libs='-lmalloc -lsocket -lnls -lnsl -lintl' trn-3.6/hints/hp9000_800.sh0000664000000000000000000000010205422563243012043 0ustar libswanted=`echo $libswanted | sed -e 's/malloc //' -e 's/BSD //` trn-3.6/hints/sco_3_2_4.sh0000664000000000000000000000007605422563246012223 0ustar xlibpth='' mailer='/usr/lib/mail/execmail' libc='/lib/libc.a' trn-3.6/hints/sco_3.sh0000664000000000000000000000011005422563246011544 0ustar libswanted=`echo $libswanted | sed 's/ x//'` cppminus='' d_rename=undef trn-3.6/hints/aix_rs.sh0000664000000000000000000000007205422563241012025 0ustar cppstdin='/lib/cpp -D_AIX -D_IBMR2 -U__STR__' cppminus='' trn-3.6/hints/isc_2_2_1.sh0000664000000000000000000000001605422563244012201 0ustar d_ftime=undef trn-3.6/hints/altos486.sh0000664000000000000000000000015705422563242012131 0ustar : have heard of problems with -lc_s on Altos 486 set `echo " $libswanted " | sed "s/ c_s / /"` libswanted="$*" trn-3.6/hints/Policy.sh0000664000000000000000000000764407205300754012012 0ustar #!/bin/sh # # This file was produced by running the Polich.sh.SH script, which # gets its values from config.sh, which is generally produced by # running Configure. # # $Id: Policy.sh.SH,v 3.5 1994/04/24 16:47:38 davison Trn $ # login name of the person who configured trn (not particularly interesting). cf_by='matthew' # time of configuration (not particularly interesting). cf_time='Fri Nov 17 19:01:16 GMT 2000' # install directives. # bin directories (string values) # name of the final resting place bin='/usr/lib/trn' # how to get to the final resting place (thank you, AFS) installbin='/usr/lib/trn' # private libraries # name of the final resting place for those items in the library # directory (string value) privlib='/usr/share/trn' # How to get to the library final resting place (thanks, AFS) installprivlib='/usr/share/trn' # interesting questions about man # where do man page sources go? mansrc='/usr/share/man/man1' # what extention do man pages get? manext='1' # path to assorted programs that we might want to override. # name of the default editor. (string value) defeditor='/usr/bin/emacs' # prefered user shell (string value) prefshell='/bin/sh' # favorite local pager (string value) pager='/usr/bin/pager' # where is inews? (string value) inewsloc='inews' # path to interactive speller or "none" (string value) ispell_prg='/usr/bin/ispell' # spelling options for ispell_prg or "spell" if "none" (string value) ispell_options='' # internal options # ignore the ORGANIZATION environment variable? (define/undef) d_ignoreorg='undef' # does the mailer understand FQDN addressing? (define/undef) d_internet='define' # do you have a news admin? (define/undef) d_newsadm='define' # name of the news admin? (string value) newsadmin='news' # read via NNTP? (define/undef) d_nntp='define' # use the XDATA NNTP extension? (define/undef) d_xdata='define' # path to a file containing a server name, or a hostname (string value) servername='/etc/news/server' # distribution names (string values) # local city citydist='none' # "local" country cntrydist='none' # "local" continent contdist='none' # site distribution locdist='none' # organizational distribution orgdist='none' # state/province distribution name statedist='none' # multistate region distribution name multistatedist='none' # Naming information. # password file contains names (define/undef) d_passnames='define' # berkeley style password entries (name first in GCOS) (define/undef) d_berknames='define' # USG style password entries (account number first in GCOS) # (define/undef) d_usgnames='undef' # what type of name to use.. (bsd/usg/other) nametype='bsd' # How portable do we want to be? Determines if we do lookups now # or wait until run time. (define/undef) d_portable='define' # news library information # where is the news library (usually /usr/lib/news) may contain ~ newslib='/tmp' # absolute path name to /usr/lib/news. newslibexp='/tmp' # where is the news spool (usually /{var,usr}/spool/news) newsspool='/tmp' # active file stuff, like where is it, what is its name, etc # path to the active file. (string value) active='none' # do we have an active.times file? (define/undef) d_acttimes='define' # path to the active.times file. (string value) acttimes='nntp' # organizations name. path to file, or constant string orgname='/etc/news/organization' # only one of the two following is needed # command to find the posting hosts name (string value, optional) phostcmd='' # file containing posting hosts name or constant string # (string value, optional) # phost='/etc/news/whoami' # what should we use? mthreads or overview # use the mthreads format? (define/undef) d_usemt='define' # where do we find the thread files? (string value) threaddir='/tmp' # use the overview format? (define/undef) d_useov='define' # where do we find the .overview fils? (string value) overviewdir='/tmp' # trn start up options trn_init='FALSE' # start up with the selector? trn_select='TRUE' trn-3.6/hints/solaris_2_1.sh0000664000000000000000000000014005455506005012652 0ustar #d_vfork=undef i_dirent=define i_sysdir=undef libs='-lmalloc -lsocket -lnls -lnsl -lintl -lucb' trn-3.6/hints/dgux.sh0000664000000000000000000000007405422563242011512 0ustar cppstdin='/lib/cpp' libs='-ldgc' d_strchr='define' cc='gcc' trn-3.6/hints/mc6000.sh0000664000000000000000000000012705422563244011451 0ustar # defaults for the masscomp (concurrent) 6000 series running RTU 5.0 cppstdin=/lib/cpp trn-3.6/hints/domainos.sh0000664000000000000000000000005005455506004012345 0ustar ccflags='-A nansi' d_ignoreorg='define' trn-3.6/hints/next.sh0000664000000000000000000000002305422563245011516 0ustar libswanted='sys_s' trn-3.6/hints/Policy.sh.old0000664000000000000000000000762207116537437012576 0ustar #!/bin/sh # # This file was produced by running the Polich.sh.SH script, which # gets its values from config.sh, which is generally produced by # running Configure. # # $Id: Policy.sh.SH,v 3.5 1994/04/24 16:47:38 davison Trn $ # login name of the person who configured trn (not particularly interesting). cf_by='matthew' # time of configuration (not particularly interesting). cf_time='Sun Jun 4 21:41:43 BST 2000' # install directives. # bin directories (string values) # name of the final resting place bin='/usr/bin' # how to get to the final resting place (thank you, AFS) installbin='/usr/bin' # private libraries # name of the final resting place for those items in the library # directory (string value) privlib='/usr/lib/trn' # How to get to the library final resting place (thanks, AFS) installprivlib='/usr/lib/trn' # interesting questions about man # where do man page sources go? mansrc='/usr/man/man1' # what extention do man pages get? manext='1' # path to assorted programs that we might want to override. # name of the default editor. (string value) defeditor='/usr/bin/emacs' # prefered user shell (string value) prefshell='/bin/sh' # favorite local pager (string value) pager='/usr/bin/pager' # where is inews? (string value) inewsloc='inews' # path to interactive speller or "none" (string value) ispell_prg='/usr/bin/ispell' # spelling options for ispell_prg or "spell" if "none" (string value) ispell_options='' # internal options # ignore the ORGANIZATION environment variable? (define/undef) d_ignoreorg='undef' # does the mailer understand FQDN addressing? (define/undef) d_internet='define' # do you have a news admin? (define/undef) d_newsadm='define' # name of the news admin? (string value) newsadmin='news' # read via NNTP? (define/undef) d_nntp='define' # use the XDATA NNTP extension? (define/undef) d_xdata='define' # path to a file containing a server name, or a hostname (string value) servername='/etc/news/server' # distribution names (string values) # local city citydist='none' # "local" country cntrydist='none' # "local" continent contdist='none' # site distribution locdist='none' # organizational distribution orgdist='none' # state/province distribution name statedist='none' # multistate region distribution name multistatedist='none' # Naming information. # password file contains names (define/undef) d_passnames='define' # berkeley style password entries (name first in GCOS) (define/undef) d_berknames='define' # USG style password entries (account number first in GCOS) # (define/undef) d_usgnames='undef' # what type of name to use.. (bsd/usg/other) nametype='bsd' # How portable do we want to be? Determines if we do lookups now # or wait until run time. (define/undef) d_portable='define' # news library information # where is the news library (usually /usr/lib/news) may contain ~ newslib='/tmp' # absolute path name to /usr/lib/news. newslibexp='/tmp' # where is the news spool (usually /{var,usr}/spool/news) newsspool='/tmp' # active file stuff, like where is it, what is its name, etc # path to the active file. (string value) active='none' # do we have an active.times file? (define/undef) d_acttimes='define' # path to the active.times file. (string value) acttimes='nntp' # organizations name. path to file, or constant string orgname='/etc/news/organization' # only one of the two following is needed # command to find the posting hosts name (string value, optional) phostcmd='' # file containing posting hosts name or constant string # (string value, optional) # phost='/etc/news/whoami' # what should we use? mthreads or overview # use the mthreads format? (define/undef) d_usemt='define' # where do we find the thread files? (string value) threaddir='/tmp' # use the overview format? (define/undef) d_useov='define' # where do we find the .overview fils? (string value) overviewdir='/tmp' # trn start up options trn_init='FALSE' # start up with the selector? trn_select='TRUE' trn-3.6/hints/dynix.sh0000664000000000000000000000010505422563243011672 0ustar libswanted=`echo $libswanted | sed -e 's/socket /socket seq inet /'` trn-3.6/hints/sco_xenix.sh0000664000000000000000000000007405426404021012533 0ustar i_dirent=undef libswanted=`echo $libswanted | sed 's/ x//'` trn-3.6/hints/sgi.sh0000664000000000000000000000006005455506004011317 0ustar d_voidsig=define d_vfork=undef d_sigblock=undef trn-3.6/hints/freebsd.sh0000664000000000000000000000002305652030036012142 0ustar i_sysfilio='undef' trn-3.6/hints/solaris_2_0.sh0000664000000000000000000000001605455506004012652 0ustar d_vfork=undef trn-3.6/hints/isc_3_2_3.sh0000664000000000000000000000010505422563244012203 0ustar set `echo $libswanted | sed -e 's/ socket / inet /'` libswanted="$*" trn-3.6/hints/solaris_2_2.sh0000664000000000000000000000014105426171557012664 0ustar d_sigblock='undef' d_getcwd='define' d_getwd='undef' libs='-lmalloc -lsocket -lnls -lnsl -lintl' trn-3.6/Rnmail.SH0000664000000000000000000001500207041327652010537 0ustar case $CONFIG in '') . ./config.sh ;; esac echo "Extracting Rnmail (with variable substitutions)" $spitshell >Rnmail <>Rnmail <<'!NO!SUBS!' orgname=${NEWSORG-$orgname} !NO!SUBS! ;; *) $spitshell >>Rnmail <<'!NO!SUBS!' orgname=${NEWSORG-${ORGANIZATION-$orgname}} !NO!SUBS! ;; esac $spitshell >>Rnmail <<'!NO!SUBS!' dotdir=${DOTDIR-${HOME-$LOGDIR}} tmpart=$dotdir/.letter speller=$rnlib/Speller mail_sig=${MAILSIGNATURE-$dotdir/.mail_sig} headerfile="" case $# in 0) ;; *) case $1 in -h) headerfile="$2" case $# in 3) oldart=$3 ;; esac ;; esac ;; esac case $headerfile in '') case $# in 0) to=h while $test "X$to" = Xh ; do $echo "" $echo $n "To: $c" read to case $to in h) $cat <<'EOH' Type the net address of those people to whom you wish the message sent. Additional recipients may be added on the Cc: line when you edit. Separate multiple addresses with spaces. EOH ;; esac done ;; *) to="$*" ;; esac to=`$echo "$to" | $sed 's/ */ /g'` title=h while $test "X$title" = Xh ; do $echo "" $echo $n "Title/Subject: $c" read title case $title in h) $cat <<'EOH' Type the title for your message. EOH ;; esac done # now build a file with a header for them to edit case $orgname in /*) orgname=`$cat $orgname` ;; esac $sed -e '/^Reply-To: $/d' > $tmpart < $tmpart ;; esac file=h while $test X$file = Xh ; do $echo "" $echo $n "Prepared file to include [none]: $c" read file case $file in h) $cat <<'EOH' If you have already produced the body of your message, type the filename for it here. If you just want to proceed directly to the editor, type a RETURN. In any event, you will be allowed to edit as many times as you want before you send off the message. EOH ;; '') $echo "" >> $tmpart state=edit ;; *) $cat $file >>$tmpart state=ask ;; esac done $echo "" if $test -r $mail_sig; then $echo "-- " >> $tmpart $cat $mail_sig >> $tmpart fi while : ; do case $state in edit) rescue="$sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.letter ;$echo Message appended to ${HOME-$LOGDIR}/dead.letter ; chmod -f o-rwx ${HOME-$LOGDIR}/dead.letter; exit" trap "$rescue" 1 trap : 2 case "${VISUAL-${EDITOR-}}" in '') tmp=h ;; *) tmp='' ;; esac while $test "X$tmp" = Xh ; do $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c" read tmp case $tmp in h) $cat <<'EOH' Type a return to get the default editor, or type the name of the editor you prefer. The default editor depends on the VISUAL and EDITOR environment variables. EOH ;; '') ;; *) VISUAL=$tmp export VISUAL ;; esac done ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart trap "$rescue" 2 state=ask ;; ask) $echo "" $echo $n "Check spelling, Send, Abort, Edit, or List? $c" read ans case $ans in [aA]*) state=rescue ;; [eE]*) set $ans case $# in 2) VISUAL="$2" ;; esac state=edit ;; [lL]*) $pager $tmpart state=ask ;; [cC]*|[sS][pP]*) $speller $tmpart state=ask ;; [sS]*) state=send ;; [hH]*) $cat <<'EOH' Type c to check the message's spelling, s to send the message, a to abort and append the message to dead.letter, e to edit the message again, or l to list the message with your pager. To invoke an alternate editor, type 'e editor'. EOH esac ;; send) if $test -f $dotdir/.signature; then $echo $n "Append .signature file? [y] $c" read ans case $ans in ''|y*) $echo "-- " >> $tmpart cat $dotdir/.signature >> $tmpart ;; esac fi case $mailer in *sendmail) $mailer -t <$tmpart ;; # but recmail does not know about Bcc, alas *recmail) $mailer <$tmpart ;; *) set X `$sed <$tmpart -n -e '/^To:/{' -e 's/To: *//p' -e q -e '}'` shift set X "$@" `$sed <$tmpart -n -e '/^Cc:/{' -e 's/Cc: *//p' -e q -e '}'` shift set X "$@" `$sed <$tmpart -n -e '/^Bcc:/{' -e 's/Bcc: *//p' -e q -e '}'` shift $grep -v "^Bcc:" <$tmpart | $mailer "$@" ;; esac case $? in 0) state=cleanup ;; *) state=rescue ;; esac ;; rescue) $cat $tmpart >> ${HOME-$LOGDIR}/dead.letter $echo "Message appended to ${HOME-$LOGDIR}/dead.letter" $echo "A copy may be temporarily found in $tmpart" chmod -f o-rwx ${HOME-$LOGDIR}/dead.letter $tmpart exit ;; cleanup) case "${MAILRECORD-none}" in none) ;; *) set X ${USER-${LOGNAME-`$who am i`}} unknown shift $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $MAILRECORD "From $1 `LANG= date`" if $test $? -eq 0 ; then $echo "Message appended to $MAILRECORD" else $echo "Cannot append to $MAILRECORD" fi ;; esac exit ;; esac done !NO!SUBS! $eunicefix Rnmail chmod 755 Rnmail trn-3.6/edit_dist.c0000664000000000000000000001731505420174065011203 0ustar /* The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" /* Declare MEM_SIZE */ #include "util.h" /* Declare safemalloc() */ #ifdef EDIT_DISTANCE /* edit_dist -- returns the minimum edit distance between two strings Program by: Mark Maimone CMU Computer Science 13 Nov 89 Last Modified: 28 Jan 90 If the input strings have length n and m, the algorithm runs in time O(nm) and space O(min(m,n)). HISTORY 13 Nov 89 (mwm) Created edit_dist() and set_costs(). 28 Jan 90 (mwm) Added view_costs(). Should verify that THRESHOLD computations will work even when THRESHOLD is not a multiple of sizeof(int). 17 May 93 (mwm) Improved performance when used with trn's newsgroup processing; assume all costs are 1, and you can terminate when a threshold is exceeded. */ #define TRN_SPEEDUP /* Use a less-general version of the routine, one that's better for trn. All change costs are 1, and it's okay to terminate if the edit distance is known to exceed MIN_DIST */ #define THRESHOLD 4000 /* worry about allocating more memory only when this # of bytes is exceeded */ #define STRLENTHRESHOLD ((int) ((THRESHOLD / sizeof (int) - 3) / 2)) #define SAFE_ASSIGN(x,y) (((x) != NULL) ? (*(x) = (y)) : (y)) #define swap_int(x,y) (_iswap = (x), (x) = (y), (y) = _iswap) #define swap_char(x,y) (_cswap = (x), (x) = (y), (y) = _cswap) #define min3(x,y,z) (_mx = (x), _my = (y), _mz = (z), (_mx < _my ? (_mx < _mz ? _mx : _mz) : (_mz < _my) ? _mz : _my)) #define min2(x,y) (_mx = (x), _my = (y), (_mx < _my ? _mx : _my)) static int insert_cost = 1; static int delete_cost = 1; static int change_cost = 1; static int swap_cost = 1; static int _iswap; /* swap_int temp variable */ static char *_cswap; /* swap_char temp variable */ static int _mx, _my, _mz; /* min2, min3 temp variables */ void view_costs(ins, del, ch, swap) int *ins, *del, *ch, *swap; { SAFE_ASSIGN(ins, insert_cost); SAFE_ASSIGN(del, delete_cost); SAFE_ASSIGN(ch, change_cost); SAFE_ASSIGN(swap, swap_cost); } /* view_costs */ void set_costs(ins, del, ch, swap) int ins, del, ch, swap; { insert_cost = ins; delete_cost = del; change_cost = ch; swap_cost = swap; } /* set_costs */ /* edit_distn -- returns the edit distance between two strings, or -1 on failure */ int edit_distn(from, from_len, to, to_len) char *from, *to; register int from_len, to_len; { #ifndef TRN_SPEEDUP register int ins, del, ch; /* local copies of edit costs */ #endif register int row, col, index; /* dynamic programming counters */ register int radix; /* radix for modular indexing */ #ifdef TRN_SPEEDUP register int low; #endif int *buffer; /* pointer to storage for one row of the d.p. array */ static int store[THRESHOLD / sizeof (int)]; /* a small amount of static storage, to be used when the input strings are small enough */ /* Handle trivial cases when one string is empty */ if (from == NULL || !from_len) if (to == NULL || !to_len) return 0; else return to_len * insert_cost; else if (to == NULL || !to_len) return from_len * delete_cost; /* Initialize registers */ radix = 2 * from_len + 3; #ifdef TRN_SPEEDUP #define ins 1 #define del 1 #define ch 1 #define swap_cost 1 #else ins = insert_cost; del = delete_cost; ch = change_cost; #endif /* Make from short enough to fit in the static storage, if it's at all possible */ if (from_len > to_len && from_len > STRLENTHRESHOLD) { swap_int(from_len, to_len); swap_char(from, to); #ifndef TRN_SPEEDUP swap_int(ins, del); #endif } /* if from_len > to_len */ /* Allocate the array storage (from the heap if necessary) */ if (from_len <= STRLENTHRESHOLD) buffer = store; else buffer = (int *) safemalloc((MEM_SIZE) radix * sizeof (int)); /* Here's where the fun begins. We will find the minimum edit distance using dynamic programming. We only need to store two rows of the matrix at a time, since we always progress down the matrix. For example, given the strings "one" and "two", and insert, delete and change costs equal to 1: _ o n e _ 0 1 2 3 t 1 1 2 3 w 2 2 2 3 o 3 2 3 3 The dynamic programming recursion is defined as follows: ar(x,0) := x * insert_cost ar(0,y) := y * delete_cost ar(x,y) := min(a(x - 1, y - 1) + (from[x] == to[y] ? 0 : change), a(x - 1, y) + insert_cost, a(x, y - 1) + delete_cost, a(x - 2, y - 2) + (from[x] == to[y-1] && from[x-1] == to[y] ? swap_cost : infinity)) Since this only looks at most two rows and three columns back, we need only store the values for the two preceeding rows. In this implementation, we do not explicitly store the zero column, so only 2 * from_len + 2 words are needed. However, in the implementation of the swap_cost check, the current matrix value is used as a buffer; we can't overwrite the earlier value until the swap_cost check has been performed. So we use 2 * from_len + 3 elements in the buffer. */ #define ar(x,y,index) (((x) == 0) ? (y) * del : (((y) == 0) ? (x) * ins : \ buffer[mod(index)])) #define NW(x,y) ar(x, y, index + from_len + 2) #define N(x,y) ar(x, y, index + from_len + 3) #define W(x,y) ar(x, y, index + radix - 1) #define NNWW(x,y) ar(x, y, index + 1) #define mod(x) ((x) % radix) index = 0; #ifdef DEBUG_EDITDIST printf(" "); for (col = 0; col < from_len; col++) printf(" %c ", from[col]); printf("\n "); for (col = 0; col <= from_len; col++) printf("%2d ", col * del); #endif /* Row 0 is handled implicitly; its value at a given column is col*del. The loop below computes the values for Row 1. At this point we know the strings are nonempty. We also don't need to consider swap costs in row 1. COMMENT: the indicies row and col below point into the STRING, so the corresponding MATRIX indicies are row+1 and col+1. */ buffer[index++] = min2(ins + del, (from[0] == to[0] ? 0 : ch)); #ifdef TRN_SPEEDUP low = buffer[mod(index + radix - 1)]; #endif #ifdef DEBUG_EDITDIST printf("\n %c %2d %2d ", to[0], ins, buffer[index - 1]); #endif for (col = 1; col < from_len; col++) { buffer[index] = min3( col * del + ((from[col] == to[0]) ? 0 : ch), (col + 1) * del + ins, buffer[index - 1] + del); #ifdef TRN_SPEEDUP if (buffer[index] < low) low = buffer[index]; #endif index++; #ifdef DEBUG_EDITDIST printf("%2d ", buffer[index - 1]); #endif } /* for col = 1 */ #ifdef DEBUG_EDITDIST printf("\n %c %2d ", to[1], 2 * ins); #endif /* Now handle the rest of the matrix */ for (row = 1; row < to_len; row++) { for (col = 0; col < from_len; col++) { buffer[index] = min3( NW(row, col) + ((from[col] == to[row]) ? 0 : ch), N(row, col + 1) + ins, W(row + 1, col) + del); if (from[col] == to[row - 1] && col > 0 && from[col - 1] == to[row]) buffer[index] = min2(buffer[index], NNWW(row - 1, col - 1) + swap_cost); #ifdef DEBUG_EDITDIST printf("%2d ", buffer[index]); #endif #ifdef TRN_SPEEDUP if (buffer[index] < low || col == 0) low = buffer[index]; #endif index = mod(index + 1); } /* for col = 1 */ #ifdef DEBUG_EDITDIST if (row < to_len - 1) printf("\n %c %2d ", to[row+1], (row + 2) * ins); else printf("\n"); #endif #ifdef TRN_SPEEDUP if (low > MIN_DIST) break; #endif } /* for row = 1 */ row = buffer[mod(index + radix - 1)]; if (buffer != store) free((char *) buffer); return row; } /* edit_distn */ #endif /* EDIT_DISTANCE */ trn-3.6/nghash.c0000664000000000000000000002334205644354551010510 0ustar /* $Id: nghash.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "ndir.h" #include "rcstuff.h" #include "trn.h" #include "intrp.h" #include "final.h" #include "rcln.h" #include "util.h" #include "nntp.h" #include "ngdata.h" #include "hash.h" #include "term.h" static HASHTABLE *acthash = 0; static char *actfile = 0; void ngdatahash_init() { register long offset; char *cp = filexp(ACTIVE); struct stat actstat; #ifdef USE_NNTP nntp_command("LIST"); /* tell server we want the active file */ if (nntp_check(TRUE) != NNTP_CLASS_OK) { /* and then see if that's ok */ printf("Can't get active file from server: \n%s\n", ser_line); finalize(1); } time(&lastactfetch); strcpy(active_name, cp); actfp = fopen(active_name, "w+"); /* and get ready */ if (actfp == Nullfp) { printf(cantopen,active_name) FLUSH; finalize(1); } while (1) { nntp_gets(ser_line, sizeof ser_line); if (NNTP_LIST_END(ser_line)) /* while there's another line */ break; /* get it and write it to */ fputs(ser_line, actfp); putc('\n', actfp); } fflush(actfp); if (ferror(actfp)) { printf("Error writing to active file %s.\n", active_name) FLUSH; finalize(1); } fseek(actfp,0L,0); /* rewind file */ #else /* !USE_NNTP */ actfp = fopen(cp, "r"); if (actfp == Nullfp) { printf(cantopen, cp) FLUSH; finalize(1); } #endif /* rn was being a slug about rereading the active file over and * over; try using a really big buffer to keep it in core. */ (void) fstat(fileno(actfp), &actstat); if (actfile) free(actfile); actfile = safemalloc(actstat.st_size + 1); /* * NOTE: this won't work on machines with ints too small to hold * the size of the active file. */ if (fread(actfile, 1, (int)actstat.st_size, actfp) != actstat.st_size) { fprintf(stderr, "active file not read\n"); actfile[0] = '\0'; } actfile[actstat.st_size] = '\0'; if (acthash) hashdestroy(acthash); acthash = hashcreate((int)(actstat.st_size/40), (int (*)())NULL); /* count entries */ for (activeitems=0, offset=0; offset < actstat.st_size; activeitems++) { register char *p; HASHDATUM data; data.dat_ptr = actfile + offset; if ((p = index(data.dat_ptr, '\n')) == NULL) data.dat_len = actstat.st_size - offset; else data.dat_len = p - data.dat_ptr + 1; if ((p = index(data.dat_ptr, ' ')) == NULL) p = data.dat_ptr; hashstore(acthash, data.dat_ptr, p - data.dat_ptr, data); offset += data.dat_len; } } ACT_POS findact(outbuf, nam, len, suggestion) register char *outbuf; register char *nam; register int len; register long suggestion; { register ACT_POS retval; #ifdef USE_NNTP if (!actfp) { switch (nntp_list("active", nam, len)) { case 0: return -1; case 1: strcpy(outbuf, ser_line); do { nntp_gets(ser_line, sizeof ser_line); } while (!NNTP_LIST_END(ser_line)); break; } return 0; } #endif /* see if we know the right place and can just return */ if (suggestion != 0 && fseek(actfp, suggestion, 0) >= 0 && fgets(outbuf, LBUFLEN, actfp) != NULL && outbuf[len] == ' ' && strnEQ(outbuf, nam, len)) return suggestion; /* hmm, apparently not, gotta look it up. */ #ifdef DEBUG if (debug & DEB_SOFT_POINTERS) printf("Missed, looking for %s in %sLen = %d\n",nam,outbuf,len) FLUSH; #endif /* can we just do a quick hashed lookup? */ if (acthash != NULL) { HASHDATUM data; outbuf[0] = '\0'; data = hashfetch(acthash, nam, len); if (data.dat_ptr == NULL) return -1; else { (void) bcopy(data.dat_ptr, outbuf, (int)data.dat_len); outbuf[data.dat_len] = '\0'; return data.dat_ptr - actfile; } } /* apparently not. gotta do it the slow way. */ (void) fseek(actfp, 0L, 0); for (retval=0; fgets(outbuf,LBUFLEN,actfp)!=NULL; retval+=strlen(outbuf)) if (outbuf[len] == ' ' && strnEQ(outbuf, nam, len)) break; if (ferror(actfp)) { perror("error on active file"); sig_catcher(0); /* NOTREACHED */ } else if (feof(actfp)) return -1; /* no such group */ return retval; } #ifdef EDIT_DISTANCE /* Edit Distance extension to trn * * Mark Maimone (mwm@cmu.edu) * Carnegie Mellon Computer Science * 9 May 1993 * * This code helps trn handle typos in newsgroup names much more * gracefully. Instead of "... does not exist!!", it will pick the * nearest one, or offer you a choice if there are several options. */ #define LENGTH_HACK 5 /* Don't bother comparing strings with lengths * that differ by more than this; making this * smaller than MIN_DIST prunes the number of * calls to edit_dist(). We won't catch any * sequences of MIN_DIST INSERTs or DELETEs, * but we (should!) save some time. */ #define MAX_NG 9 /* Maximum number of options */ #define ABS(x) (((x) < 0) ? -(x) : (x)) static void check_distance _((HASHDATUM*,int)); static char **ngptrs; /* List of potential matches */ static int ngn; /* Length of list in ngptrs[] */ static int nglen; /* Length of name in ngname */ static int best_match; /* Value of best match */ /* find_close_match -- Finds the closest match for the string given in * global ngname. If found, the result will be the corrected string * returned in that global. * * We compare the (presumably misspelled) newsgroup name with all legal * newsgroups, using the Edit Distance metric. The edit distance between * two strings is the minimum number of simple operations required to * convert one string to another (the implementation here supports INSERT, * DELETE, CHANGE and SWAP). This gives every legal newsgroup an integer * rank. * * You might want to present all of the closest matches, and let the user * choose among them. But because I'm lazy I chose to only keep track of * all with newsgroups with the *single* smallest error, in array ngptrs[]. * A more flexible approach would keep around the 10 best matches, whether * or not they had precisely the same edit distance, but oh well. */ int find_close_match() { int ret = 0; best_match = -1; ngptrs = (char**)safemalloc(MAX_NG * sizeof (char*)); ngn = 0; nglen = strlen(ngname); /* Iterate over all legal newsgroups */ hashwalk(acthash, check_distance, 0); /* ngn is the number of possibilities. If there's just one, go with it. */ switch (ngn) { case 0: break; case 1: { char *cp = index(ngptrs[0], ' '); *cp = '\0'; #ifdef VERBOSE IF(verbose) printf("(I assume you meant %s)\n", ngptrs[0]) FLUSH; ELSE #endif #ifdef TERSE printf("(Using %s)\n", ngptrs[0]) FLUSH; #endif set_ngname(ngptrs[0]); *cp = ' '; ret = 1; break; } default: ret = get_near_miss(); break; } /* switch */ free((char*)ngptrs); return ret; } /* find_close_match */ static void check_distance(data, ngnum) HASHDATUM *data; int ngnum; { int value, len; char *cp; if ((cp = index(data->dat_ptr, ' ')) == NULL) return; len = cp - data->dat_ptr; /* Efficiency: don't call edit_dist when the lengths are already too * different */ if (ABS(len - nglen) > LENGTH_HACK) return; value = edit_distn(ngname, strlen(ngname), data->dat_ptr, len); if (value > MIN_DIST) return; if (value < best_match) ngn = 0; if (best_match < 0 || value <= best_match) { best_match = value; if (ngn < MAX_NG) ngptrs[ngn++] = data->dat_ptr; } /* if */ } /* Now we've got several potential matches, and have to choose between them * somehow. Again, results will be returned in global ngname */ int get_near_miss() { char promptbuf[256]; char options[MAX_NG+10], *op = options; int i; #ifdef VERBOSE IF(verbose) printf("However, here are some close matches:\n") FLUSH; #endif if (ngn > 9) ngn = 9; /* Since we're using single digits.... */ for (i = 0; i < ngn; i++) { char *cp = index(ngptrs[i], ' '); *cp = '\0'; printf(" %d. %s\n", i+1, ngptrs[i]); sprintf(op++, "%d", i+1); /* Expensive, but avoids ASCII deps */ *cp = ' '; } *op++ = 'n'; *op = '\0'; #ifdef VERBOSE IF(verbose) sprintf(promptbuf, "Which of these would you like?"); ELSE #endif #ifdef TERSE sprintf(promptbuf, "Which?"); #endif reask: in_char(promptbuf, 'A', options); #ifdef VERIFY printcmd(); #endif putchar('\n') FLUSH; switch (*buf) { case 'n': case 'N': case 'q': case 'Q': case 'x': case 'X': return 0; break; case 'h': case 'H': #ifdef VERBOSE IF(verbose) 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; ELSE #endif #ifdef TERSE fputs("Illegal newsgroup, enter a number or 'n'.\n", stdout) FLUSH; #endif goto reask; default: if (isdigit(*buf)) { char *s = index(options, *buf); i = s ? (s - options) : ngn; if (i >= 0 && i < ngn) { char *cp = index(ngptrs[i], ' '); *cp = '\0'; set_ngname(ngptrs[i]); *cp = ' '; return 1; } /* if */ } /* if */ fputs(hforhelp, stdout) FLUSH; break; } /* switch */ settle_down(); goto reask; } /* get_near_miss */ #endif /* EDIT_DISTANCE */ trn-3.6/ng.c0000664000000000000000000010471105663312260007634 0ustar /* $Id: ng.c,v 3.0 1992/02/01 03:09:32 davison Trn $ */ /* This software is Copyright 1991 by Stan Barber. * * Permission is hereby granted to copy, reproduce, redistribute or otherwise * use this software as long as: there is no monetary profit gained * specifically from the use or reproduction of this software, it is not * sold, rented, traded or otherwise marketed, and this copyright notice is * included prominently in any copy made. * * The authors make no claims as to the fitness or correctness of this software * for any use whatsoever, and it is provided as is. Any use of this software * is at the user's own risk. */ #include "EXTERN.h" #include "common.h" #include "trn.h" #include "term.h" #include "final.h" #include "util.h" #include "cache.h" #include "bits.h" #include "artsrch.h" #include "help.h" #include "kfile.h" #include "rcstuff.h" #include "head.h" #include "art.h" #include "artio.h" #include "ngstuff.h" #include "intrp.h" #include "respond.h" #include "ngdata.h" #include "backpage.h" #include "rcln.h" #include "sw.h" #include "last.h" #include "search.h" #include "nntp.h" #include "rthread.h" #include "rt-select.h" #include "rt-wumpus.h" #include "decode.h" #include "charsubst.h" #include "INTERN.h" #include "ng.h" #include "artstate.h" /* somebody has to do it */ /* art_switch() return values */ #define AS_NORM 0 #define AS_INP 1 #define AS_ASK 2 #define AS_CLEAN 3 int exit_code = NG_NORM; void ng_init() { #ifdef KILLFILES open_kfile(KF_GLOBAL); #endif #ifdef CUSTOMLINES init_compex(&hide_compex); init_compex(&page_compex); #endif } /* do newsgroup on line ng with name ngname */ /* assumes that we are chdir'ed to NEWSSPOOL, and assures that that is * still true upon return, but chdirs to NEWSSPOOL/ngname in between * * If you can understand this routine, you understand most of the program. * The basic structure is: * for each desired article * for each desired page * for each line on page * if we need another line from file * get it * if it's a header line * do special things * for each column on page * put out a character * end loop * end loop * end loop * end loop * * (Actually, the pager is in another routine.) * * The chief problem is deciding what is meant by "desired". Most of * the messiness of this routine is due to the fact that people want * to do unstructured things all the time. I have used a few judicious * goto's where I thought it improved readability. The rest of the messiness * arises from trying to be both space and time efficient. Have fun. */ int do_newsgroup(start_command) char *start_command; /* command to fake up first */ { char oldmode = mode; #ifdef CHARSUBST char *whatnext = "%s%sWhat next? [%s]"; charsubst = charsets; #else char *whatnext = "%sWhat next? [%s]"; #endif exit_code = NG_NORM; localkf_changes = 0; killfirst = 0; if (extractdest) { free(extractdest); extractdest = Nullch; } if (extractprog) { free(extractprog); extractprog = Nullch; } /* initialize the newsgroup data structures */ if (!access_ng()) return -1; #ifdef ARTSEARCH srchahead = (scanon && !ThreadedGroup /* did they say -S? */ && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0); #endif /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */ forcelast = TRUE; /* if 0 unread, do not bomb out */ recent_artp = curr_artp = Nullart; recent_art = curr_art = lastart+1; prompt = whatnext; /* remember what newsgroup we were in for sake of posterity */ writelast(); /* see if there are any special searches to do */ has_normal_kills = FALSE; #ifdef KILLFILES open_kfile(KF_LOCAL); # ifdef VERBOSE IF(verbose) kill_unwanted(firstart,"Processing memorized commands...\n\n",TRUE); ELSE # endif # ifdef TERSE kill_unwanted(firstart,"Auto-processing...\n\n",TRUE); # endif #endif if (!selected_count) selected_only = FALSE; top_article(); /* do they want a special top line? */ firstline = getval("FIRSTLINE",Nullch); /* custom line suppression, custom page ending */ #ifdef CUSTOMLINES if (hideline = getval("HIDELINE",Nullch)) compile(&hide_compex,hideline,TRUE,TRUE); if (pagestop = getval("PAGESTOP",Nullch)) compile(&page_compex,pagestop,TRUE,TRUE); #endif /* now read each unread article */ rc_changed = doing_ng = TRUE; /* enter the twilight zone */ if (!unsafe_rc_saves) checkcount = 0; /* do not checkpoint for a while */ do_fseek = FALSE; /* start 1st article at top */ for (; art<=lastart+1; ) { /* for each article */ mode = 'a'; /* do we need to "grow" the newsgroup? */ if ((art > lastart || forcegrow) && !keep_the_group_static) { ART_NUM oldlast = lastart; #ifdef USE_NNTP if ((actfp || getngsize(ng) > lastart) && !nntp_group(ngname,ng)) { fprintf(stderr,"Your server went south for the winter:\n%s\n", ser_line); finalize(1); } if (ngmax[ng] > lastart) { grow_ng(ngmax[ng]); } #else grow_ng(getngsize(ng)); #endif if (forcelast && art > oldlast) art = lastart+1; } if (!artp || (artp->flags & AF_TMPMEM) != AF_TMPMEM || art != 0) artp = find_article(art); if (start_command) { /* do we have an initial command? */ hide_pending(); pushstring(start_command, 0); free(start_command); start_command = Nullch; art = curr_art = lastart+1; artp = curr_artp = Nullart; if (input_pending()) goto reinp_article; } if (art>lastart) { /* are we off the end still? */ ARTICLE *ap; ART_NUM i; art = lastart + 1; /* keep pointer references sane */ if (!forcelast && toread[ng] && selected_only && !selected_count) { art = curr_art; artp = curr_artp; strcpy(buf, "+"); goto article_level; } count_subjects(CS_RETAIN); for (i = absfirst, ap = article_ptr(i); i <= lastart; i++, ap++) if (!(ap->flags & (AF_READ|AF_CACHED))) article_count++; toread[ng] = (ART_UNREAD)article_count; if (artp != curr_artp) { recent_art = curr_art; /* remember last article # (for '-') */ curr_art = art; /* set current article # */ recent_artp = curr_artp; curr_artp = artp; } if (erase_screen) clear(); /* clear the screen */ else fputs("\n\n",stdout) FLUSH; #ifdef VERBOSE IF(verbose) printf("End of newsgroup %s.",ngname); /* print pseudo-article */ ELSE #endif #ifdef TERSE printf("End of %s",ngname); #endif if (article_count) { if (selected_only) printf(" (%ld + %ld articles still unread)", (long)selected_count, (long)article_count-selected_count); else printf(" (%ld article%s still unread)", (long)article_count,article_count==1?nullstr:"s"); } if (redirected) { if (redirected == nullstr) printf("\n\n** This group has been disabled by your news admin **"); else printf("\n\n** Please start using %s **", redirected); } else if (!article_count && !forcelast) goto cleanup; /* actually exit newsgroup */ mode = 'e'; prompt = whatnext; #ifdef ARTSEARCH srchahead = 0; /* no more subject search mode */ #endif fputs("\n\n",stdout) FLUSH; } else if (!reread && (was_read(art) || (artp->flags & AF_MISSING) || (selected_only && !(artp->flags & AF_SEL)))) { /* has this article been read? */ inc_art(selected_only,FALSE);/* then skip it */ continue; } else if (!reread && !parseheader(art)) { oneless(artp); /* mark deleted as read */ ng_skip(); } else { /* we have a real live article */ if (artp != curr_artp) { recent_art = curr_art; /* remember last article # (for '-') */ curr_art = art; /* set current article # */ recent_artp = curr_artp; curr_artp = artp; } if (!do_fseek) { /* starting at top of article? */ artline = 0; /* start at the beginning */ topline = -1; /* and remember top line of screen */ /* (line # within article file) */ } clear(); /* clear screen */ #ifdef USE_NNTP if (art == 0 && artp && artp->msgid && !(artp->flags & AF_CACHED)) { art = nntp_stat_id(artp->msgid); if (art) artp = find_article(art); } #endif if (!artopen(art)) { /* make sure article is found & open */ char tmpbuf[256]; ART_LINE linenum; /* see if we have tree data for this article anyway */ init_tree(); sprintf(tmpbuf,"%s: article is not available.",ngname); if (artp && !(artp->flags & AF_CACHED)) { if (absfirst < first_cached || last_cached < lastart || !cached_all_in_range) sprintf(tmpbuf,"%s: article may show up in a moment.", ngname); } linenum = tree_puts(tmpbuf,0,0); vwtary(artline,(ART_POS)0); finish_tree(linenum); prompt = whatnext; #ifdef ARTSEARCH srchahead = 0; #endif } else { /* found it, so print it */ switch (do_article()) { case DA_CLEAN: /* quit newsgroup */ goto cleanup; case DA_TOEND: /* do not mark as read */ goto reask_article; case DA_RAISE: /* reparse command at end of art */ goto article_level; case DA_NORM: /* normal end of article */ break; } } if (art >= absfirst) /* don't mark non-existant articles */ mark_as_read(); /* mark current article as read */ do_hiding = TRUE; #ifdef ROTATION rotate = FALSE; #endif } /* if these gotos bother you, think of this as a little state machine */ reask_article: #ifdef MAILCALL setmail(FALSE); #endif setdfltcmd(); #ifdef CLEAREOL if (erase_screen && can_home_clear) clear_rest(); #endif /* CLEAREOL */ unflush_output(); /* disable any ^O in effect */ standout(); /* enter standout mode */ /* print prompt, whatever it is */ interp(cmd_buf, sizeof cmd_buf, mailcall); #ifdef CHARSUBST printf(prompt,cmd_buf,current_charsubst(),dfltcmd); #else printf(prompt,cmd_buf,dfltcmd); #endif un_standout(); /* leave standout mode */ putchar(' '); fflush(stdout); reinp_article: reread = FALSE; forcelast = FALSE; eat_typeahead(); #ifdef PENDING look_ahead(); /* see what we can do in advance */ cache_until_key(); #endif art = curr_art; artp = curr_artp; getcmd(buf); if (errno || *buf == '\f') { if (LINES < 100 && !int_count) *buf = '\f'; /* on CONT fake up refresh */ else { putchar('\n') FLUSH; /* but only on a crt */ goto reask_article; } } article_level: output_chase_phrase = TRUE; /* parse and process article level command */ switch (art_switch()) { case AS_INP: /* multichar command rubbed out */ goto reinp_article; case AS_ASK: /* reprompt "End of article..." */ goto reask_article; case AS_CLEAN: /* exit newsgroup */ goto cleanup; case AS_NORM: /* display article art */ break; } } /* end of article selection loop */ /* shut down newsgroup */ cleanup: decode_end(); #ifdef KILLFILES kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE); /* do cleanup from KILL file, if any */ #endif chase_xrefs(FALSE); in_ng = FALSE; /* leave newsgroup state */ if (artfp != Nullfp) { /* article still open? */ fclose(artfp); /* close it */ artfp = Nullfp; /* and tell the world */ openart = 0; } putchar('\n') FLUSH; deselect_all(); yankback(); /* do a Y command */ bits_to_rc(); /* reconstitute .newsrc line */ doing_ng = FALSE; /* tell sig_catcher to cool it */ if (!unsafe_rc_saves) write_rc(); /* and update .newsrc */ rc_changed = FALSE; /* tell sig_catcher it is ok */ if (chdir(spool)) { printf(nocd,spool) FLUSH; sig_catcher(0); } #ifdef KILLFILES if (localkfp) { fclose(localkfp); localkfp = Nullfp; } #endif mode = oldmode; return exit_code; } /* Whew! */ /* decide what to do at the end of an article */ int art_switch() { register ART_NUM i; setdef(buf,dfltcmd); #ifdef VERIFY printcmd(); #endif switch (*buf) { case '<': /* goto previous subject/thread */ visit_prev_thread(); return AS_NORM; case '>': /* goto next subject/thread */ visit_next_thread(); return AS_NORM; case 'U': { /* unread some articles */ char *u_prompt, *u_help_thread; if (!artp) { u_help_thread = nullstr; #ifdef VERBOSE IF(verbose) u_prompt = "\nSet unread: +select or all?"; ELSE #endif #ifdef TERSE u_prompt = "\nSet unread?"; #endif dfltcmd = "+an"; } else { #ifdef VERBOSE IF(verbose) { u_prompt = "\n\ Set unread: +select, thread, subthread, or all?"; u_help_thread = "\ Type t or SP to mark this thread's articles as unread.\n\ Type s to mark the current article and its descendants as unread.\n"; } ELSE #endif #ifdef TERSE { u_prompt = "\nSet unread?"; u_help_thread = "\ t or SP to mark thread unread.\n\ s to mark subthread unread.\n"; } #endif dfltcmd = "+tsan"; } reask_unread: in_char(u_prompt,'u',dfltcmd); #ifdef VERIFY printcmd(); #endif putchar('\n') FLUSH; if (*buf == 'h') { #ifdef VERBOSE IF(verbose) { fputs("\ Type + to enter select thread mode using all the already-read articles.\n\ (The selected threads will be marked as unread and displayed as usual.)\n\ ",stdout) FLUSH; fputs(u_help_thread,stdout); fputs("\ Type a to mark all articles in this group as unread.\n\ Type n to change nothing.\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\ + to select threads from the unread.\n\ ",stdout) FLUSH; fputs(u_help_thread,stdout); fputs("\ a to mark all articles unread.\n\ n to change nothing.\n\ ",stdout) FLUSH; } #endif goto reask_unread; } else if (*buf == 'n' || *buf == 'q') return AS_ASK; else if (*buf == 't' && u_help_thread != nullstr) { unkill_thread(artp->subj->thread); if ((artp = first_art(artp->subj)) != Nullart) art = article_num(artp); } else if (*buf == 's' && u_help_thread != nullstr) unkill_subthread(artp); else if (*buf == 'a') { register ARTICLE *ap; check_first(absfirst); ap = article_ptr(absfirst); for (i = absfirst; i <= lastart; i++, ap++) if ((ap->flags & (AF_READ|AF_MISSING)) == AF_READ) { ap->flags &= ~AF_READ; /* mark as unread */ toread[ng]++; } count_subjects(CS_NORM); } else if (*buf == '+') { *buf = 'U'; goto run_the_selector; } else { fputs(hforhelp,stdout) FLUSH; settle_down(); goto reask_unread; } return AS_NORM; } case '[': /* goto parent article */ case '{': /* goto thread's root article */ if (artp && ThreadedGroup) { if (!find_parent(*buf == '{')) { register char *cp = (*buf=='['?"parent":"root"); #ifdef VERBOSE IF(verbose) printf("\nThere is no %s article prior to this one.\n", cp) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo prior %s.\n",cp) FLUSH; #endif return AS_ASK; } reread = TRUE; return AS_NORM; } not_threaded: if (!artp) { #ifdef VERBOSE IF(verbose) fputs("\nYou're at the end of the group.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nEnd of group.\n",stdout) FLUSH; #endif return AS_ASK; } #ifdef VERBOSE IF(verbose) fputs("\nThis group is not threaded.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nUnthreaded group.\n",stdout) FLUSH; #endif return AS_ASK; case ']': /* goto child article */ case '}': /* goto thread's leaf article */ if (artp && ThreadedGroup) { if (!find_leaf(*buf == '}')) { #ifdef VERBOSE IF(verbose) fputs("\n\ This is the last leaf in this tree.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nLast leaf.\n",stdout) FLUSH; #endif return AS_ASK; } reread = TRUE; return AS_NORM; } goto not_threaded; case '(': /* goto previous sibling */ case ')': /* goto next sibling */ if (artp && ThreadedGroup) { if (!(*buf == '(' ? find_prev_sib() : find_next_sib())) { register char *cp = (*buf == '(' ? "previous" : "next"); #ifdef VERBOSE IF(verbose) printf("\nThis article has no %s sibling.\n",cp) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo %s sibling.\n",cp) FLUSH; #endif return AS_ASK; } reread = TRUE; return AS_NORM; } goto not_threaded; case 'T': if (!ThreadedGroup) goto not_threaded; /* FALL THROUGH */ case 'A': if (!artp) goto not_threaded; switch (ask_memorize(*buf)) { case ',': case 'j': return AS_NORM; } return AS_ASK; case 'K': if (!artp) goto not_threaded; /* first, write kill-subject command */ (void)art_search(buf, (sizeof buf), TRUE); art = curr_art; artp = curr_artp; kill_subject(artp->subj,KF_ALL);/* take care of any prior subjects */ return AS_NORM; case ',': /* kill this node and all descendants */ if (!artp) goto not_threaded; if (ThreadedGroup) kill_subthread(artp,KF_ALL); else if (art >= absfirst && art <= lastart) mark_as_read(); return AS_NORM; case 'J': /* Junk all nodes in this thread */ if (!artp) goto not_threaded; if (ThreadedGroup) { kill_thread(artp->subj->thread,KF_ALL); return AS_NORM; } /* FALL THROUGH */ case 'k': /* kill current subject */ if (!artp) goto not_threaded; kill_subject(artp->subj,KF_ALL); if (!ThreadedGroup || last_cached < lastart) { *buf = 'k'; goto normal_search; } return AS_NORM; case 't': carriage_return(); #ifndef CLEAREOL erase_eol(); /* erase the prompt */ #else if (erase_screen && can_home_clear) clear_rest(); else erase_eol(); /* erase the prompt */ #endif /* CLEAREOL */ fflush(stdout); page_line = 1; entire_tree(curr_artp); return AS_ASK; case ':': /* execute command on selected articles */ page_line = 1; if (!thread_perform()) return AS_INP; putchar('\n'); art = curr_art; artp = curr_artp; return AS_ASK; case 'p': /* find previous unread article */ do { dec_art(selected_only,FALSE); } while (art >= firstart && (was_read(art) || !parseheader(art))); #ifdef ARTSEARCH srchahead = 0; #endif if (art >= firstart) return AS_NORM; art = absfirst; /* FALL THROUGH */ case 'P': /* goto previous article */ dec_art(FALSE,TRUE); check_dec_art: if (art < absfirst) { #ifdef VERBOSE IF(verbose) printf("\nThere are no%s%s articles prior to this one.\n", *buf=='P'?nullstr:" unread", selected_only?" selected":nullstr) FLUSH; ELSE #endif #ifdef TERSE printf("\nNo previous%s%s articles\n", *buf=='P'?nullstr:" unread", selected_only?" selected":nullstr) FLUSH; #endif art = curr_art; artp = curr_artp; return AS_ASK; } reread = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '-': if (recent_art >= 0) { art = recent_art; artp = recent_artp; reread = TRUE; forcelast = TRUE; #ifdef ARTSEARCH srchahead = -(srchahead != 0); #endif return AS_NORM; } else { exit_code = NG_MINUS; return AS_CLEAN; } case 'n': /* find next unread article? */ if (art > lastart) { if (!toread[ng]) return AS_CLEAN; top_article(); } #ifdef ARTSEARCH else if (scanon && !ThreadedGroup && srchahead) { *buf = Ctl('n'); if (!next_art_with_subj()) goto normal_search; return AS_NORM; } #endif else { inc_art(selected_only,FALSE); if (art > lastart) top_article(); } #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case 'N': /* goto next article */ if (art > lastart) if (!first_subject) { art = absfirst; artp = article_ptr(art); } else { artp = first_subject->articles; if (artp->flags & AF_MISSING) inc_art(FALSE,TRUE); else art = article_num(artp); } else inc_art(FALSE,TRUE); if (art <= lastart) reread = TRUE; else forcelast = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '$': art = lastart+1; artp = Nullart; forcelast = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '1': case '2': case '3': /* goto specified article */ case '4': case '5': case '6': /* or do something with a range */ case '7': case '8': case '9': case '.': forcelast = TRUE; switch (numnum()) { case NN_INP: return AS_INP; case NN_ASK: return AS_ASK; case NN_REREAD: reread = TRUE; #ifdef ARTSEARCH if (srchahead) srchahead = -1; #endif break; case NN_NORM: if (was_read(art)) { top_article(); pad(just_a_sec/3); } else { putchar('\n'); return AS_ASK; } break; } return AS_NORM; case Ctl('k'): edit_kfile(); return AS_ASK; case Ctl('n'): /* search for next article with same subject */ case Ctl('p'): /* search for previous article with same subject */ if (*buf == Ctl('n')? next_art_with_subj() : prev_art_with_subj()) return AS_NORM; case '/': case '?': normal_search: #ifdef ARTSEARCH { /* search for article by pattern */ char cmd = *buf; reread = TRUE; /* assume this */ page_line = 1; switch (art_search(buf, (sizeof buf), TRUE)) { case SRCH_ERROR: art = curr_art; return AS_ASK; case SRCH_ABORT: art = curr_art; return AS_INP; case SRCH_INTR: #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH; ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art) FLUSH; #endif art = curr_art; /* restore to current article */ return AS_ASK; case SRCH_DONE: fputs("done\n",stdout) FLUSH; pad(just_a_sec/3); /* 1/3 second */ if (!srchahead) { art = curr_art; return AS_ASK; } top_article(); reread = FALSE; return AS_NORM; case SRCH_SUBJDONE: #ifdef UNDEF fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH; pad(just_a_sec/3); /* 1/3 second */ #endif top_article(); reread = FALSE; return AS_NORM; case SRCH_NOTFOUND: fputs("\n\n\n\nNot found.\n",stdout) FLUSH; art = curr_art; /* restore to current article */ return AS_ASK; case SRCH_FOUND: if (cmd == Ctl('n') || cmd == Ctl('p')) { oldsubject = TRUE; reread = FALSE; } break; } return AS_NORM; } #else /* !ARTSEARCH */ buf[1] = '\0'; notincl(buf); return AS_ASK; #endif case 'u': /* unsubscribe from this newsgroup? */ rcchar[ng] = NEGCHAR; return AS_CLEAN; case 'M': if (art <= lastart) { delay_unmark(artp); oneless(artp); printf("\nArticle %ld will return.\n",(long)art) FLUSH; } return AS_ASK; case 'm': if (art >= absfirst && art <= lastart) { unmark_as_read(); printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH; } return AS_ASK; case 'c': /* catch up */ switch (ask_catchup()) { case 'n': return AS_ASK; case 'u': return AS_CLEAN; } art = lastart+1; artp = Nullart; forcelast = FALSE; return AS_NORM; case 'Q': exit_code = NG_ASK; /* FALL THROUGH */ case 'q': /* go back up to newsgroup level? */ return AS_CLEAN; case 'j': putchar('\n') FLUSH; if (art >= absfirst && art <= lastart) mark_as_read(); return AS_ASK; case 'h': { /* help? */ int cmd; if ((cmd = help_art()) > 0) pushchar(cmd); return AS_ASK; } case '&': if (switcheroo()) /* get rest of command */ return AS_INP; /* if rubbed out, try something else */ return AS_ASK; case '#': #ifdef VERBOSE IF(verbose) printf("\nThe last article is %ld.\n",(long)lastart) FLUSH; ELSE #endif #ifdef TERSE printf("\n%ld\n",(long)lastart) FLUSH; #endif return AS_ASK; case '+': /* enter selection mode */ run_the_selector: *buf = do_selector(*buf); switch (*buf) { case '+': putchar('\n') FLUSH; return AS_ASK; case 'Q': exit_code = NG_ASK; /* FALL THROUGH */ case 'q': break; case 'N': exit_code = NG_SELNEXT; break; case 'P': exit_code = NG_SELPRIOR; break; default: if (toread[ng]) return AS_NORM; break; } return AS_CLEAN; case '=': { /* list subjects */ char tmpbuf[256]; ART_NUM oldart = art; int cmd, len; char *s; char *subjline = getval("SUBJLINE",Nullch); ARTICLE *ap = article_ptr(firstart); page_init(); for (i=firstart; i<=lastart && !int_count; i++, ap++) { if (!(ap->flags & AF_READ) && (s = fetchsubj(i,FALSE)) != Nullch) { sprintf(tmpbuf,"%5ld ", i); len = strlen(tmpbuf); if (subjline) { art = i; interp(tmpbuf + len, sizeof tmpbuf - len, subjline); } else safecpy(tmpbuf + len, s, sizeof tmpbuf - len); if (cmd = print_lines(tmpbuf,NOMARKING)) { if (cmd > 0) pushchar(cmd); break; } } } int_count = 0; art = oldart; return AS_ASK; } case '^': top_article(); #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; #ifdef DEBUG case 'D': printf("\nFirst article: %ld\n",(long)firstart) FLUSH; { ARTICLE *ap = article_ptr(firstart); for (i = firstart; i <= lastart && !int_count; i++, ap++) { if (ap->subj) printf("%5ld %c %s\n",i,(was_read(i)?'y':'n'), ap->subj->str) FLUSH; } } int_count = 0; return AS_ASK; #endif case 'v': if (art <= lastart) { reread = TRUE; do_hiding = FALSE; } return AS_NORM; #ifdef ROTATION case Ctl('x'): #endif case Ctl('r'): #ifdef ROTATION rotate = (*buf==Ctl('x')); #endif if (art <= lastart) reread = TRUE; else forcelast = TRUE; return AS_NORM; #ifdef ROTATION case 'X': rotate = !rotate; /* FALL THROUGH */ #else case Ctl('x'): case 'x': case 'X': notincl("x"); return AS_ASK; #endif case 'l': case Ctl('l'): /* refresh screen */ refresh_screen: if (art <= lastart) { reread = TRUE; clear(); do_fseek = TRUE; artline = topline; if (artline < 0) artline = 0; } return AS_NORM; case Ctl('^'): carriage_return(); erase_eol(); /* erase the prompt */ #ifdef MAILCALL setmail(TRUE); /* force a mail check */ #endif return AS_ASK; #ifdef INNERSEARCH case Ctl('e'): if (art <= lastart) { reread = TRUE; do_fseek = TRUE; innerlight = artline - 1; topline = artline; innersearch = artsize; gline = 0; hide_everything = 'b'; } return AS_NORM; #endif case 'B': /* back up one line */ case 'b': case Ctl('b'): /* back up a page */ if (art <= lastart) { ART_LINE target; reread = TRUE; clear(); do_fseek = TRUE; if (*buf == 'B') target = topline - 1; else { target = topline - (LINES - 2); if (marking && (marking_areas & BACKPAGE_MARKING)) { highlight = topline; } } artline = topline; if (artline >= 0) do { artline--; } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0); topline = artline; if (artline < 0) artline = 0; } return AS_NORM; case '!': /* shell escape */ if (escapade()) return AS_INP; return AS_ASK; case 'C': { cancel_article(); return AS_ASK; } case 'Z': case 'z': { supersede_article(); /* supersedes */ return AS_ASK; } case 'R': case 'r': { /* reply? */ reply(); return AS_ASK; } case 'F': case 'f': { /* followup command */ followup(); forcegrow = TRUE; /* recalculate lastart */ return AS_ASK; } case Ctl('f'): { /* forward? */ forward(); return AS_ASK; } case '|': case 'w': case 'W': case 's': case 'S': /* save command */ case 'e': /* extract command */ if (save_article() == SAVE_ABORT) return AS_INP; int_count = 0; return AS_ASK; case 'E': if (decode_fp) decode_end(); else putchar('\n') FLUSH; return AS_ASK; case 'Y': /* yank back M articles */ yankback(); top_article(); /* from the beginning */ return AS_NORM; /* pretend nothing happened */ #ifdef STRICTCR case '\n': fputs(badcr,stdout) FLUSH; return AS_ASK; #endif case '_': if (!finish_dblchar()) return AS_INP; switch (buf[1] & 0177) { case 'P': art--; goto check_dec_art; case 'N': if (art > lastart) art = absfirst; else art++; if (art <= lastart) reread = TRUE; #ifdef ARTSEARCH srchahead = 0; #endif return AS_NORM; case '+': if (!artp) goto not_threaded; if (ThreadedGroup) { select_arts_thread(artp, 0); printf("\nSelected all articles in this thread.\n"); } else { select_arts_subject(artp, 0); printf("\nSelected all articles in this subject.\n"); } if ((artp = first_art(artp->subj)) != Nullart) { if (art == article_num(artp)) return AS_ASK; art = article_num(artp); } return AS_NORM; case '-': if (!artp) goto not_threaded; if (sel_mode == SM_THREAD) { deselect_arts_thread(artp); printf("\nDeselected all articles in this thread.\n"); } else { deselect_arts_subject(artp); printf("\nDeselected all articles in this subject.\n"); } return AS_ASK; #ifdef CHARSUBST case 'C': if (!*(++charsubst)) charsubst = charsets; goto refresh_screen; #endif case 'a': case 's': case 't': case 'T': *buf = buf[1]; goto run_the_selector; } /* FALL THROUGH */ default: printf("\n%s",hforhelp) FLUSH; settle_down(); break; } return AS_ASK; } #ifdef MAILCALL /* see if there is any mail */ void setmail(force) bool_int force; { if (force) mailcount = 0; if (!(mailcount++)) { char *mailfile = filexp(getval("MAILFILE",MAILFILE)); if (stat(mailfile,&filestat) < 0 || !filestat.st_size || filestat.st_atime > filestat.st_mtime) mailcall = nullstr; else mailcall = getval("MAILCALL","(Mail) "); } mailcount %= 5; /* check every 5 articles */ } #endif void setdfltcmd() { if (!toread[ng]) { if (art > lastart) dfltcmd = "qnp"; else dfltcmd = "npq"; } else { #ifdef ARTSEARCH if (srchahead) dfltcmd = "^Nnpq"; else #endif dfltcmd = "npq"; } } /* Ask the user about catching-up the current group. Returns 'y' if yes, ** 'n' or 'N' if no ('N' means we used one line when in the selector), ** or 'u' for yes with unsubscribe. Actually performs the catchup and ** unsubscription as needed. */ char ask_catchup() { char ch; bool use_one_line = (mode == 't'); int leave = 0; if (!use_one_line) putchar('\n') FLUSH; reask_catchup: #ifdef VERBOSE IF(verbose) in_char("Do you really want to mark everything as read?",'C',"yn#h"); ELSE #endif #ifdef TERSE in_char("Really?",'C',"ynh"); #endif #ifdef VERIFY printcmd(); #endif if ((ch = *buf) == 'h') { use_one_line = FALSE; #ifdef VERBOSE IF(verbose) fputs("\n\ Type y or SP to mark all articles as read.\n\ Type n to leave articles marked as they are.\n\ Enter a number to mark all but the last # articles as read.\n\ Type u to mark everything read and unsubscribe.\n\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\ y or SP to mark all read.\n\ n to forget it.\n\ u to mark all and unsubscribe.\n\n\ ",stdout) FLUSH; #endif goto reask_catchup; } if (ch == 'n' || ch == 'q') { if (use_one_line) return 'N'; putchar('\n') FLUSH; return 'n'; } if (ch == '#') { use_one_line = FALSE; in_char("\nEnter the number of articles to leave unread: ", 'C', "0"); if ((ch = *buf) == '0') ch = 'y'; } if (isdigit(ch)) { buf[1] = FINISHCMD; if (!finish_command(FALSE)) { use_one_line = FALSE; putchar('\n') FLUSH; goto reask_catchup; } else { leave = atoi(buf); ch = 'y'; } } if (ch != 'y' && ch != 'u') { use_one_line = FALSE; printf("\n%s\n", hforhelp) FLUSH; settle_down(); goto reask_catchup; } if (mode == 'n') { putchar('\n') FLUSH; catch_up(ng, leave); } else { int i; ARTICLE *ap; for (i = firstart, ap = article_ptr(i); i <= lastart-leave; i++, ap++) ap->flags = ((ap->flags & ~sel_mask) | AF_READ); selected_count = selected_subj_cnt = selected_only = 0; toread[ng] = 0; if (dmcount) yankback(); putchar('\n') FLUSH; } if (ch == 'u') { rcchar[ng] = NEGCHAR; printf("(If you meant to hit 'y' instead of 'u', press '-'.)\n"); } return ch; } char ask_memorize(ch) char_int ch; { bool thread_cmd = (ch == 'T'); bool use_one_line = (mode == 't'); char *mode_string = (thread_cmd? "thread" : "subject"); char *mode_phrase = (thread_cmd? "replies to this article" : "this subject and all replies"); ART_NUM art_hold = art; ARTICLE *artp_hold = artp; if (!use_one_line) putchar('\n') FLUSH; sprintf(cmd_buf,"Memorize %s command:", mode_string); reask_memorize: in_char(cmd_buf, 'm', "+.j,cC"); #ifdef VERIFY printcmd(); #endif if ((ch = *buf) == 'h') { use_one_line = FALSE; #ifdef VERBOSE IF(verbose) printf("\n\ Type + or SP to auto-select this %s (i.e. includes future articles).\n\ Type . to auto-select %s.\n\ Type j to auto-kill (junk) this %s.\n\ Type , to auto-kill %s.\n\ Type c to clear all selection/killing on this %s.\n\ Type C to clear all selection/killing on %s.\n\ Type q to abort the operation.\n\n\ ",mode_string,mode_phrase,mode_string,mode_phrase,mode_string,mode_phrase) FLUSH; ELSE #endif #ifdef TERSE printf("\n\ + or SP auto-selects this %s.\n\ . auto-selects %s.\n\ j auto-kills this %s.\n\ , auto-kills %s.\n\ c clears auto-commands for this %s.\n\ C clears auto-commands for %s.\n\ q aborts.\n\n\ ",mode_string,mode_phrase,mode_string,mode_phrase,mode_string,mode_phrase) FLUSH; #endif goto reask_memorize; } if (ch == 'q') { if (use_one_line) return 'Q'; putchar('\n'); return 'q'; } if (ch == '+') { if (!thread_cmd) { (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; ch = '.'; } else ch = (use_one_line? '+' : '.'); if (thread_cmd) select_arts_thread(artp, AUTO_SELECTALL); else select_arts_subject(artp, 0); if (mode != 't') printf("\nSelection memorized.\n"); } else if (ch == '.') { if (!thread_cmd) { (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; } else ch = (use_one_line? '+' : '.'); select_subthread(artp,thread_cmd? AUTO_SELECT : 0); if (mode != 't') printf("\nSelection memorized.\n"); } else if (ch == 'j') { if (!thread_cmd) { *buf = 'K'; (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; } if (thread_cmd) kill_thread(artp->subj->thread,KF_ALL|KF_KILLFILE); else kill_subject(artp->subj,KF_ALL); if (mode != 't') printf("\nKill memorized.\n"); } else if (ch == ',') { if (!thread_cmd) { (void)art_search(buf, (sizeof buf), TRUE); art = art_hold; artp = artp_hold; } kill_subthread(artp,KF_ALL|(thread_cmd?KF_KILLFILE:0)); if (mode != 't') printf("\nKill memorized.\n"); } else if (ch == 'c') { if (thread_cmd) clear_thread(artp->subj->thread); else clear_subject(artp->subj); } else if (ch == 'C') { clear_subthread(artp); } else { use_one_line = FALSE; printf("\n%s\n", hforhelp) FLUSH; settle_down(); goto reask_memorize; } if (!use_one_line) putchar('\n') FLUSH; return ch; }