[comp.sources.misc] v09i084: newsclip 1.1, part 15 of 15

brad@looking.ON.CA (Brad Templeton) (12/20/89)

Posting-number: Volume 9, Issue 84
Submitted-by: brad@looking.ON.CA (Brad Templeton)
Archive-name: newsclip/part15

#! /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 15 (of 15)."
# Contents:  doc/man.mm.2
# Wrapped by allbery@uunet on Tue Dec 19 20:10:09 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'doc/man.mm.2' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'doc/man.mm.2'\"
else
echo shar: Extracting \"'doc/man.mm.2'\" \(53631 characters\)
sed "s/^X//" >'doc/man.mm.2' <<'END_OF_FILE'
XIf you know that ``foo'' is also fed by ``bar'' and ``baz'', you could
Xcreate a string array with those three names, or just say:
X.Bb
Xreject if "foo" in path || "bar" in path || "baz" in path;
X.Be
X.H 2 "Regular Expressions"
X.P
XAs noted above, the search strings used with the \fBhas\fP operator
Xare patterns from a language called ``regular expressions.''  The
Xbasics of this system are defined in the documentation for the
X\fBed\fP text editor, and the \fBgrep\fP and particularly \fBegrep\fP
Xfile searching programs.
X.P
XPatterns in NewsClip follow the patterns of \fBegrep\fP, which is to
Xsay they include the patterns of \fBed\fP, plus the following rules:
X.AL
X
X.LI
XThe special tag characters \\( and \\) are not supported.
X.LI
XA regular expression followed by a plus (\fB+\fP) matches one or more
Xof the regular expression.  (This is like \fB*\fP, except it matches 1
Xor more cases rather than 0 or more cases.)  Thus ``a+'' matches
X\fBa\fP, \fBaa\fP, \fBaaa\fP and so on.
X.LI
XA regular expression followed by a question mark (\fB?\fP) matches 0
Xor 1 occurrences of the regular expression, ie. the expression
Xis optional.
X.LI
XYou can search for two different regular expressions by putting an or
Xbar (\fB|\fP) between them.  Either the first or the second may match.
XBe warned that currently this is not very efficient.
X.LI
XYou can group patterns together with parentheses so that the scope of
Xthe above rules applies where you like.  ``abc(def|ghi)jkl'' matches
X``abcdefjkl'' or ``abcghijkl,'' for example.
X.LE
X
X.H 3 "Literal Patterns"
X.P
XClearly there are times when you don't want to use the regular expression
Xmetacharacters in a search string -- you wish to search for the exact
Xstring, even if it has the special characters in it.
X.P
XWhen you have a typed-in pattern string, you can do this easily by escaping
Xthe special characters with a backslash.  For example, \fB\\$\fP matches a
Xreal dollar sign.
X.P
XIf you get a string from an article and you wish to use it as a pattern,
Xyou should pass it through the \fBliteral\_pattern\fP function.  This
Xescapes all special characters for you, so that pattern matching on the
Xresulting string will only match strings that contain the exact text of
Xyour pattern string.  For example, \fBliteral\_pattern( "It costs $5.93" )\fP
Xwill be the string ``\fBIt costs \\$5\\.93\fP'' with the special characters
Xescaped.
X.P
XIt is important to do this if you are storing strings from an article
X(like the \fBsubject\fP) in a database for later searches using a database
Xof patterns.
X
X
X
X.H 1 "Databases"
X.P
XQuite often your news clipping decisions will be based on past history --
Xlists of users, topics or sites that you either like or don't like.
XOne particularly common desire is to ask to see or not see the followups
Xto a given article.
X.P
XTo keep track of old information, and in general to read information to
Xand from disk, NewsClip programs use the database type.
X.P
XA database is effectively an array of integer values that is indexed with
X\fIstrings\fP instead of numbers.  If you index into a database with a string,
Xyou'll get back the integer value for that index, or 0 if the string isn't
Xin the database at all.
X.P
XQuite often the integer value is unimportant,
Xand all you care about is whether or not a given string has an entry in the
Xdatabase or not.  In this case, one can use the \fBin\fP operator to find
Xout if a string, or any member from an array of strings, is ``in'' the
Xdatabase.
X.P
XLet's say you have a list of USENET posters whom you don't like.  You might
Xwant to give negative scores to all the articles they write.
X.Bb
X	extern userid from;
X	extern int score;
X	database badusers;
Xprocedure
Xinit() {
X	badusers = fresh\_database( 5 );
X	badusers["bad@foo.bar"] = -5;
X	badusers["worse@site.com"] = -10;
X	badusers["worst@ihate.edu"] = -20;
X}
Xprocedure
Xarticle() {
X	/* this adjusts score by 0 if not found */
X	adjust badusers[from];
X	/* and other code, of course */
X}
X.Be
XThe above scheme needn't be just a bad user database.  You could assign
Xpositive scores to good users, and they would be adjusted upwards using 
Xthis scheme.
X.P
XIf scores aren't your concern, you could also simply say
X\fBreject if from in badusers;\fP
X.P
XThe \fBfresh\_database\fP function creates an empty database,
Xin this case expected to hold an average of 5 indices.   The 5 is not
Xa hard and fast limit -- you could still have 100 or even 1000 indices, but
Xdatabase use would then get very slow.   The closer your guess is to the
Xreal number, the more optimal your use of the database will be in memory
Xspace and speed.
X
X.H 2 "Message-ids"
X.P
XNaturally, one thing you will want to keep a database of is article
Xmessage-ids.  The message-id is a string that is unique for every USENET
Xarticle posted.  Every followup has a line called \fBreferences\fP which
Xindicates the message-ids of the articles to which this is a followup.
X(If an article is a followup to a followup -- and there are far too many
Xof these -- there will be two message-ids in the \fBreferences\fP array.)
X.P
XLet's update our program to include programmed control of a \fBmessages\fP
Xdatabase.  If an article comes in from a user in our \fBbadusers\fP database,
Xwe will reject it, of course, but we'll also store its message-id in
Xour messages database.  Then we can reject the followups, too.
X.Bb
X	extern userid from;
X	extern string message\_id
X	extern string array references;
X	database badusers;
X	database messages;
Xprocedure
Xinit() {
X	badusers = fresh\_database( 5 );
X	messages = fresh\_database( 100 );
X	badusers["bad@foo.bar"] = true;
X	badusers["worse@site.com"] = true;
X	badusers["worst@ihate.edu"] = true;
X}
Xprocedure
Xarticle() {
X	extern int followup;
X	if( from in badusers ) {
X		messages[message\_id] = true;
X		reject;
X		}
X	reject if followup && references in messages;
X}
X.Be
X.P
XThis program has been set to simply reject based on the presence of a user
Xor ``parent'' (\fBreferences\fP) in the database.  With a little cleverness,
Xyou could arrange to use the score system, so that all followups get the
Xsame adjustment that their parent got when it was accepted or rejected.
X.P
X(We do mean cleverness, as the indexing feature doesn't work on an array
Xlike \fBreferences\fP, and you would have to write your own loop to find
Xthe first valid parent and its score.)
X.P
XNote that we don't try to use \fBreferences\fP until we know it is there
Xand defined.  The \fBfollowup\fP variable tells us this, although we
Xcould also have tested if \fBreferences != nilarray\fP.
X
X.H 2 "Disk Files"
X.P
XAll this is interesting, but not very useful if you can't have databases
Xremember things from session to session of news clipping.
X
X.H 3 "Reading in a Database"
X.P
XTo read a database from a file, use the \fBread\_database\fP function.
XYou provide it with the filename of the database.
X.P
XDatabase files have a particular format that includes the integer field
Xvalue, a date of last access/change and the string index.  You may, however,
Xcreate your own database files, or add lines to existing database files
Xquite easily.   Just add lines with nothing more than the index string.
X.P
X(You will read about the uses of the date of last access/change later.)
X.P
XIn fact, your first databases, unless they are created with newsclip programs,
Xwill probably be simple files where each line is a database index string.
XWhen you read in such lines, the integer value field will be set to 1,
Xand the date of last access set to the current time and date.  If you want
Xto create databases with integer values other than 1, see the reference
Xsection.
X.P
XIf you had a file \fB/tmp/baduser\fP that contained the lines:
X.Bb
Xbad@foo.bar
Xworse@site.com
Xworst@ihate.edu
X.Be
Xthen you could create our badusers database with:
X.Bb
Xbadusers = read\_database( "/tmp/baduser" );
X.Be
Xinstead of the \fBfresh\_database\fP call.
X.P
XIf you create a database in this way, you should later write it out
Xwith the \fBwrite\_database\fP procedure described below, so that
Xthe lines get proper dates and the database is written out in the
Xproper format.
X.P
XIf the database you try to read isn't present on the disk, you'll get
Xan empty database -- the same as if you had coded \fBfresh\_database(30)\fP.
X
X.H 3 "Writing the Database"
X.P
XTo write out a database, you say:
X.Bb
Xwrite\_database( base, filename, oldest );
X.Be
Xwhere \fIoldest\fP is a date value that represents the oldest record that
Xyou want written out.
X.P
XSome databases, most notably those of message-ids and subject lines,
Xwill contain elements that age with time.
XThere is no point in keeping an old message-id in the database when nobody
Xis referring to the original message any more.
X.P
XBy specifying a date argument, you can arrange for old, unused elements
Xof the database to ``expire,'' and not get written out.
X.P
XEvery time a database element is created, changed, or referenced with array
Xindexing or the \fBin\fP and \fBhas\fP operators, the access time for the
Xrecord is
Xupdated to the current time.  If you want to expire all elements that have
Xnot been used in a month, provide the date value for one month previous
Xto the current date when you write out the database.  For example,
X.Bb
Xextern datetime time\_now;
Xwrite\_database( messages, "/me/mymessages", time\_now - month );
X.Be
Xwill do the trick.  As you might guess, \fBtime\_now\fP contains the
Xcurrent date and time, and \fBmonth\fP is a special constant that
Xcontains the number of seconds (dates are measured in seconds) in one
Xmonth (approximately).
X.P
XIf you write out an empty or nil database and the database file doesn't
Xcurrently exist, it will not be created.
X.H 2 "Datbabase Filenames"
X.P
XYou may want to keep all sorts of databases.  In particular, you may
Xwant to keep databases on both the all-article or ``global'' level, along
Xwith a variety of small databases on a per-newsgroup level.
X.P
XIf your database needs are small, you may find that database lookup is
Xfast enough that you can keep all your database information in global
Xdatabases -- even if the information is stuff that's likely to only
Xoccur in one newsgroup like subject lines or message-ids.  If your needs
Xget complex, you may decide to maintain individual databases of such
Xthings for some, or even all, of your newsgroups.
X.P
XTo help you do this, certain escape sequences can be used in database
Xfilenames to let you read and write your database files to the right
Xplace in the filesystem.  All the escapes start with a tilde.
X.P
XThe most useful ones are a single tilde (\fB~\fP) which expands to your
Xhome directory, and \fB~n\fP, which expands to the name of the current
Xnewsgroup (\fBmain\_newsgroup\fP) with dots mapped to slashes, the way
Xthey are in the news spool directories.  RN keeps the kill file for
Xa group like ``rec.humor'' in \fB$HOME/News/rec/humor/KILL\fP.  You can
Xdo the same by using a filename like \fB~/News/~n/badsubjects\fP.  If
Xyour home is \fB/u/brad\fP and the newsgroup is ``rec.humor,'' this
Xexpands to \fB/u/brad/News/rec/humor/badsubjects\fP, which would be
Xyour bad subjects database for the newsgroup rec.humor, of course.
X.P
XOther escapes are possible.  They are defined in the reference section.
X
X.H 3 "Newsgroup Specific Databases"
X.P
XTo make use of such databases, you will want to use the \fBstartgroup\fP
Xand \fBendgroup\fP procedure ``entry points'' of a NewsClip program.
X.P
XAs you learned earlier on in the manual, NewsClip programs allow
Xa variety of entry point procedures, most of them optional.
XThese two can come in handy now.
X.P
XThe \fBstartgroup\fP procedure is executed whenever processing of a
Xnew newsgroup starts.  That new newsgroup will be stored in the
Xvariable \fBmain\_newsgroup\fP.   Here is where you can initialize
Xthings for the newsgroup -- such as reading in newsgroup specific databases.
X.P
XThe \fBendgroup\fP procedure is called when processing of a newsgroup is
Xfinished.
X.Bb
X	database groupkill;
X	extern string subject;
Xprocedure
Xstartgroup() {
X	groupkill = read\_database( "~/News/~n/subjects" );
X}
Xprocedure
Xendgroup() {
X	extern datetime time\_now;
X	write\_database( groupkill, "~/News/~n/subjects", time\_now - 3*week );
X	free\_database( groupkill );
X	groupkill = nildatabase;
X}	
Xprocedure
Xarticle() {
X	if( drop\_re( subject ) in groupkill )
X		reject;
X}
X.Be
X.P
XThis program maintains databases of subject lines that you don't want to
Xsee in each newsgroup.  There doesn't have to be a file for each newsgroup.
XIf the file is missing, an empty database will be created that matches
Xnothing, and no file will be written out at the end.
X.P
XYou will note that after writing out the database, expiring any items that
Xhave not been seen in 3 weeks, we called the \fBfree\_database\fP procedure.
XUnlike most items allocated in a NewsClip program, databases do not
Xstay in temporary memory -- they have to last from article to article.
X.P
XIf you're reading in a database for each group and getting rid of it when
Xthe group is done, you will want to free the memory it uses.  Whenever
Xyou free the memory used by a database, you should set the descriptor
Xthat used to refer to that database to \fBnildatabase\fP, so that no
Xfurther attempts are made to refer to it. 
X.P
XAnother function has been introduced here -- \fBdrop\_re\fP.  This function
Xis intended for use on subject lines.  It removes any ``Re:'' prefixes
Xfrom the subject line.  If you are storing subject lines in a database,
Xyou will want to do this without any ``Re:'' prefixes that might get
Xadded by followup software.  When you search, you will want to remove
Xthese prefixes as well.
X.H 4 "File Existence"
X.P
XIt will often be the case that local newsgroup databases will not be
Xpresent for all databases.  While attempts to read such non-existent
Xfiles produce empty databases rather than errors, you may wish to avoid
Xeven that.
X.P
XWith the {fun} exists function, you can test if a file exists at all.
XYou might wish to set flags if the database is not found.
X.Bb
Xif( !exists( "~/News/~n/bigbase" ) )
X	dontscan = true;
X.Be
X.H 3 "Creating Directories"
X.P
XUnfortunately, the \fBwrite\_database\fP procedure is not able
Xto create directories for files as needed.  If you write to a database
Xusing \fB~n\fP in the filename, the write might well fail because
Xthe directory structure for ``comp/sys/ibm/pc'' (for example) might not
Xexist in your database directory.
X.P
XYou must create such directories by hand.  If you are using the RN
Xnewsreader, you will find that its ``Ctrl-k'' command (for editing
Xkill files) will create such directories for you.  (It then places
Xyou in the editor to edit your kill file, but you can quit without
Xwriting out your file and the directories will still be created.)
X.P
XIf you don't wish to have to create directories, you can use the
X\fB~N\fP code instead of the \fB~n\fP code.  It creates a name
Xthat fits in a single filename segment, doing the best it can in the
Xlimited (usually to 14 characters) space.
X.P
XIf the entire newsgroup name will fit in a file name, it is unaltered,
Xand no check character is added.
X.P
XFor example, for ``comp.sys.ibm.pc'' you might get the file name
X``\fBHCp.sys.ibm.pc\fP'' where ``H'' is a ``checksum'' character.
X.P
XThis will normally be unique, but there is no guarantee of it.  As new
Xnewsgroups are created, two might collide.  Use this form at your own
Xrisk.
X.H 2 "Database As Strings"
X.P
XThe normal purpose of the database is the high-speed ``hashed'' lookup
Xof exact search strings.   As databases are the only items which
XNewsClip programs can read and write to disk, a few other features
Xare available.
X.P
XIt is possible, for example, to search all the indices in a database
Xwith the \fBhas\fP operator.   If you write \fBdb has "a.*b"\fP, you
Xwill be told if any of the index strings contain that pattern.
X.P
XIt is also possible to use a database as a group of pattern strings.  If
Xyou use a database on the \fIright hand\fP side of the \fBhas\fP operator,
Xthen all the index strings in the database will be used as patterns to
Xsearch your text area.
X.P
XThis allows you to keep a file of search patterns on disk, rather than
Xhard coding them into your program.  Searching in this way is somewhat
Xslower than searching for patterns hard coded into the program, however.
XIn some ways, keeping a database of search patterns on disk is the closest
Xthing to an RN kill file.
X.P
XNote that when you read patterns from disk, you don't need four backslashes
Xto match a single literal backslash, the way you do with constant patterns
Xin your program.  Two backslashes will do.
X.H 2 "Database FOR Loop"
X.P
XYou can scan through all the strings in a database with NewsClip's special
Xvariant of the \fBfor\fP loop.  The syntax is:
X.Bb
Xfor( \fIstringvar\fP in \fIdatabase\fP )
X	\fIstatement\fP;
X.Be
X.P
XThis will cause a loop that loops through the database.  On each iteration,
Xthe string variable will have a different index value from the database.
XThere is no particular pattern to the order that the database is traversed,
Xbut you will get each index once and only once.
X.P
XFor example, you could count the number of matches of a pattern in a database
Xlike this:
X.Bb
Xint matches;
Xstring dbstr;
Xdatabase db;
Xdb = read\_database( "myfile" );
Xmatches = 0;
Xfor( dbstr in db )
X	if( dbstr has "a.*b" )
X		matches++;
X.Be
X.P
XWhen performing such a loop, it is possible to get the integer value for
Xthe index in the normal way, with \fBdb[dbstr]\fP in this case.  This
Xis not particularly efficient, however, as this special \fBfor\fP loop
Xis intended to scan the strings of the database, not the values.
X
X.H 2 "Database Uses"
X.P
XYou can use database as ``kill files,'' or as their reverse -- what
Xyou might call ``keep files.''  It is possible, however, to do much
Xmore.  As noted, databases can remember scores, and you can assign
Xthose scores to articles that have fields in your databases.
X.P
XYou may wish to keep lots of database files, or just a few.  If you
Xaren't using the database index integer value fields for anything,
Xyou can combine several databases in one database by varying the value
Xfield.   You might make it 1 for message-ids to keep, -1 for message-ids
Xto reject, 2 for users to accept, -2 for users to reject and so on.
XSuch strings are unlikely to ever overlap.
X.P
XIf you don't want to keep individual databases for individual newsgroups,
Xyou can also concatenate newsgroup names and the key strings together before
Xadding to and searching the database.  The \fBconcat\fP function helps you
Xdo that.
X.P
XYour system manager might also keep system databases associated with
Xnewsgroups in the news spool directories, the news library directory or
Xeven the newsclip library directory.  Check local system documentation for
Xinformation.
X.P
XIf you do not use the automatic \fBxref\fP feature,
Xyou can use databases to avoid processing crossposted articles more than
Xonce.  If you see a crossposted article during a session, you might record
Xits message-id in a database, and then check to avoid processing articles 
Xalready in the database.   Normally you just import the \fBxref\fP variable
Xon machines that keep \fBXref:\fP lines in their articles.
X.P
X(Alternately, we suggest rejecting articles where the first group in
Xthe \fBnewsgroups\fP array that is a \fBnewsrc\_group\fP is \fB\fP not
Xthe \fBmain\_newsgroup\fP.)
X.P
XThe nice thing about the database is that other programs, such as your
Xnewsreader, can easily update your database files.  Usually all it takes
Xis adding a line to the end of the file containing the string you want to add.
XYou may be able to program macros in your newsreader that stick records into
Xyour news filter databases with the touch of a key.
X
X.H 2 "Notes"
X.P
XAssigning to a database index creates an index if it isn't already
Xpresent in the database.  You must thus be careful to only make assignments
Xwhen you actually want the entry to be in the database.   Don't
Xassume that assigning an integer value of 0 will stop the index from
Xbeing present in the database.
X.P
XIf you do assign 0 to an index, you will of course get 0 back when you
Xtry to get the value of that index.  That is also what you get back when
Xan index isn't there.   The only way to tell the difference between an
Xindex that isn't there and one with a value of zero is the \fBin\fP
Xoperator.  We advise in general against using the value zero.
X.P
XIt is possible to use the increment (\fB++\fP) and decrement (\fB-\-\fP)
Xoperators on a database index.  If the index doesn't exist, it gets
Xcreated at 0, and then incremented (to 1) or decremented (to -1).
X.P
XIf you ask whether any element of an array exists \fBin\fP a database,
Xonly the access date
Xon the first entry in the array that was found will be updated.
XIf you ask whether a string or strings match a database full of patterns,
Xonly the first pattern in the database that matches has its access time
Xupdated.
X.P
X``First'' by the way, is hard to predict, as the patterns are
Xtried in an internal database order that has no relation to how the
Xitems were entered into the database, or the order they appear in
Xdatabase files.
X.P
XWhile it's not usually called for, you can delete and free up individual
Xindices from the database with the \fBdb\_delete\fP procedure.
X.Bb
Xmydb["foo"] = 100;
Xdb\_delete( mydb, "foo" );
X.Be
X
X.H 3 "Message IDs"
X.P
XMost of the examples above were done with message-ids.  Unfortunately,
Xdue to human error and the wide variety of posting software that exists,
Xnot all followup messages contain a \fB: References\fP line with a proper
Xlist of parent articles.
X.P
XThis means that if you filter using message ids, you still may not get
Xexactly what you want.
X.P
XThe other alternative is to use subjects.  Followups usually have
Xa \fBSubject:\fP line that consists of the original article with
X\fBRe:\fP prepended.   If there is buggy software, there might be
Xmore than one \fBRe:\fP, but this is usually not the case.
X.P
XThis is still not perfect, because may people who do followups generate
Xnew subjects.  This is, in fact, a wise move when the subject of
Xa stream of articles drifts away from the original topic.  If the
X\fBreferences\fP array were always correct, this would not be a problem.
X.P
XYou can store subjects in a database and search for them just like
Xmessage-ids.  Just be sure to apply the \fBdrop\_re\fP function to
Xany subject lines you work with.
X.P
XYou can also store subject lines in a database that you intend to use
Xas a database of patterns.  This will catch messages where the original
Xsubject is still present, but has been expanded upon.  It's slower, but
Xwill catch more articles.   If you do this, be sure to apply the
X\fBliteral\_pattern\fP function as well as the \fBdrop\_re\fP
Xfunction before storing the subject in your database of patterns.
X.Bb
Xextern string drop\_re( string );
Xextern string literal\_pattern( string );
X/* to store */
Xdatab[ literal\_pattern( drop\_re( subject ) ) ] = true;
X/* to check */
Xreject if subject has datab;
X.Be
X.P
XPerhaps you will find it best to work with both subjects and
Xmessage-ids.
X
X
X
X.H 1 "Declarations"
X.P
XWe have already discussed a few types of declarations, such as variables,
Xexternal variables and entry point procedures.
X.P
XTo summarize, global declarations can appear anywhere in the program, outside
Xthe bodies of procedures and functions.
X.P
XLocal declarations can appear inside procedures and functions, at the very top.
X.P
XVariable declarations consist of a type name, the optional \fBarray\fP
Xclassifier and the name of the variable.  External variables can be
Ximported by preceding the above with \fBextern\fP.
X.H 2 "External Functions"
X.P
XIt is also possible, and usually necessary, to make external declarations
Xfor functions and procedures.  All the functions we have described to you
Xso far have been special ``predeclared'' functions.  You do not have to
Xwrite external declarations for predeclared functions, and so you have
Xgotten along fine so far.
X.P
XA sample external declaration looks like this:
X.Bb
Xextern string left( string, int );
X.Be
X.P
XYou can make an external function declaration at either the global level
Xor the local level.  Global external declarations last for the rest of
Xthe program.  Local ones exist only inside the routine they're in.
XYou can't make an external declaration for one of your own functions.
XIf you have to use one of those before you declare it in the program, you
Xmake a forward declaration.
X.P
XThe general syntax is:
X.Bb
Xextern \fItype\fP \fIfuncname\fP( \fItype, type, ...\fP );
X.Be
XThe first type is the return type.  In the parentheses, you give a list
Xof types for the arguments.  You must provide a type for each argument.
X.P
XThere are many external functions available in the NewsClip library,
Xmany more in the C library, and you can also define your own.
X
X.H 2 "Local Procedures & Functions"
X.P
XProcedures and functions are almost identical in form.   Functions return
Xa value, and the type of value they return must be declared.  Procedures
Xdon't return values, so the word \fBprocedure\fP is placed where you
Xwould put the return type.  Two examples are:
X.Bb
Xprocedure
Xbumpcount()
X{
X	counter = counter + 5;
X}
Xint
Xsquare( int value )
X{
X	return value * value;
X}
X.Be
X.P
XAs you can see, procedures and functions can have an optional list of
X\fIarguments\fP provided.   In this list, you will name arguments with
Xa type and an argument name.   Those arguments can be used like variables
Xwithin the subroutine.
X.P
XThe details of procedures and functions are complex.  If you are not
Xalready familiar with C programming, we do not advise that you make use
Xof procedures and functions, other than in defining the simple entry
Xpoint procedures shown in the examples.  In this section, we will discuss
Xonly the fine points of procedures and functions, assuming that the reader
Xhas a knowledge of C or some similar programming language.
X.P
XThe other main difference between procedures and functions is the
X\fBreturn\fP statement.  In both cases, \fBreturn\fP makes the subroutine
Xterminate immediately.
XInside a function, the \fBreturn\fP statement must be given an argument,
Xand that argument must be of a type that matches the declared return type
Xthat came before the function name.
X.P
XInside a procedure, the \fBreturn\fP statement must not be given an
Xargument.  It just triggers termination of the procedure.
X.P
XThe \fBaccept\fP and \fBreject\fP statements may only be used in
Xprocedures.   In fact, they should only be used in procedures that
Xhave been called only within procedures.  While this rule is not
Xstrictly enforced, if your \fBarticle\fP procedure calls a function that
Xcalls a procedure that does a \fBreject\fP, then the score will indeed
Xbe set to a very negative number, but processing will not be
Xterminated immediately.
X.P
XYou have, of course, already been using procedures, as the main
Xentry points like \fBarticle\fP are actually procedures with no arguments.
X.P
XArguments must be declared, and user subroutines can only take a fixed
Xnumber of arguments.   Argument types must match or be compatible when
Xcalling a subroutine.   Automatic type conversion is done, unlike in C.
XFor example, if you pass a newsgroup variable to a subroutine that takes
Xa string argument, the newsgroup name string will actually be passed.
X.P
XArguments are passed by value only.  This means that subroutines can
Xuse their arguments like variables, but they can't change the value
Xof an argument variable for the caller, and thus can't pass values back
Xexcept via the function return type.  Sadly, procedures which wish to pass
Xvalues back are limited to setting global variables.
X.P
XAll procedures and functions must be declared in advance, except for
Xa special list of predeclared routines that are part of the NewsClip
Xlanguage.  You may not use an undeclared function and assume it returns
Xan integer, as you can in C.
X
X.H 2 "Forward Declarations"
X.P
XIf you plan to use a procedure before it is defined in your program
Xfile, you must make a forward declaration.  A forward declaration looks
Xjust like an \fBextern\fP declaration, except the keyword \fBforward\fP
Xappears in place of \fBextern\fP.
X.P
XFor example, if a function is defined as:
X.Bb
Xstring
Xrepeat( int n, string str )
X{
X	int i;
X	extern string concat( string, string );
X	string ret;
X	ret = "";
X	for( i = 0; i < n; i++ )
X		ret = concat( ret, str );
X	return ret;
X}
X.Be
XThen a forward definition would look like:
X.Bb
Xforward string repeat( int, string );
X.Be
X
X.H 2 "Your Own Headers"
X.P
XWhile there is a predefined header variable that you can import for
Xjust about every known USENET header, you may still wish to define your
Xown header variables.  You can, with a special declaration.  In place of
Xa regular declaration, say:
X.Bb
Xheader \fItype\fP \fIvarname\fP : "\fIkeyword\fP";
Xor
Xheader \fItype\fP array \fIvarname\fP : "\fIkeyword\fP", "\fIdelims\fP";
X.Be
X.P
XThe header line prefaced by the \fIkeyword\fP will be processed and
Xstored into your
Xvariable.   If it's an array variable, the field will be parsed with the
Xdelimiters you specify in the delimiter string.   Any span of the
Xdelimiter characters counts as one delimiter, by the way.  Typical
Xdelimiters are space, tab and comma.
X.P
XYou can use this to define new header lines that appear but aren't supported
Xby NewsClip yet.  You can also make your own definitions for standard
Xheader lines, so long as you don't try to import the official header
Xvariable.  The only header line you can't make your own definition for
Xis \fBnewsgroups\fP -- that's always a newsgroup array.
X.P
XYou can, for example, define your own \fBXref:\fP header line, thus
Xavoiding the automatic processing that comes when you import the
X\fBxref\fP variable.
X.P
XThere is a special trick involving the \fIkeyword\fPs.  If your keyword
Xstring starts with a lower case letter, then the field will be mapped
Xto lower case before being parsed.   If it starts with an upper case
Xletter, the mapping doesn't take place.   Almost all the predefined header
Xvariables include mapping to lower case, as it's better for string comparisons
Xand pattern matching.
X.P
XYou can thus use your own header declarations to stop the lower case
Xmapping done normally in NewsClip.
X.Bb
Xheader string subject : "Subject";
X.Be
Xwould give you a new subject variable.   While this example shows the
Xnew variable replacing the old, we advise that you give new variables
Xdifferent names when possible, so as to avoid confusion.
X.Bb
Xheader string upsubject : "Subject";
X.Be
X.P
XAnother special trick involves the delimiter string.  While you can include
Xspace as a delimiter character, that doesn't allow you to break up things
Xlike multi-word lists split up with commas.   If you put an \fBS\fP at the
Xfront of the delimiter string, the S does not become a delimiter.  Instead
Xit signals that white space should be removed from the front and end of
Xelement strings.   This is how the \fBKeywords\fP array is parsed, by default,
Xusing a delimiter string of \fB"S;"\fP to do the job.
X.P
XHeader declarations may only appear as global declarations.  You can not
Xplace them inside subroutines.
X
X
X
X.H 1 "General Notes"
X.P
XIn this chapter we describe some of the special functions, procedures
Xand variables available for use in NewsClip programs.  Sometimes the
Xdescription here is just an introduction.  You should check the reference
Xsection for more details.
X
X.H 2 "Distribution"
X.P
XThere may be times when you wish to examine an article based on how
Xfar it was intended to be distributed.  This is of use in filtering feeds
Xto other sites, but it is also useful to readers who want to give priority
Xto articles posted to a local subnet of a worldwide group.
X.P
XUSENET distribution is normally controlled by the \fBNewsgroups:\fP line, but
Xdistribution can also be restricted by proving a further list of groups
Xon a \fBDistribution:\fP line.
X.P
XIt is worth noting that while the names used on the \fBDistribution:\fP line
Xare usually thought of as special distribution keywords, they are actually
Xspecial newsgroups.  Thus \fBdistribution\fP is a newsgroup array.  In
Xfact, you can use regular newsgroup names as distributions if you want.
XAn article posted to ``comp.misc'' with a distribution of ``rec.humor''
Xwould only go to machines that get both.
X.P
XUsually on USENET the higher level ``root'' names are used to define
Xhierarchies of newsgroups as well as distribution.  ``Comp'' is a newsgroup
Xthat nobody posts to, but any machine that is fed ``comp'' will be fed all
Xgroups that are in the ``comp'' hierarchy.
X.P
XWe tell you all this because the USENET distribution mechanism is not
Xwidely understood, but it's important to know about it to filter articles
Xwith it.
X.P
XIf the \fBDistribution:\fP line is missing, then the \fBNewsgroups:\fP line
Xdoubles as the distribution.   As such, we advise you not to use the
X\fBdistribution\fP variable, but rather the special \fBrdistribution\fP
Xvariable.  This variable gives the \fBdistribution\fP array if that is
Xpresent, and gives \fBnewsgroups\fP otherwise.  As such it is always
Xdefined and correct.
X.P
XYou can check to see if an article has been explicitly limited to a
Xdistribution like ``usa'' by using an expression like
X\fB#usa in rdistribution\fP, remembering that ``usa'' is a newsgroup name
Xconstant.   It is more likely you will want to check for anything that
Xstarts with usa, so you might rather use \fBrdistribution has "^usa"\fP,
Xwhich uses pattern matching.
X.P
XNormally, however, you just want to check the distribution level -- is it
Xlocal, citywide, statewide, national or international?  In other words,
Xabout how many machines was the article posted to?
X.P
XAssuming the NewsClip system has been installed properly, every newsgroup
Xand distribution (remember that distributions are just special newsgroups)
Xwill have an integer \fIdistribution level\fP associated with it.
XYou can get this level with the function \fBdlevel(group)\fP.
X.P
XThis number is an estimate of the number of machines that get
Xthe given group or distribution.   It is by no means guaranteed to be
Xanywhere near accurate.  What is important is that the numbers are
Xordered, and that wider distributions will have a higher distribution
Xlevel number than small ones.   This means that you can do comparisons.
X.P
XTo help in this, we have defined some special fake newsgroups which
Xexist solely to be used as arguments to \fBdlevel\fP.  They are names
Xlike \fB#local\fP, \fB#organization\fP, \fB#city\fP, \fB#region\fP,
X\fB#state\fP, \fB#province\fP, \fB#country\fP, \fB#continent\fP,
X\fB#usenet\fP and \fB#world\fP.   They will be defined to be the
Xright numbers for the distributions at your site that match these
Xgeographic regions.
X.P
XIf you want to see how far a group is going, you can ask something
Xlike:
X.Bb
Xif( dlevel(group) >= dlevel( #state ) )
X.Be
Xand that should tell you if the group is statewide or larger.  Of course
Xyou can also hard code your local statewide distribution if you want
Xto.
X
X.H 3 "distribution\_level"
X.P
XThe key to all this is a special variable called \fBdistribution\_level\fP.
XIt is the distribution number for the current article.  It is calculated
Xby looking at all the groups on the \fBDistribution:\fP and \fBNewsgroups:\fP
Xlines.
X.P
XIf you want to see all the articles posted for within your city, you can
Xsay:
X.Bb
Xaccept if distribution\_level <= dlevel( #city );
X.Be
X.P
XYou can also assign points as you like based on distribution, assigning
Xmore or less to the score based on how wide the article was meant to go.
XAnd of course, you can do it on a group by group basis.
X.P
XThis is similar to another trick, which shows you articles posted by
Xlocal people.  If you're in the large domain ``foo.edu,'' you can ask to see
Xall articles posted by locals with
X\fBaccept if right(from,2) == "foo.edu";\fP or
X\fBaccept if from has "foo.edu$";\fP, whichever you prefer.  Since
Xall articles restricted to your domain normally come from people within
Xthe domain, this shows you the local articles.  It also shows you the
Xworldwide articles posted by local people.
X.P
X(The above only works if ``foo.edu'' is never used as a name on its own.
XUse \fBright(domain(from),2)\fP if ``user@foo.edu'' might exist.)
X
X.H 2 "Special Header Variables"
X.P
XIn the previous section, we saw the variable \fBrdistribution\fP which
Xgives you the ``real distribution.''  There are similar variables for
Xall the other header items which have defaults.  \fBRreply\_to\fP
Xand \fBRsender\fP default to \fBfrom\fP if their main header line is
Xmissing.  \fBRfollowup\_to\fP and \fBRdistribution\fP default to
X\fBnewsgroups\fP.
X.P
XYou have also seen some calculated variables that depend on header
Xlines.  These include \fBdistribution\_level\fP, described above,
Xand \fBfollowup\fP which is true if the article has a
X\fBReferences:\fP line.
X
X.H 2 "The Outside World"
X.P
XA few variables you can import come from places beyond even the
Xheader.  For example, when you invoke the news clipping program from
Xthe shell, you can give options that begin with the string \fIo=\fP.
X(You can spell it out as \fIoption=\fP if you like.)  The arguments
Xof these options are placed in the string array \fBoptions\fP.
X.P
XThus if your program is called \fBnclip\fP and it gets called with:
X.Bb
Xnclip o=quick o=+j
X.Be
Xthen the variable options will have two values, ``quick'' and ``+j.''
XYou can test for the presence of one with an expression like
X\fB"quick" in options\fP.
X.P
XAside from options, you can also examine environment variables with the
Xstring function \fBgetenv\fP.  You might tell users to define an
Xenvironment variable ``CLIPOPTS''.  A call to \fBgetenv("CLIPOPTS")\fP
Xwould return the string they provided, or \fBnilstring\fP if the environment
Xvariable were not defined.
X.P
XThere will be times when you want to check for messages from people
Xat your own site, and particularly for messages from yourself.  While
Xyou can code this explicitly with something like \fBfrom == "me@mysite"\fP,
Xwe have provided two external variables that contain the mail address and
Xthe site/domain of the person running the newsclip program.
X.Bb
Xextern string my\_domain;
Xextern string my\_mail\_address;
Xif( domain(from) == my\_domain ) {
X	adjust 20;
X	if( from == my\_mail\_address )
X		adjust 100;
X	}
X.Be
X.P
XYou will want to use these variables if you are writing newsclip programs
Xor subroutines for use by other people.
X.P
XIn the above example, you may have noticed the string function \fBdomain\fP,
Xwhich returns the string after the first at-sign (@) in the argument it
Xis given.  When passed a \fBuserid\fP variable, this gives the \fIsite\fP
Xor \fIdomain\fP part of the address.
X.P
XNote that there is no way to find out the local machine's full domain
Xname under program control, so these variables will only be valid if the
Xperson who installed the NewsClip system set them up properly.
X.P
XAnother useful system value is the \fBdatetime\fP value \fBtime\_now\fP,
Xwhich gives the current time, or at least the time when your news clipping
Xprogram started running.   You have already seen this used in the
X\fBwrite\_database\fP examples.
X
X.H 2 "Nil Variables"
X.P
XHeader variables that are not defined for an article, along with
Xuninitialized global variables all have what are known as
X``nil'' values.  (Uninitialized local variables have undefined values --
Xyou can't be sure at all what they are.)
X.P
XBefore using a header variable that might not have been set for an
Xarticle, you should always check first to see if it is nil.  If you
Xtry any operators or nil arrays, strings or userids, you may crash your
Xprogram -- particularly if you assign into an index of a nil or undefined
Xarray variable.
X.P
XRemember, all header variables are set to 0 or nil with each new article.
X.P
XUnlike in C, you must explicitly compare your values with special nil
Xconstants.  You can't just use an array variable alone in an if, as in:
X.Bb
Xif( arrvar )
X	whatever;
X.Be
X.P
XInstead use:
X.Bb
Xif( arrvar != nilarray )
X	whatever;
X.Be
XThere are similar values known as \fBnilstring\fP, \fBniluserid\fP and
X\fBnilnewsgroup\fP.   Nil integers and dates are set to zero.
X.P
XThere is also a special value called \fBnildatabase\fP.  If you try to
Xindex into a nil database to read values, you will always get 0, as though
Xthe index were not found.  Any attempt to store into a nil database or
Xfree one will generate an error.
X
X.H 2 "Global Control & Subscription"
X.P
XRight now, newsreading programs usually handle the details of what newsgroups
Xa person subscribes or does not subscribe to.  In general, this means that
Xthe NewsClip program's only job is to filter articles in subscribed
Xnewsgroups.
X.P
XThis doesn't have to be so, of course.  For example, if your \fBarticle\fP
Xprocedure were to include a line like:
X.Bb
Xreject if is comp.misc;
X.Be
Xthat would effectively ``unsubscribe'' you to that group.   It would
Xalso reject all crosspostings from that group into groups you read.
X.P
XWe have provided a more efficient way of doing this, by providing some
Xvariables you can set in the \fBstartgroup\fP procedure of your clipping
Xprogram.
X.P
XThese variables are called \fBreject\_all\fP and \fBaccept\_all\fP.  If
Xyou set \fBreject\_all\fP in your \fBstartgroup\fP procedure, that is the
Xsame as unsubscribing to a group.  All articles will be rejected until
Xwe are finished processing that group.
X.P
XThis is more efficient than including the sample \fBreject if\fP statement
Xabove, for if \fBreject\_all\fP is set, then the articles don't even get
Xlooked at.  They are rejected out of hand.    These two special variables
Xare reset whenever a clipping program finishes with a newsgroup, so they
Xonly apply to one newsgroup at a time.
X.P
XThe variable \fBaccept\_all\fP does the reverse.  All articles in the
Xgroup are accepted, without even being examined.  This effectively turns
Xoff the use of your \fBarticle\fP filtering procedure for the duration of
Xthat group.
X.P
XYou could set up to unsubscribe to groups with code like:
X.Bb
Xprocedure
Xstartgroup() {
X	extern newsgroup main\_newsgroup;
X	extern int reject\_all;
X	switch( main\_newsgroup ) {
X		case #comp.misc:
X		case #news.software.b:
X		case #talk.politics.misc:
X			reject\_all = true;
X		}
X			
X}
X.Be
X.P
XOf course, you could also do the reverse of this, by doing nothing for
Xthe groups named in the \fBcase\fP statements, and turning on
X\fBreject\_all\fP by default.
X.P
XThe use of these variables can be important in the \fIpipe\fP mode, where
Xyour program talks to a newsreader.  By setting one of these two special
Xvariables, you tell that newsreader that it doesn't have to even talk to
Xthe news filter program for the duration of a newsgroup.
X.P
XThese variables should not be used at all in \fIfilter\fP mode, because in that
Xmode there is no such thing as a ``current newsgroup.''
X
X.H 3 "Named Groups"
X.P
XOne good idea is to set \fBaccept\_all\fP for all groups that aren't
Xactually named in your program.  If the group isn't named in your
Xprogram, you probably don't care to scan it.  There's a handy function
Xto do this for you.
X.Bb
Xextern newsgroup main\_newsgroup;
Xextern int accept\_all;
Xextern int named\_group( newsgroup );
Xif( ! named\_group( newsgroup ) )
X	accept\_all = true;
X.Be
XA ``named group'' is one that you named as a newsgroup constant
X(\fB#rec.humor\fP) or inside an \fBis\fP operator.
X.P
XIf you deal with groups in general, for example by checking for
Xthe ``comp'' prefix on the front of the group name, such groups will
Xnot be considered named groups.  You will have to add checks for such
Xgroups to ensure you don't set \fBaccept\_all\fP on them.
X.P
XAs you might guess, \fBnamed\_group\fP returns true if the group is
Xa named one, and false otherwise.
X.P
XThis is a very useful thing to do when working with a newsreader in
Xpipe mode, as it will know to not bother sending you messages in groups
Xyou don't wish to filter.
X
X.H 2 "String Tools"
X.P
XTo help you manipulate the strings that show up in news article
Xheaders, a number of special string functions are available.  Most
Xof these have to be imported as externals.
X.P
XIt is important to note that string values are allocated in what we call
Xa ``temporary'' pool of memory.  That means that the memory for all normal
Xstrings is erased and re-used with each new article.  If you assign a
Xstring variable during the processing of one article, you can't expect
Xit to be around for the next one.
X.P
XThis is usually only a problem for code in sections outside the
X\fBarticle\fP procedure.   Strings created in the \fBstartgroup\fP procedure
Xstill go away with the first article.   If that isn't what you want, you
Xcan create permanent strings with the \fBpermstring\fP function.  Just
Xsay \fBstr = permstring( tempstr )\fP to make a string permanent.  The
Xtwo variables can even be the same.
X.P
XDon't make too many strings permanent, or you will run out of memory on
Xsome machines.
X
X.H 3 "Dots & Domains"
X.P
XMany key strings on USENET are delimited with dots.  In
Xparticular, newsgroup names and domain names are split into parts this
Xway.
X.P
XWe have provided two functions to take out substrings from such names.
XThe \fBleft\fP and \fBright\fP functions give you left or right parts
Xof such strings, delimited with dots.   You provide a string and the
Xnumber of parts you want.
X.P
XFor example, \fBleft( "comp.sys.ibm.pc", 1 )\fP is ``comp'' and
X\fBleft( "comp.sys.ibm.pc", 2 )\fP is ``comp.sys,'' as you might
Xexpect.   \fBRight( "me.my.cs.edu", 1 )\fP is ``edu,'' the top level
Xdomain.
X.P
XYou can check for left and right parts with pattern matching, but often
Xthe use of \fBleft\fP and \fBright\fP is more efficient.  If you ask for
Xmore parts than there are, you get the whole string.
X.P
XQuite often when you get an email address from a field like \fBfrom\fP,
Xyou wish to examine just the ``site name'' or ``domain'' part of it.
XThe \fBdomain\fP function does this for you.  For example,
X\fBdomain("user@foo.bar.com")\fP is ``foo.bar.com.''
X
X.H 3 "Length and Indexing"
X.P
XYou can get the length of a string with \fBstrlen\fP.  It returns an
Xinteger.   You can index any character in a string, from character 0,
Xto the character at position \fBstrlen(string)-1\fP with the
X\fBchindex\fP function.
X.P
X\fBChindex("ABCD", 2)\fP is the integer 67, which is the ascii code
Xfor the letter ``C.''   You can express character constants in single
Xquotes if you wish to do comparison.  You will find expressions like
X\fBchindex(mystr, 3) == 'S'\fP can do the trick for you.
X.P
XYou can't assign characters into a string.  You can just read them out.
X.H 3 "Subjects"
X.P
XMost USENET subject lines are from followups, and as such they have
Xone or more instances of ``Re:'' on the front.  The function
X\fBdrop\_re\fP takes a subject string, and returns the string without
Xany spaces or ``Re:'' prefixes on the front.  In many programs, this is
Xthe string you want to examine, so you will see things like:
X.Bb
Xextern string subject
X{procgap}
Xstring realsubject;
Xrealsubject = drop\_re( subject );
X.Be
Xat the front of \fBarticle\fP procedure, all subsequent tests are
Xdone on the variable \fBrealsubject\fP.
X.H 3 "Concat"
X.P
XYou can concatenate two strings together with the \fBconcat\fP function.
XIf you want to concatenate more than two strings, just call \fBconcat\fP
Xseveral times.  For example,
X.Bb
Xallthree = concat(  concat( s1, s2 ), s3 );
X.Be
Xworks fine to concatenate three strings.
X.P
XYou can use this to build filenames or database keys or whatever you
Xlike.
X
X.H 2 "Cross Posting"
X.P
XMany articles are cross posted to several groups.  In our typical
Xprogram, we did a loop so that our group-specific filtering
Xroutines were performed for each group in the list.   You may or may
Xnot always wish to do this.
X.P
XIf an article is cross posted to several groups you read, you usually
Xdon't want to see it twice.  To arrange this, simply put in an import
Xdeclaration for the \fBxref\fP variable.
X.Bb
Xextern string array xref;
X.Be
X.P
XIf you do this, and you're running in the \fInewsrc\fP mode, then when
Xa cross-posted article is rejected, it will get marked read in all the
Xsubscribed groups in which it is found.
X.P
XThis is normally what you want, but it is also not the default.  If,
Xas suggested in the chapter on a typical program, you
Xdo a loop that checks all groups an article is posted to, then if an
Xarticle is rejected in one group, it will be rejected in all the others
Xas well, as it runs through the same code.  NewsClip is fast enough
Xthat this may be all you need to do the job.
X.P
XIf you don't run the articles through the same code, you may get articles
Xaccepted in one group and rejected in another.   Sometimes this can
Xbe what you want.   Remember that many modern newsreaders make sure that
Xthey don't show you the same article more than once.  By having individual
Xcontrol over what groups an article is accepted in, you can control what
Xgroup you read articles in.  With most newsreaders, you will see the
Xarticle in the first group in your reading order.
X.P
XIf you are producing a file list (ie. not running in \fInewsrc\fP mode), it
Xis fairly important that the same article not be listed twice in two
Xdifferent places.  We advise that you import the \fBxref\fP variable in
Xthis case.   When you do, even articles that are accepted get marked off
Xas read in the other groups, so that nothing gets listed twice.
X.P
XThere are other ways to eliminate crosspostings, of course.  One suggestion
Xon this is described in the database chapter.   If your system does not
Xsupport the \fBXref:\fP header line, you will need to code up such
Xa system, and it may even be more efficient.
X
X.H 2 "Entry Points"
X.P
XYou are now very familiar with the \fBarticle\fP entry point procedure
Xthat is called with each new article.  Here is a complete list of
Xthe entry point procedures.
X.P
XRemember that most of these entry points are \fIoptional\fP.  That means
Xif you create one, but spell its name wrong, you will get a procedure
Xthat never gets called, and a null procedure where you wanted a real one.
X.H 3 "Init"
X.P
XCode that is run when the newsclip session first begins.  This
Xallows the initialization of global variables.   You will want to
Xload up your global databases and set any of the special control
Xvariables here, too.
X.H 3 "Startgroup"
X.P
XCode that is run whenever a new newsgroup is processed.  Here is where
Xyou can test for and load newsgroup related databases.  This procedure
Xis given an argument, which is an estimate of the number of unread articles
Xin the group.  The estimate may not be very accurate, and will usually only
Xbe good in \fInewsrc\fP mode.
X.H 3 "Endgroup"
X.P
XCode that is run when we're done with a newsgroup.  Here is where you can
Xwrite out your newsgroup related databases and free them.
X.H 3 "Article"
X.P
XCode that is run with each article.  This code decides whether
Xto accept or reject the article.  The default is to accept.
X.H 3 "Post_article"
X.P
XCode that is run after the \fBarticle\fP routine terminates.
XSuch code can examine the score and take special further action.
XThis procedure is given an integer argument, namely the score.  (This
Xis also available in the global \fBscore\fP variable.)
X.H 3 "Terminate"
X.P
XCode that is run when the newsclip session is done.  In this
Xsection, one usually writes out any updated information to disk.
X.H 3 "Command"
X.P
XCode that is run when a kill command comes down the pipe in \fIpipe\fP
Xmode.  This procedure gets a string argument, which is the command
Xstring.  It is expected to either \fBreject\fP the command, or process
Xit an issue an \fBaccept\fP.
X
X.H 2 "Advanced Statements"
X.P
XThere are a few statements we haven't covered at this point.  They are
Xmeant for advanced users.  Some come from C, others are new to NewsClip.
XFull descriptions can be found in the reference section.
X.H 3 "Control Flow"
X.P
XFrom C, we have included 4 basic control flow statements.  \fBbreak\fP
Xjumps out of an enclosing loop or \fBswitch\fP statement.  \fBContinue\fP
Xjumps immediately to the top of any current \fBfor\fP or \fBwhile\fP
Xloop, so that the next iteration can be executed.
X.P
XFinally, there is the general \fBgoto\fP statement, which jumps to a label
Xwithin a subroutine.
XYou can label any statement by placing an identifier and a colon in front
Xof it.
X.P
XThe \fBreturn\fP statement terminates a subroutine.  Inside a function, it
Xmust be given an argument that is the function return value.
XIn procedures it must not be given an argument.  The \fBaccept\fP and
X\fBreject\fP statements are variants of \fBreturn\fP which set the
X\fBscore\fP.
X.H 3 "Dynamic Arrays"
X.P
XAdvanced users can create arrays of any size they choose.  The array is
Xallocated in temporary memory.  Try:
X.Bb
X\fIarrayvar\fP = array \fIexpr\fP;
X.Be
Xwhere the \fIexpr\fP should be an integer expression giving the desired
Xsize of the array.  The array will index from 0 to \fIexpr\fP minus 1.
XFor example:
X.Bb
Xstring array myar;
Xmyar = array 20;
X.Be
Xcreates a string array with 20 elements, indexed from 0 to 19.
X.H 3 "String Parsing & Type Conversion"
X.P
XYou can also access the same parsing routines that turn header fields
Xinto header variables.   For scalar (non array) variables, try:
X.Bb
Xparse \fIvar\fP = \fIstring-expr\fP;
X.Be
XThis converts the string into the right type and stores it in the variable.
XYou can convert newsgroup names to newsgroup numbers, or dates to date
Xvalues this way.  It is also a handy way of converting a string to an
Xinteger.
X.P
XThe array form is:
X.Bb
Xparse \fIarrvar\fP = \fIstring-expr\fP, \fIdelims\fP;
X.Be
XThe second string expression defines the delimiters that will be used
Xto parse the array.  The same rules apply here as for the \fBheader\fP
Xdeclaration described elsewhere.  For example:
X.Bb
Xnewsgroup array myn;
Xparse myn = "rec.autos,news.groups", " ,";
X.Be
X
X
X
X.H 1 "Compiling & Operation"
X.P
END_OF_FILE
if test 53631 -ne `wc -c <'doc/man.mm.2'`; then
    echo shar: \"'doc/man.mm.2'\" unpacked with wrong size!
fi
# end of 'doc/man.mm.2'
fi
echo shar: End of archive 15 \(of 15\).
cp /dev/null ark15isdone
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