brad@looking.ON.CA (Brad Templeton) (12/20/89)
Posting-number: Volume 9, Issue 77 Submitted-by: brad@looking.ON.CA (Brad Templeton) Archive-name: newsclip/part08 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 8 (of 15)." # Contents: doc/filter.man getdate.y nrc.c # Wrapped by allbery@uunet on Tue Dec 19 20:10:01 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'doc/filter.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'doc/filter.man'\" else echo shar: Extracting \"'doc/filter.man'\" \(18501 characters\) sed "s/^X//" >'doc/filter.man' <<'END_OF_FILE' X.if n .ds La ' X.if n .ds Ra ' X.if t .ds La ` X.if t .ds Ra ' X.if n .ds Lq " X.if n .ds Rq " X.if t .ds Lq `` X.if t .ds Rq '' X.de Ch X\\$3\\*(Lq\\$1\\*(Rq\\$2 X.. X.TH FILTER 5 "May 10, 1989" X.ds ]W News Filter X.SH NAME Xfilter \- reader/newsfilter communications protocol. X.SH SYNOPSIS XThis document describes version 1.00 of the protocol used to implement Xnewsreader communication with newsfilter processes. The intent is to support Xconstruction of newsfilters that can communicate with any standard reader. X.SH DESCRIPTION XDuring initialization, newsreaders may attempt to establish communication Xwith a newsfilter process. If it succeeds, information on each article will Xbe passed to the newsfilter using a protocol described in this document; Xthe newsfilter will pass back an `interest score' which the reader may use Xto determine whether and how the article should be presented. X.PP XThe news sources distribution provides C libraries for both the `top' (reader) Xend, and the `bottom' (filter) end. X.SH THEORY OF OPERATION XProtocol execution may be thought of a series of \fRtransactions\fR; in each Xtransaction, the newsreader sends command down to the newsfilter, and Xblocks waiting a response. Optionally, the reader may time out if a response Xis not received within some maximum time. X.PP XSome transactions group into \fIdialogues\fR; these are logical sequences Xof transactions which share state (i.e. affect shared data in the protocol Xservice routines). X.PP XA protocol session consists of a \fIstart dialogue\fR, followed by any Xnumber of \fIcommand dialogues\fR, terminated by an \fIend dialogue\fR. XThere are presently two kinds of command dialogue; X\fInewsgroup dialogues\fR and \fIarticle dialogues\fR. Specifications for Xeach of these are given below. X.SH COMMAND AND RESPONSE FORMAT XAll messages start with a fixed-size \fIheader\fR. Some messages Xmay be followed by an \fIargument list\fR, the length of which is provided as Xpart of the header. In a few cases, the argument list may be followed by an Xadditional variable-length \fItext section\fR. X.PP XHere is the format of a header: X.nf X 0 Type: `C' = call, `R' = response, `Q' = query, `A' = answer X 1 A command code letter. X 2 A space ` ' X 3-8 A 6-digit decimal command sequence number. X 9 A space ` ' X 10-12 A 3-digit decimal argument list length. X 13 A terminating 0 (NUL) or newline byte. X.fi X.PP XIn a call, the sequence number is 1 for the first command issued and Xincreases by one in each following command. XIn a response, the sequence number field is the number of the command to which Xthe response pertains. The newsreader is not required to provide meaningful Xsequence numbers, but whatever number the newsreader sends will be used in Xall filter responses to that command. X.PP XIf a numeric field is smaller than the full field width, Xit should be either left justified and Xspace-filled to the right or 0 filled from the left. The latter form is Xrecommended and is the one shown in this document's examples. X.PP XThe \fIargument list\fR (if any) of a message is interpreted as a Xsequence of NUL-separated character strings. The Xlength field in the header must count the the terminating zero byte found at Xthe end of the last argument. The length of Xthe argument list is limited to 256 characters counting the NUL bytes. X.P XThe \fItext section\fR (in the non-implemented pipe mode) may consist of Xeither (a) an RFC-822 message Xheader followed and terminated by two carriage returns (a blank line), or b) XASCII data. Both are presented in a special packet format described below. X.SH STARTUP XWhile it is possible that this protocol may be implemented using other Xforms of inter process communication, this first version of the protocol Xis expected to be implemented using two nameless pipes. News filter Xprograms will take their commands from the master newsreading program Xby reading from the standard input. They will give their responses and Xqueries to the standard output. X.PP XA typical newsreader will fork a child news filter process, create pipes Xto talk to the standard input of the child and read from the standard Xoutput of the child, and then execute a news filter executable program. X.PP XWhile a newsreader may use any means to decide the location of the Xnews filter program it executes, the standard name is X.B nclip. XThe nclip program should be found in the same directory the newsreader Xuses to keep user files, such as the X.N .newsrc Xfile. The newsreader may also search the directories listed in the Xuser's PATH environment variable for this executable. X.PP XWhen the filter program is executed, it should be executed with the Xfollowing argument: X.TP 0 Xmode=pipe X.PP XOptionally, if the newsreader has a directory in which it places Xuser files, it should pass the name of that directory in a second argument Xof the form: X.TP 0 Xdot=<dirname> X.SS INITIAL SEQUENCE XWhen a news filter starts up operation, it should immediately send Xa ``response'' with the OK message (see below) to its standard output. This Xis not a response to any command, just a response to being executed. XThis will indicate that the news filter program has started correctly. X.PP XNewsreading programs which start up a news filter and do not get Xthis immediate initial response should assume the filter has failed Xto start, and act as though it is not present. X.PP XIf the OK message is detected, the newsreader should then send a XVersion command to the news filter and await a Version response. If Xall goes well, general operation may then continue. X.PP XNews filter programs should be sure to handle signals properly. For Xexample, a filter program should probably ignore INT (break) signals, as Xit is not talking to a terminal but will still be in the newsreader's Xprocess group (on Unix). X.PP XAt the end of a session, the newsreader should send the Quit command Xto the filter, await an OK, and then terminate or go on with the knowledge Xthe news filter program is not in operation. X X.SH DIALOGUE SPECIFICATIONS XIn the following specifications, the form of a message is given Xas a pseudo-BNF listing the code byte of the header and the meaning of the Xarguments following. Required and computed format elements such as the leading Xtype byte, the embedded spaces, the sequence number, the argument list length Xfield, and various NUL separators should be understood from the request format Xdescription above. X.PP XEach command or response specification is followed by an example. The first Xline of each example shows a sample header of the given type, and the second Xline an argument list. X.SS The `Start' Dialogue XThe `start dialogue' consists of a single transaction. The reader sends a X`V' (Version) command and expects a `V' (Version) response. These are defined Xas follows: X.TP 0 XCOMMAND: V <version-string> X.TP 0 X CV 000001 005\\0 X V100\\0 X.PP XThis command can be sent to a newsfilter to establish the newsfilter Xlanguage protocol understood by both programs. The newsfilter Xprogram will respond with a version line of its own, including Xthe list of valid commands it understands and the list of responses it Xcan give back. The newsreader should only send those commands in Xthe list given; others will produce an `error' response. All newsfilters must Xaccept the set of commands listed in this document -- the specification for XV100 of the newsfilter interface language. X.TP 0 XRESPONSE: V <version> <commands> <responses> <plang> <pversion> X.TP 0 X RV 000001 029\\0 X V100\\0ABHNPQV\\0ABEHORV\\0newsclip\\0100\\0 X.PP XThe <vnum> argument is a Version number for the command Xlanguage understood by the newsfiltering program. This language is Xversion V100. Later releases will have a higher number. X.PP XThe <commands> arg is the set of command codes the newsfilter understands. XThe <responses> arg is the set of responses that it knows to send back. X.PP XThe <plang> argument is the name of the filter language ('P' commands) that Xthe newsfilter understands, and the <pversion> number is a version number. XIf the newsfilter does not understand any language for P commands, it should Xuse the name `NULL' and a version number of 0. X.PP XIf the newsreader gets an `error' response to this message, it should assume Xthat the filter is present but cannot handle the language specified, or has Xfailed to initialize properly. The reader may attempt different Version Xcommands, or may decide to send a Quit command. X.PP XDefined names currently are: X NULL X newsclip X rnkill X.PP XNames can be registered via email to newsfilters@looking.on.ca X.SS The `End' Dialogue XThe \fIend dialogue\fR consists of a single transaction; the reader sends Xa `Q' (Quit) command and expects an `O' (Ok) response. X.TP XCOMMAND: Q X.TP X CQ 000236 000\\0 X.PP XThe newsfilter program should terminate. An response of `Ok' is Xexpected, after which the pipes will close. X.PP XClosing the command pipe, causing EOF for the newsfilter, should Xalso cause the newsfilter to terminate. If a newsreader detects EOF Xon the answer pipe, it should assume the newsfilter has terminated and Xact accordingly, possibly giving an error message to the user. X.TP 0 XRESPONSE: O X.TP 0 X RO 052317 000\\0 X.PP XThis response confirms that the newsfilter is exiting gracefully. X.SS The `Program' Dialogue X.PP XThe \fIprogram dialogue\fR begins with an 'P' (Program) command and ends with Xone of the responses 'O' (Ok) or `E' (Error). X.TP 0 XCOMMAND: P <command-string> X.TP 0 X CK 012321 016\\0 X kill From: Eric\\0 X.PP XThe first arg passes free format commands to the newsfilter. Normally these Xwill be things to add to the newsfilter's "kill files" or other such Xcommands. The format of the commands is entirely up to the newsfilter. X.P XThe above command might be request to kill all articles that include the string X"Eric" in their From line. It would be generated by a newsreader that Xknew how to translate user requests into commands to this particular Xnewsfilter. X.PP XThe newsfilter should interpret the command. If it is a valid command, it Xshould execute it and issue an OK response. If it is not a valid command, Xit should issue an Error response. The newsreader may decide to issue Xan error message because of the error response, or do further Xanalysis of the command. X.TP 0 XRESPONSE: O X.TP 0 X RO 12321 000\\0 X.PP XThis response confirms that the argument was accepted as a valid command Xby the newsfilter program. X.TP 0 XRESPONSE: E <error-message> X.TP 0 X RE 12321 017\\0 X No such command!\\0 X.PP XThis response tells the reader the argument was rejected as an invalid command Xby the newsfilter program. X.SS The `Newsgroup' Dialogue XThe \fINewsgroup dialogue\fR consists of a single transaction; the reader sends Xan `N' (Newsgroup) command, and expects an Accept, Reject or Ok response. X.TP 0 XCOMMAND: N <newsgroup> X.TP 0 X CN 00005 015\\0 X news.groups\\0 X.PP XThis command asks the newsfilter for general information on Xa newsgroup. Responses can be `A' (Accept), which indicates that Xall articles in this group should be accepted without consulting the Xnewsfilter, `R' (Reject) which means that all articles should be rejected Xwithout consulting the newsfilter or `O' (Ok - Consult), which means that Xarticles should be fed to the newsfilter for examination. X.PP XNote that even in the case of an `A' or `R', articles in that group Xmay still be sent for examination. It is just less efficient to do so. X.TP 0 XRESPONSE: A <score> X.TP 0 X RA 000005 003\\0 X 22\\0 X.PP XThis response indicates that the article should be accepted. The single Xargument is an `interest score' computed by the newskiller; it may be omitted Xto indicate a value of 1. X.TP 0 XRESPONSE: R <score> X.TP 0 X RR 000005 003\\0 X -2\\0 X.PP XThis response indicates that the article should be rejected. The single Xargument is an `interest score' computed by the newskiller; it may be omitted Xto indicate a value of -1. X.PP XBy convention, the `R' response carries a zero or negative score and the `A' Xresponse a positive one. X.PP XThe `O' response is as documented for the `Q' (Quit) command above. X.SS The `Article' Dialogue XThe \fIarticle dialogue\fR begins with an 'A' (Article) command and ends with Xone of the responses 'A' (Accept) or `R' (Reject). There may be one or more Xtransactions in this dialogue. X.TP 0 XCOMMAND: A <newsgroup> <number> <mode> [<filename>] X.TP 0 X CA 000006 048\\0 X news.groups\\034\\0R\\0/usr/spool/news/news/groups/43\\0 X.PP XThe first two arguments are a normal newsgroup and article-number pair; if Xthe newsfilter Xcan deduce a final interest score from these, it will do so and return accept Xor reject immediately. Otherwise, the newsfilter can return article information Xrequests to see portions of the article; see the following description of Xarticle information exchange. The <filename> argument, if present, is used Xin resolving the article information request. The <mode> argument Xindicates whether the article is present in the file, or must be Xrequested. X.PP XText may be passed down to the newsfilter in one of two modes; \fIpipe mode\fR Xor \fIfile mode\fR. The mode is triggered by the signle character <mode> Xbyte argument. File modes require a file name Xname argument on the reader command that triggered the article information Xrequest. For pipe mode, no file name is given and a mode character of X'P' is provided. The two file modes use mode characters of X'F' (full) and 'R' (request). X.PP XPipe mode is currently not Ximplemented in any of the news filtering or newsreading programs using Xthis protocol. It is defined for future expansion. X.PP XIn \fIpipe mode\fR article portions are passed down in the text sections of XHeader and Body replies from the newsreader in accordance with text query Xsequences started by the newsfilter. Text query sequence protocol is specified Xbelow. X.PP XIn \fIfile mode\fR the article information is passed in the file named. XThis file may be either the permanent location of the article, or a tempfile. X.PP XIn the former case, it is likely (but not necessary) that the 'F' (full) Xfile mode will be used. In this case, the entire article is already present Xin the file, and no further queries should be issued by the newsfilter. XThe only acceptable responses to a full file mode Article command are XAccept, Reject and Error. X.PP XIn the latter case of request mode, the newsfilter should issue text queries Xbefore attempting Xto read first the header, and later the body of the article. After Xissuing such queries, the filter should wait for a response from the Xnewsreader before reading into the file. X.PP XA text query sequence consists of a series of 'H' (Header) and 'B' (Body) Xrequests sent by the newsfilter to the newsreader, and corresponding responses Xby the newsreader. X.TP 0 XQUERY: H X.TP 0 X QH 000340 000\\0 X.PP XThis query requests the RFC-822 header of the article selected by a previous X`A' command, or of the article selected by the newsreader at the time of Xissuance of a 'P' command. X.TP 0 XANSWER: H [<size> [<asize>]] X.TP 0 X AH 000340 004\\0 X 364\\0 X.PP XThis answer signifies that the newsreader has header data ready in response Xto a previous 'H' query. The optional <size> argument specifies the total Xlength of the header. The body of the article may exist beyond the header. XFilters should not assume they will read EOF at the end of a header. X.PP XIn pipe mode, this answer is followed immediately by a text section in RFC-822 Xformat (see above), using message packet format. XMessage packets consist of a length byte, followed Xby 0 to 255 text data bytes. A 0 length packet indicates the end of the Xheader, which must be preceded by a blank line. X.PP XThe optional <asize> is the size of the entire article, but only if the Xnewsreader has it handy. X.TP 0 XQUERY: B [<size>] X.TP 0 X QB 000341 004\\0 X 125\\0 X.PP XThis query asks the reader to send down all or a portion of the body of the Xcurrent article. The filter may optionally communicate the most it wants Xof the article with the <size> argument. If this is present, the newsreader Xneed not transmit or place more than <size> bytes of the body. The Xnewsreader is still always free to send the entire body -- this is merely Xan optimization. If the <size> argument is not present, the entire body Xmust be made available. X.TP 0 XANSWER: B [<size>] X.TP 0 X AB 000341 004\\0 X 125\\0 X.PP XThe 'B' response signifies that the newsreader has text data ready for the Xnewsfilter. The argument optionally gives the length of the data in bytes. This Xlength may be smaller or larger than the requested length. It will only Xbe smaller if the article body itself is smaller than the requested length. X.PP XNote that a newsreader can use request mode even if it always has Xcomplete article files ready for the filter. It should merely respond Xto queries immediately, doing nothing. The 'F' (full) mode simply allows Xa reader to be sure it will never receive queries. This allows very Xsimple reader implementations of this protocol. X.PP XNNTP readers and other readers that do not have access to single Xarticle files should use the request mode, building a temporary file Xfor the filter as requested. X.PP XIn pipe mode, this answer is followed immediately by a text section in Xmessage packet format. Message packets consist of a length byte, followed Xby 0 to 255 text data bytes. A 0 length packet indicates EOF. X.SH NOTES XThe protocol is designed for use over the most primitive IPC common on UNIX, Xa pair of nameless pipes. Some older UNIXes (V7 in particular) feature pipe Ximplementations that behave rather badly (as in, cause a lockup or sudden Xprocess death) if reads and writes are not carefully synchronized. Thus the Xrigid alternation of fixed-size with variable-size transmissions, and the Xcare in specifying lengths of variable parts in fixed-part blocks. X.PP XUnder a more forgiving IPC implementation (such as System V message queues), Xthe fixed and variable-length parts might be sent in one transmission; this Xis an implementation detail left up to service libraries. X.PP XThe all-ASCII format avoids potential alignment problems. X.PP XIs is expected that the protocol service libraries will automatically choose Xpipe or file mode for text query sequence, depending on whether the calling Xnewsreader browses a file hierarchy or talks to some sort of network daemon. X.PP XThe 'F' (full) file mode is the simplest mode, intended for use on Xsystems where the article files reside in normal format on the user's Xmachine. A newsreader can be adapted to this mode of operation with Xminimal changes. X.SH AUTHORS XThis protocol was developed by Brad Templeton and Eric Raymond. END_OF_FILE if test 18501 -ne `wc -c <'doc/filter.man'`; then echo shar: \"'doc/filter.man'\" unpacked with wrong size! fi # end of 'doc/filter.man' fi if test -f 'getdate.y' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'getdate.y'\" else echo shar: Extracting \"'getdate.y'\" \(12599 characters\) sed "s/^X//" >'getdate.y' <<'END_OF_FILE' X%token ID MONTH DAY MERIDIAN NUMBER UNIT MUNIT SUNIT ZONE DAYZONE AGO X%{ X /* Steven M. Bellovin (unc!smb) */ X /* Dept. of Computer Science */ X /* University of North Carolina at Chapel Hill */ X /* @(#)getdate.y 2.13 9/16/86 */ X X#include <sys/types.h> X#ifdef USG Xstruct timeb X{ X time_t time; X unsigned short millitm; X short timezone; X short dstflag; X}; X#else X#include <sys/timeb.h> X#endif X#include <ctype.h> X X#if defined(BSD4_2) || defined (BSD4_1C) X#include <sys/time.h> X#else /*sane*/ X#include <time.h> X#endif /*sane*/ X X#define NULL 0 X#define daysec (24L*60L*60L) X static int timeflag, zoneflag, dateflag, dayflag, relflag; X static time_t relsec, relmonth; X static int hh, mm, ss, merid, daylight; X static int dayord, dayreq; X static int month, day, year; X static int ourzone; X#define AM 1 X#define PM 2 X#define DAYLIGHT 1 X#define STANDARD 2 X#define MAYBE 3 X%} X X%% Xtimedate: /* empty */ X | timedate item; X Xitem: tspec = X {timeflag++;} X | zone = X {zoneflag++;} X | dtspec = X {dateflag++;} X | dyspec = X {dayflag++;} X | rspec = X {relflag++;} X | nspec; X Xnspec: NUMBER = X {if (timeflag && dateflag && !relflag) year = $1; X else {timeflag++;hh = $1/100;mm = $1%100;ss = 0;merid = 24;}}; X Xtspec: NUMBER MERIDIAN = X {hh = $1; mm = 0; ss = 0; merid = $2;} X | NUMBER ':' NUMBER = X {hh = $1; mm = $3; merid = 24;} X | NUMBER ':' NUMBER MERIDIAN = X {hh = $1; mm = $3; merid = $4;} X | NUMBER ':' NUMBER NUMBER = X {hh = $1; mm = $3; merid = 24; X daylight = STANDARD; ourzone = $4%100 + 60*$4/100;} X | NUMBER ':' NUMBER ':' NUMBER = X {hh = $1; mm = $3; ss = $5; merid = 24;} X | NUMBER ':' NUMBER ':' NUMBER MERIDIAN = X {hh = $1; mm = $3; ss = $5; merid = $6;} X | NUMBER ':' NUMBER ':' NUMBER NUMBER = X {hh = $1; mm = $3; ss = $5; merid = 24; X daylight = STANDARD; ourzone = $6%100 + 60*$6/100;}; X Xzone: ZONE = X {ourzone = $1; daylight = STANDARD;} X | DAYZONE = X {ourzone = $1; daylight = DAYLIGHT;}; X Xdyspec: DAY = X {dayord = 1; dayreq = $1;} X | DAY ',' = X {dayord = 1; dayreq = $1;} X | NUMBER DAY = X {dayord = $1; dayreq = $2;}; X Xdtspec: NUMBER '/' NUMBER = X {month = $1; day = $3;} X | NUMBER '/' NUMBER '/' NUMBER = X {month = $1; day = $3; year = $5;} X | MONTH NUMBER = X {month = $1; day = $2;} X | MONTH NUMBER ',' NUMBER = X {month = $1; day = $2; year = $4;} X | NUMBER MONTH = X {month = $2; day = $1;} X | NUMBER MONTH NUMBER = X {month = $2; day = $1; year = $3;}; X X Xrspec: NUMBER UNIT = X {relsec += 60L * $1 * $2;} X | NUMBER MUNIT = X {relmonth += $1 * $2;} X | NUMBER SUNIT = X {relsec += $1;} X | UNIT = X {relsec += 60L * $1;} X | MUNIT = X {relmonth += $1;} X | SUNIT = X {relsec++;} X | rspec AGO = X {relsec = -relsec; relmonth = -relmonth;}; X%% X Xstatic int mdays[12] = X {31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; X#define epoch 1970 X Xextern struct tm *localtime(); Xtime_t dateconv(mm, dd, yy, h, m, s, mer, zone, dayflag) Xint mm, dd, yy, h, m, s, mer, zone, dayflag; X{ X time_t tod, jdate; X register int i; X time_t timeconv(); X X if (yy < 0) yy = -yy; X if (yy < 100) yy += 1900; X mdays[1] = 28 + (yy%4 == 0 && (yy%100 != 0 || yy%400 == 0)); X if (yy < epoch || yy > 1999 || mm < 1 || mm > 12 || X dd < 1 || dd > mdays[--mm]) return (-1); X jdate = dd-1; X for (i=0; i<mm; i++) jdate += mdays[i]; X for (i = epoch; i < yy; i++) jdate += 365 + (i%4 == 0); X jdate *= daysec; X jdate += zone * 60L; X if ((tod = timeconv(h, m, s, mer)) < 0) return (-1); X jdate += tod; X if (dayflag==DAYLIGHT || (dayflag==MAYBE&&localtime(&jdate)->tm_isdst)) X jdate += -1*60*60; X return (jdate); X} X Xtime_t dayconv(ord, day, now) int ord, day; time_t now; X{ X register struct tm *loctime; X time_t tod; X time_t daylcorr(); X X tod = now; X loctime = localtime(&tod); X tod += daysec * ((day - loctime->tm_wday + 7) % 7); X tod += 7*daysec*(ord<=0?ord:ord-1); X return daylcorr(tod, now); X} X Xtime_t timeconv(hh, mm, ss, mer) register int hh, mm, ss, mer; X{ X if (mm < 0 || mm > 59 || ss < 0 || ss > 59) return (-1); X switch (mer) { X case AM: if (hh < 1 || hh > 12) return(-1); X return (60L * ((hh%12)*60L + mm)+ss); X case PM: if (hh < 1 || hh > 12) return(-1); X return (60L * ((hh%12 +12)*60L + mm)+ss); X case 24: if (hh < 0 || hh > 23) return (-1); X return (60L * (hh*60L + mm)+ss); X default: return (-1); X } X} Xtime_t monthadd(sdate, relmonth) time_t sdate, relmonth; X{ X struct tm *ltime; X time_t dateconv(); X time_t daylcorr(); X int mm, yy; X X if (relmonth == 0) return 0; X ltime = localtime(&sdate); X mm = 12*ltime->tm_year + ltime->tm_mon + relmonth; X yy = mm/12; X mm = mm%12 + 1; X return daylcorr(dateconv(mm, ltime->tm_mday, yy, ltime->tm_hour, X ltime->tm_min, ltime->tm_sec, 24, ourzone, MAYBE), sdate); X} X Xtime_t daylcorr(future, now) time_t future, now; X{ X int fdayl, nowdayl; X X nowdayl = (localtime(&now)->tm_hour+1) % 24; X fdayl = (localtime(&future)->tm_hour+1) % 24; X return (future-now) + 60L*60L*(nowdayl-fdayl); X} X Xstatic char *lptr; X Xyylex() X{ X extern int yylval; X int sign; X register char c; X register char *p; X char idbuf[20]; X int pcnt; X X for (;;) { X while (isspace(*lptr)) lptr++; X X if (isdigit(c = *lptr) || c == '-' || c == '+') { X if (c== '-' || c == '+') { X if (c=='-') sign = -1; X else sign = 1; X if (!isdigit(*++lptr)) { X /* yylval = sign; return (NUMBER); */ X return yylex(); /* skip the '-' sign */ X } X } else sign = 1; X yylval = 0; X while (isdigit(c = *lptr++)) yylval = 10*yylval + c - '0'; X yylval *= sign; X lptr--; X return (NUMBER); X X } else if (isalpha(c)) { X p = idbuf; X while (isalpha(c = *lptr++) || c=='.') X *p++ = c; X *p = '\0'; X lptr--; X return (lookup(idbuf)); X } X X else if (c == '(') { X pcnt = 0; X do { X c = *lptr++; X if (c == '\0') return(c); X else if (c == '(') pcnt++; X else if (c == ')') pcnt--; X } while (pcnt > 0); X } X X else return (*lptr++); X } X} X Xstruct table { X char *name; X int type, value; X}; X Xstruct table mdtab[] = { X {"January", MONTH, 1}, X {"February", MONTH, 2}, X {"March", MONTH, 3}, X {"April", MONTH, 4}, X {"May", MONTH, 5}, X {"June", MONTH, 6}, X {"July", MONTH, 7}, X {"August", MONTH, 8}, X {"September", MONTH, 9}, X {"Sept", MONTH, 9}, X {"October", MONTH, 10}, X {"November", MONTH, 11}, X {"December", MONTH, 12}, X X {"Sunday", DAY, 0}, X {"Monday", DAY, 1}, X {"Tuesday", DAY, 2}, X {"Tues", DAY, 2}, X {"Wednesday", DAY, 3}, X {"Wednes", DAY, 3}, X {"Thursday", DAY, 4}, X {"Thur", DAY, 4}, X {"Thurs", DAY, 4}, X {"Friday", DAY, 5}, X {"Saturday", DAY, 6}, X {0, 0, 0}}; X X#define HRS *60 X#define HALFHR 30 Xstruct table mztab[] = { X {"a.m.", MERIDIAN, AM}, X {"am", MERIDIAN, AM}, X {"p.m.", MERIDIAN, PM}, X {"pm", MERIDIAN, PM}, X {"nst", ZONE, 3 HRS + HALFHR}, /* Newfoundland */ X {"n.s.t.", ZONE, 3 HRS + HALFHR}, X {"ast", ZONE, 4 HRS}, /* Atlantic */ X {"a.s.t.", ZONE, 4 HRS}, X {"adt", DAYZONE, 4 HRS}, X {"a.d.t.", DAYZONE, 4 HRS}, X {"est", ZONE, 5 HRS}, /* Eastern */ X {"e.s.t.", ZONE, 5 HRS}, X {"edt", DAYZONE, 5 HRS}, X {"e.d.t.", DAYZONE, 5 HRS}, X {"cst", ZONE, 6 HRS}, /* Central */ X {"c.s.t.", ZONE, 6 HRS}, X {"cdt", DAYZONE, 6 HRS}, X {"c.d.t.", DAYZONE, 6 HRS}, X {"mst", ZONE, 7 HRS}, /* Mountain */ X {"m.s.t.", ZONE, 7 HRS}, X {"mdt", DAYZONE, 7 HRS}, X {"m.d.t.", DAYZONE, 7 HRS}, X {"pst", ZONE, 8 HRS}, /* Pacific */ X {"p.s.t.", ZONE, 8 HRS}, X {"pdt", DAYZONE, 8 HRS}, X {"p.d.t.", DAYZONE, 8 HRS}, X {"yst", ZONE, 9 HRS}, /* Yukon */ X {"y.s.t.", ZONE, 9 HRS}, X {"ydt", DAYZONE, 9 HRS}, X {"y.d.t.", DAYZONE, 9 HRS}, X {"hst", ZONE, 10 HRS}, /* Hawaii */ X {"h.s.t.", ZONE, 10 HRS}, X {"hdt", DAYZONE, 10 HRS}, X {"h.d.t.", DAYZONE, 10 HRS}, X X {"gmt", ZONE, 0 HRS}, X {"g.m.t.", ZONE, 0 HRS}, X {"bst", DAYZONE, 0 HRS}, /* British Summer Time */ X {"b.s.t.", DAYZONE, 0 HRS}, X {"eet", ZONE, 0 HRS}, /* European Eastern Time */ X {"e.e.t.", ZONE, 0 HRS}, X {"eest", DAYZONE, 0 HRS}, /* European Eastern Summer Time */ X {"e.e.s.t.", DAYZONE, 0 HRS}, X {"met", ZONE, -1 HRS}, /* Middle European Time */ X {"m.e.t.", ZONE, -1 HRS}, X {"mest", DAYZONE, -1 HRS}, /* Middle European Summer Time */ X {"m.e.s.t.", DAYZONE, -1 HRS}, X {"wet", ZONE, -2 HRS }, /* Western European Time */ X {"w.e.t.", ZONE, -2 HRS }, X {"west", DAYZONE, -2 HRS}, /* Western European Summer Time */ X {"w.e.s.t.", DAYZONE, -2 HRS}, X X {"jst", ZONE, -9 HRS}, /* Japan Standard Time */ X {"j.s.t.", ZONE, -9 HRS}, /* Japan Standard Time */ X /* No daylight savings time */ X X {"aest", ZONE, -10 HRS}, /* Australian Eastern Time */ X {"a.e.s.t.", ZONE, -10 HRS}, X {"aesst", DAYZONE, -10 HRS}, /* Australian Eastern Summer Time */ X {"a.e.s.s.t.", DAYZONE, -10 HRS}, X {"acst", ZONE, -(9 HRS + HALFHR)}, /* Australian Central Time */ X {"a.c.s.t.", ZONE, -(9 HRS + HALFHR)}, X {"acsst", DAYZONE, -(9 HRS + HALFHR)}, /* Australian Central Summer */ X {"a.c.s.s.t.", DAYZONE, -(9 HRS + HALFHR)}, X {"awst", ZONE, -8 HRS}, /* Australian Western Time */ X {"a.w.s.t.", ZONE, -8 HRS}, /* (no daylight time there, I'm told */ X {0, 0, 0}}; X Xstruct table unittb[] = { X {"year", MUNIT, 12}, X {"month", MUNIT, 1}, X {"fortnight", UNIT, 14*24*60}, X {"week", UNIT, 7*24*60}, X {"day", UNIT, 1*24*60}, X {"hour", UNIT, 60}, X {"minute", UNIT, 1}, X {"min", UNIT, 1}, X {"second", SUNIT, 1}, X {"sec", SUNIT, 1}, X {0, 0, 0}}; X Xstruct table othertb[] = { X {"tomorrow", UNIT, 1*24*60}, X {"yesterday", UNIT, -1*24*60}, X {"today", UNIT, 0}, X {"now", UNIT, 0}, X {"last", NUMBER, -1}, X {"this", UNIT, 0}, X {"next", NUMBER, 2}, X {"first", NUMBER, 1}, X /* {"second", NUMBER, 2}, */ X {"third", NUMBER, 3}, X {"fourth", NUMBER, 4}, X {"fifth", NUMBER, 5}, X {"sixth", NUMBER, 6}, X {"seventh", NUMBER, 7}, X {"eigth", NUMBER, 8}, X {"ninth", NUMBER, 9}, X {"tenth", NUMBER, 10}, X {"eleventh", NUMBER, 11}, X {"twelfth", NUMBER, 12}, X {"ago", AGO, 1}, X {0, 0, 0}}; X Xstruct table milzone[] = { X {"a", ZONE, 1 HRS}, X {"b", ZONE, 2 HRS}, X {"c", ZONE, 3 HRS}, X {"d", ZONE, 4 HRS}, X {"e", ZONE, 5 HRS}, X {"f", ZONE, 6 HRS}, X {"g", ZONE, 7 HRS}, X {"h", ZONE, 8 HRS}, X {"i", ZONE, 9 HRS}, X {"k", ZONE, 10 HRS}, X {"l", ZONE, 11 HRS}, X {"m", ZONE, 12 HRS}, X {"n", ZONE, -1 HRS}, X {"o", ZONE, -2 HRS}, X {"p", ZONE, -3 HRS}, X {"q", ZONE, -4 HRS}, X {"r", ZONE, -5 HRS}, X {"s", ZONE, -6 HRS}, X {"t", ZONE, -7 HRS}, X {"u", ZONE, -8 HRS}, X {"v", ZONE, -9 HRS}, X {"w", ZONE, -10 HRS}, X {"x", ZONE, -11 HRS}, X {"y", ZONE, -12 HRS}, X {"z", ZONE, 0 HRS}, X {0, 0, 0}}; X Xlookup(id) char *id; X{ X#define gotit (yylval=i->value, i->type) X#define getid for(j=idvar, k=id; *j++ = *k++; ) X X char idvar[20]; X register char *j, *k; X register struct table *i; X int abbrev; X X getid; X if (strlen(idvar) == 3) abbrev = 1; X else if (strlen(idvar) == 4 && idvar[3] == '.') { X abbrev = 1; X idvar[3] = '\0'; X } X else abbrev = 0; X X if (islower(*idvar)) *idvar = toupper(*idvar); X X for (i = mdtab; i->name; i++) { X k = idvar; X for (j = i->name; *j++ == *k++;) { X if (abbrev && j==i->name+3) return gotit; X if (j[-1] == 0) return gotit; X } X } X X getid; X for (i = mztab; i->name; i++) X if (strcmp(i->name, idvar) == 0) return gotit; X X for (j = idvar; *j; j++) X if (isupper(*j)) *j = tolower(*j); X for (i=mztab; i->name; i++) X if (strcmp(i->name, idvar) == 0) return gotit; X X getid; X for (i=unittb; i->name; i++) X if (strcmp(i->name, idvar) == 0) return gotit; X X if (idvar[strlen(idvar)-1] == 's') X idvar[strlen(idvar)-1] = '\0'; X for (i=unittb; i->name; i++) X if (strcmp(i->name, idvar) == 0) return gotit; X X getid; X for (i = othertb; i->name; i++) X if (strcmp(i->name, idvar) == 0) return gotit; X X getid; X if (strlen(idvar) == 1 && isalpha(*idvar)) { X if (isupper(*idvar)) *idvar = tolower(*idvar); X for (i = milzone; i->name; i++) X if (strcmp(i->name, idvar) == 0) return gotit; X } X X return(ID); X} X Xtime_t getdate(p) char *p; X{ X#define mcheck(f) if (f>1) err++ X time_t monthadd(); X int err; X struct tm *lt; X struct timeb ftz; X extern long time_now; X extern unsigned int zone_offset; /* in minutes */ X X time_t sdate, tod; X X lptr = p; X lt = localtime(&time_now); X year = lt->tm_year; X month = lt->tm_mon+1; X day = lt->tm_mday; X relsec = 0; relmonth = 0; X timeflag=zoneflag=dateflag=dayflag=relflag=0; X ourzone = zone_offset; X daylight = MAYBE; X hh = mm = ss = 0; X merid = 24; X X if (err = yyparse()) return (-1); X X mcheck(timeflag); X mcheck(zoneflag); X mcheck(dateflag); X mcheck(dayflag); X X if (err) return (-1); X if (dateflag || timeflag || dayflag) { X sdate = dateconv(month,day,year,hh,mm,ss,merid,ourzone,daylight); X if (sdate < 0) return -1; X } X else { X sdate = time_now; X if (relflag == 0) X sdate -= (lt->tm_sec + lt->tm_min*60 + X lt->tm_hour*(60L*60L)); X } X X sdate += relsec; X sdate += monthadd(sdate, relmonth); X X if (dayflag && !dateflag) { X tod = dayconv(dayord, dayreq, sdate); X sdate += tod; X } X X return sdate; X} X Xyyerror(s) char *s; X{} END_OF_FILE if test 12599 -ne `wc -c <'getdate.y'`; then echo shar: \"'getdate.y'\" unpacked with wrong size! fi # end of 'getdate.y' fi if test -f 'nrc.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'nrc.c'\" else echo shar: Extracting \"'nrc.c'\" \(16971 characters\) sed "s/^X//" >'nrc.c' <<'END_OF_FILE' X X X#include "nl.h" X X/* X * These are the routines that handle the .newsrc file and all the X * activities related to using it, including reading the active file and X * the .nglas file. X */ X X /* X * Newsclip(TM) Library Source Code. X * Copyright 1989 Looking Glass Software Limited. All Rights Reserved. X * Unless otherwise licenced, the only authorized use of this source X * code is compilation into a binary of the newsclip library for the X * use of licenced Newsclip customers. Minor source code modifications X * are allowed. X * Use of this code for a short term evaluation of the product, as defined X * in the associated file, 'Licence', is permitted. X */ X X X Xnewsgroup main_newsgroup = NULL_NEWSGROUP;/* the group we are looping through */ Xint article_number; /* number of the current article */ Xextern dbptr ng_base; /* the database of known newsgroup names */ X Xextern int group_count; /* count of newsgroups in database */ X X X/* X * General initialization of newsgroup table X */ X X X/* First the routine to read the 'las' file */ X Xread_las() X{ X X FILE *howfile; X char buf[MAX_NGLEN+20]; /* buffer for line */ X char ngname[MAX_NGLEN]; X char formats[10]; X long maxnum; X ngrec *group; X extern char *lasname; /* name of las file */ X extern bool newsrc_allread; /* mark all newsrc as read */ X X /* First read the "how far" file that notes the last article X * seen by this program for each newsgroup in the .newsrc file. X */ X X sprintf( formats, "%%%ds %%lu\n", MAX_NGLEN ); X X howfile = fopen( lasname, "r" ); X X if( !howfile ) { X warning( 4, "LAS file %s missing\n", lasname ); X return; X } X X while( fgets( buf, sizeof(buf), howfile ) ) { X maxnum = 0; X /* the maxnum entry might not be read if the last article X seen number is not present */ X sscanf( buf, formats, ngname, &maxnum ); X group = (ngrec *)add_rec( ng_base, ngname, AR_CREATE ); X if( group->ngnumber == 0 ) X group->ngnumber = group_count++; X group->las = (int32)maxnum; X group->gflag |= GF_LASGROUP; X } X X fclose( howfile ); X X} X X Xngrec *rc_chain; /* start of chain of groups from .newsrc */ X X Xread_newsrc() X{ X FILE *rcfile; /* stream of the newsrc file */ X char buf[MAX_LLEN]; /* line buffer */ X char *subchar; /* position of colon in line */ X ngrec *group; /* record for this group */ X ngrec *lastgroup; /* record for previous group */ X int len; /* length of seen list */ X char *bslist; /* seen list in buffer */ X char *slist; /* final seen list */ X extern bool only_las; /* only do groups in LAS file */ X extern bool allow_unsub; /* allow unsubscribed groups? */ X bool unsub; /* subscribed? */ X extern char *newsrcname; /* newsrc file */ X X X /* Lock access to the .newsrc? */ X X rcfile = mustopen( newsrcname, "r", ".newsrc file" ); X lastgroup = (ngrec *)0; X X while( fgets( buf, sizeof(buf), rcfile ) ) { X /* skip option lines */ X if( strncmp( buf, "options ", 8 ) == 0 || isspace(buf[0]) || X buf[0] == '<' ) X continue; X unsub = FALSE; X subchar = strchr( buf, ':' ); X if( subchar == NULL ) { X if( allow_unsub ) { X subchar = strchr( buf, '!' ); X unsub = subchar != NULL; X } X if( subchar == NULL ) X continue; /* not a newsgroup line */ X } X *subchar = 0; /* terminate group name */ X if( only_las ) { X group = (ngrec *)get_rec(ng_base, buf); X if( !group || !(group->gflag & GF_LASGROUP ) ) X continue; X } X else X group = (ngrec *)add_rec( ng_base, buf, AR_CREATE ); X if( group->ngnumber == 0 ) X group->ngnumber = group_count++; X group->gflag |= GF_RCGROUP; /* group from .newsrc*/ X X /* build the RC order chain */ X if( lastgroup ) X lastgroup->chain = group->ngnumber; X else X rc_chain = group; X lastgroup = group; X if( unsub ) X group->gflag |= GF_UNSUB; X /* now add the line from the .newsrc */ X X bslist = whitestrip( subchar+1 ); X X len = strlen( bslist ); X if( bslist[len-1] != '\n' ) { X long seekback; X int extras; X char c; X /* the line was too long for the buffer! */ X /* find out how long it really is */ X seekback = ftell( rcfile ); X for( extras = 0; (c = getc(rcfile)) != EOF && c != '\n'; X extras++ ) X ; X /* go back and read it in for real */ X fseek( rcfile, seekback, 0 ); X slist = perm_alloc( len + extras + 1+1 + SLIST_EXTRAS ); X strcpy( slist, bslist ); X fgets( slist+len, extras, rcfile ); X len += extras + 1; X if( slist[len-1] == '\n' ) X slist[--len] = 0; X } X else { X slist = perm_alloc( len + 1 + SLIST_EXTRAS ); X strcpy( slist, bslist ); X slist[--len] = 0; X } X group->seenlist = slist; X group->slistlen = len + SLIST_EXTRAS; X } X fclose( rcfile ); /* for now */ X X} X X X#define set_bit( map, i ) map[i>>3] |= 1 << (i&7) X#define is_bit_set( map, i ) ((map[i>>3] >> (i&7)) & 1) X#define clear_bit( map, i ) map[i>>3] &= ~(1 << (i&7)) X X/* X * Translate the SEEN list of a newsgroup to a bitmap X * Bitmap maximum size 32767 bits or 4K bytes with 16 bit int -- no problem. X */ X Xint Xseen_to_bitmap( group, bitmap, bmsize, num_unseen ) Xngrec *group; /* newsgroup, with seen article list */ Xchar *bitmap; Xint bmsize; /* max size of bitmap */ Xint *num_unseen; /* return number of unread articles */ X{ X int bsize; X char *p; /* pointer into list */ X int num_new_seen; X X X bsize = group->highest - group->lowest + 1; X if( bsize < 0 || (bsize+7)/8 > bmsize ) X return -1; X X /* clear out the bitmap first */ X zero( bitmap, bmsize ); X num_new_seen = 0; X X p = group->seenlist; X X while( *p ) { X int32 first,last; X X first = atoi32(p); X while( isdigit(*p) ) X p++; X if( *p == '-' ) { X last = atoi32(++p); X /* make sure last is within bounds of group */ X last = min( last, group->highest ); X while( isdigit(*p) ) X p++; X } X else X last = first; X if( last >= group->lowest && last >= first ) { X int topbit, i; X /* move first up to within group */ X first = max( first, group->lowest ); X topbit = last - group->lowest; X /* set all the bits for read articles */ X for( i = first - group->lowest; i <= topbit; i++ ) { X set_bit( bitmap, i ); X if( i + group->lowest > group->las ) X num_new_seen++; X } X } X if( *p == ',' ) X p++; X } X if( num_unseen ) X *num_unseen = group->highest - group->las - num_new_seen; X return bsize; X} X X/* scan through the newsrc for unread articles and process them */ X Xdo_newsrc() X{ X ngrec *group; X char bitmap[BITMAP_SIZE]; X char newsfile[MAX_FNAME]; X int bsize; /* number of bits in bitmap */ X int art; /* article number (less lowest)*/ X int dirty; /* was this article marked read? */ X extern char *news_spool_dir; X extern bool list_always; /* always list articles */ X int num_unread; /* number of unread articles */ X extern bool newsrc_allread; /* mark all articles read */ X bool foundone; /* we liked an article here */ X extern int reading_mode; X X reading_mode = FILE_FULL; X X for( group = rc_chain; group; group = ngarray[group->chain] ) { X X /* test if group was found in active file */ X if( !( group->gflag & GF_ACTIVE ) ) X continue; X X /* adjust last article seen */ X X group->las = max( group->lowest-1, group->las ); X X if( group->las >= group->highest ) X continue; /* all messages already done*/ X dirty = FALSE; X foundone = FALSE; X X bsize = seen_to_bitmap( group, bitmap, sizeof(bitmap), X &num_unread ); X if( bsize < 0 ) { X warning( 1,"Invalid Active file line for %s\n", X group->key); X continue; /* error on this group */ X } X X /* if no unread articles, don't bother, but update las */ X if( num_unread <= 0 ) { X group->las = group->highest; X continue; X } X X main_newsgroup = group->ngnumber; X X Ustartgroup( num_unread ); X X /* Here is the actual loop were we call the user code to X accept or reject the acticle */ X X X for( art = group->las - group->lowest+1 ; art < bsize; art++ ) { X if( !is_bit_set( bitmap, art ) ) { X /* an unread article! */ X reset_tempalloc(); X sprintf( newsfile, "%s/%s/%lu", news_spool_dir, X ngdir(group->key), X (long)(art + group->lowest) ); X article_number=makeint((long)art+group->lowest); X main_newsgroup = group->ngnumber; X if( !accept_article( newsfile ) ) { X extern array *xref; X set_bit( bitmap, art ); X dirty = TRUE; X if( xref && xref->arsize > 2 ) X kill_xrefs( main_newsgroup ); X } X else { X /* in list mode, assure we don't X list cross postings */ X if( list_always ) { X extern array *xref; X printf( "%s\n", newsfile ); X if( xref && xref->arsize > 2 ) X kill_xrefs( X main_newsgroup); X } X /* mark it read anyway in batch mode */ X if( newsrc_allread ) { X set_bit( bitmap, art ); X dirty = TRUE; X } X foundone = TRUE; X } X } X } X /* bitmap processed */ X group->las = group->highest; X /* now turn bitmap into seen list again */ X if( dirty ) { X bitmap_to_seen( bitmap, group, bsize ); X group->gflag |= GF_DIRTY; X } X if( foundone ) X /* turn off unsub it if it was on, resubscribe group */ X group->gflag &= ~GF_UNSUB; X finish_group( ); X } X X X} X X#define digits(n) (n >= 10000 ? ( n >= 100000 ? 6 : 5 ) : n >= 100 ? (n >= 1000 ? 4 : 3) : ( n < 10 ? 1 : 2 ) ) X X/* convert a bitmap to a seen list, return pointer to that. X * (A 'seen list' is the string of numbers found in a .newsrc entry. X */ X X Xbitmap_to_seen( bitmap, group, numbits ) Xchar *bitmap; /* the bitmap itself */ Xngrec *group; /* group record */ Xint numbits; /* the number of bits in the bitmap */ X{ X int i; X int size; /* size of output string */ X bool oldstat; /* status of old bit */ X int32 oldnum; /* start of stream of read articles */ X bool bit; /* bit in bitmap */ X char *p; /* pointer storing slist */ X char *slist; /*new seen list */ X int32 lowest; /* lowest article in group */ X X X oldnum = 1; X size = 0; X /* clear the bit off the end */ X clear_bit( bitmap, numbits ); X X lowest = group->lowest; X X /* set stat of 'previous' bit */ X oldstat = lowest > 1; X for( i = 0; i <= numbits; i++ ) { X bit = is_bit_set( bitmap, i ); X if( bit != oldstat ) { X oldstat = bit; X if( bit ) X oldnum = i + lowest; X else { X size += 1 + digits(oldnum); X if( i + lowest > oldnum+1 ) X size += 1 + digits( i + lowest-1 ); X } X } X } X if( size >= group->slistlen ) { X perm_free( group->seenlist ); X group->seenlist = perm_alloc( size+SLIST_EXTRAS+1 ); X group->slistlen = size+SLIST_EXTRAS; X } X X /* now repeat the procedure, storing this time */ X /* set stat of 'previous' bit */ X X p = group->seenlist; X oldnum = 1; X X oldstat = lowest > 1; X for( i = 0; i <= numbits; i++ ) { X bit = is_bit_set( bitmap, i ); X if( bit != oldstat ) { X oldstat = bit; X if( bit ) X oldnum = i + lowest; X else { X sprintf( p, "%lu", (long)oldnum ); X p += digits(oldnum); X if( i + lowest > oldnum+1 ) { X sprintf(p,"-%lu",(long)i+lowest-1); X p += strlen(p); X } X *p++ = ','; X } X } X /* sanity check */ X if( p - group->seenlist >= group->slistlen ) { X fprintf( stderr,"Seen list %d bytes too big, only %d\n", X p-group->seenlist, group->slistlen); X fprintf(stderr,"%s: %s\n", group->key, group->seenlist); X abort(); X } X } X if( p[-1] == ',' ) X p[-1] = 0; X else X p[0] = 0; X X} X X/* structure for subscribe list */ X Xstruct sublist { X char *gname; X struct sublist *next; X } *newgroups; X X/* subscribe to a group for the next time around - for newsrc modes only */ X Xsubscribe( grp ) Xchar *grp; X{ X ngrec *group; X struct sublist *sptr; X X /* first see if we know about it already */ X group = (ngrec *)get_rec( ng_base, grp ); X if( group && group->gflag & GF_RCGROUP ) { X /* turn off unsubscribed flag, if on */ X group->gflag &= ~GF_UNSUB; X group->gflag |= GF_DIRTY; X } X else { X /* list this group for later concat to .newsrc */ X X /* scan to see if already present */ X for( sptr = newgroups; sptr; sptr = sptr->next ) X if( strcmp( sptr->gname, grp ) == 0 ) X return; X sptr = (struct sublist *)perm_alloc( sizeof(struct sublist) ); X sptr->gname = allocstring(grp); X /* stick on front of list */ X sptr->next = newgroups; X if( newgroups == 0 ) X newgroups = sptr; X } X X} X X X X/* Write out a new .newsrc to the specified descriptor. Return true X if a write error, false if all is well. The descriptor must not X point to the real, old .newsrc which is used to contruct the new one. X */ X Xint Xwrite_newsrc( desc ) XFILE *desc; /* descriptor of output .newsrc */ X{ X FILE *oldrc; /* old .newsrc we replace */ X char rbuf[MAX_LLEN]; X char *subchar; X ngrec *group; X extern bool allow_unsub; /* allow unsubscribed groups */ X bool we; /* write error flag */ X extern char *strpbrk(); X extern char *newsrcname; X struct sublist *sptr; X X X oldrc = mustopen( newsrcname, "r", ".newsrc file for update" ); X we = FALSE; X X while( !we && fgets( rbuf, sizeof(rbuf), oldrc ) ) { X subchar = strpbrk( rbuf, ":! \t," ); X if( !isspace(rbuf[0]) && subchar && (*subchar == ':' || X (allow_unsub && *subchar == '!') ) ) { X char oldchar; X /* option lines have no colon or ! */ X oldchar = *subchar; X *subchar = 0; X group = (ngrec *)get_rec( ng_base, rbuf ); X if( group && group->gflag & GF_DIRTY ) { X we |= fprintf( desc, "%s%c %s\n", group->key, X group->gflag&GF_UNSUB ? '!':':', X group->seenlist ) < 0; X continue; X } X else X *subchar = oldchar; X } X /* was not modified, so just copy what's there */ X we |= fputs( rbuf, desc ) == EOF; X /* loop to copy extra parts of long lines */ X while( rbuf[strlen(rbuf)-1] != '\n' && fgets( rbuf, X sizeof(rbuf), oldrc ) ) X we |= fputs( rbuf, desc ) == EOF; X } X /* write out new groups to subscribe to */ X for( sptr = newgroups; sptr; sptr = sptr->next ) X we |= fprintf( desc, "%s:\n", sptr->gname ) < 0; X X fclose( oldrc ); X fclose( desc ); X return we; X} X X/* Output a new .nglas file */ X Xwrite_las() X{ X FILE *lasfile; X ngrec *group; X bool we; /* write error */ X extern char *lasname; /* name of las file */ X struct sublist *sptr; X X lasfile = fopen( lasname, "w" ); X X if( !lasfile ) { X warning( 1, "Could not open file %s\n", lasname ); X return; X } X X we = FALSE; X X for( group = rc_chain; group && !we; group = ngarray[group->chain] ) X we |= fprintf( lasfile, "%s %lu\n", group->key, X (long)group->las ) < 0; X /* write out new groups to subscribe to */ X for( sptr = newgroups; sptr; sptr = sptr->next ) X we |= fprintf( lasfile, "%s 0\n", sptr->gname ) < 0; X fclose( lasfile ); X if( we ) X warning( 1, "Unable to write file %s\n", lasname ); X X} X X X/* the master loop for .newsrc mode */ X X Xprocess_newsrc() X{ X FILE *nrdesc; /* newsrc descriptor */ X extern bool out_newsrc; /* do we write a .newsrc? */ X extern char *newsrcname; X extern char *temprc; /* temporary write location */ X extern bool newsrc_allread; /* newsrc to be marked all read? */ X X /* init the newsgroups list and read in the data */ X initngs(TRUE); X X Uinit(); X X /* go through the .newsrc and process the articles */ X do_newsrc(); X X Uterminate(); X X if( !out_newsrc ) X return; X X /* Now write out the .newsrc file */ X X nrdesc = mustopen( temprc, "w", "temporary .newsrc for update" ); X X if( !nrdesc || write_newsrc( nrdesc ) ) { X char pidbuf[40]; X X warning( 0, "Could not write out .newsrc\n" ); X sprintf( pidbuf, "/tmp/nr%d", getpid() ); X nrdesc = mustopen( pidbuf, "w", "emergency .newsrc" ); X if( !nrdesc || write_newsrc( nrdesc ) ) X warning( 0, "Attempt to write to %s failed\n", X pidbuf ); X else X warning( 0, "Copy written to %s\n", pidbuf ); X } X else { X /* release the old .newsrc */ X if( unlink( newsrcname ) ) X warning( 0, "Could not unlink old %s\n", newsrcname ); X else { X if( link( temprc, newsrcname ) ) X warning( 0, "Could not link in %s\n", temprc ); X else X unlink( temprc ); X } X write_las(); X } X X} X X/* Mark articles listed as cross references as read in their groups */ X Xkill_xrefs( mgroup ) Xnewsgroup mgroup; /* main newsgroup */ X{ X extern array *xref; /* array of xrefs */ X extern char *sitename; /* short name of system */ X int i; X char bitmap[BITMAP_SIZE]; X int num_unread; /* a dummy */ X int bsize; /* size of bitmap */ X int32 artbase; X X if( xref && cleq( xref->vals[0].ustring, sitename ) ) X for( i = 1; i < xref->arsize; i++ ) { X ngrec *group; X char *colon; X char *xrpat; X X xrpat = xref->vals[i].ustring; X colon = strchr( xrpat, ':' ); X if( colon ) { X *colon++ = 0; X group = (ngrec *)get_rec( ng_base, xrpat ); X /* if not a subscribed group or the current X group, then skip this one */ X if( group == 0 || group->ngnumber == mgroup || X !(group->gflag & GF_RCGROUP) ) X continue; X artbase = atol(colon) - group->lowest; X if( artbase < 0 ) X continue; X /* ok, we have an article to mark unread */ X bsize = seen_to_bitmap( group, bitmap, X sizeof(bitmap), &num_unread ); X /* if no change, keep going */ X if(artbase>bsize||is_bit_set(bitmap,artbase)) X continue; X /* set the bit */ X set_bit( bitmap, artbase ); X /* put back new bitmap */ X bitmap_to_seen( bitmap, group, bsize ); X group->gflag |= GF_DIRTY; X } X } X} END_OF_FILE if test 16971 -ne `wc -c <'nrc.c'`; then echo shar: \"'nrc.c'\" unpacked with wrong size! fi # end of 'nrc.c' fi echo shar: End of archive 8 \(of 15\). cp /dev/null ark8isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 15 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0