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

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

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

#! /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 4 (of 15)."
# Contents:  article.c comp/lex.c comp/predef.c group.c userlib.c
#   whoami.c
# Wrapped by allbery@uunet on Tue Dec 19 20:09:55 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'article.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'article.c'\"
else
echo shar: Extracting \"'article.c'\" \(7411 characters\)
sed "s/^X//" >'article.c' <<'END_OF_FILE'
X
X
X#include "nl.h"
X
X/*
X * Master routine to call the user's code on an article
X * Returns true if the article is to be accepted.
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
Xint num_links;			/* number of links to file */
Xint article_size;		/* size of article in bytes */
Xdatehold write_time;		/* time article was written */
Xnewsgroup dir_newsgroup;	/* directory newsgroup */
Xint distribution_level;		/* level of distribution for this article */
Xint accept_all = 0;		/* accept all articles in a group? */
Xint reject_all = 0;		/* reject all articles in a group? */
X
Xusername dummy_user = {
X"", ""
X};
X
X#include <sys/stat.h>
X
Xstatic FILE *art_desc;		/* article descriptor */
X
Xbool
Xaccept_article( filename )
Xchar *filename;		/* name of the article file, or null if a pipe */
X{
X	struct stat ourstat;		/* stat of file, if possible */
X	extern bool needs_stat;		/* is a stat of the article needed? */
X	extern datehold time_now;	/* time this program was run */
X	extern newsgroup main_newsgroup;	/* the 'current newsgroup' */
X	extern array *newsgroups;	/* array of newsgroups for this art */
X	extern int extra_groups;	/* number of temporary groups */
X	extern int ex_group_base;	/* base for temporary group count */
X	extern int score;		/* score of article test */
X	extern int io_mode;		/* style of article reading */
X	FILE *art;		/* file stream of the article */
X	bool filemode;			/* are we using a file */
X	extern char *article_filename;	/* global for user program */
X	extern int reading_mode;	/* style of file reading */
X
X	/* first we check if global accept/reject flags are on for this group */
X
X	if( accept_all ) {
X		score = 1000;
X		return TRUE;
X		}
X	 else if( reject_all ) {
X		score = -1000;
X		return FALSE;
X		}
X
X	score = 1;		/* default is to accept */
X
X	filemode = reading_mode != PIPE_READ;
X
X	/* Next open the file, if the article is indeed in a file */
X
X	if( filemode ) {
X		art = fopen( filename, "r" );
X
X		if( !art ) {
X			warning( 3, "Could not open article %s\n", filename );
X			score = -1;
X			return FALSE;
X			}
X		article_filename = filename;
X		}
X	 else {
X		art = (FILE *)0;
X		article_filename = "";
X		}
X
X	/* Get statistics from the inode if the article is in a file and
X	   the user program wants these statistics */
X
X	if( needs_stat && reading_mode == FILE_FULL &&
X				fstat( fileno(art), &ourstat ) == 0 ) {
X		num_links = ourstat.st_nlink;
X		if( (int)ourstat.st_size != ourstat.st_size )
X			article_size = MAXINT;
X		 else
X			article_size = ourstat.st_size;
X		write_time = (datehold)ourstat.st_mtime;
X		}
X	 else {
X		num_links = 1;
X		article_size = -1;
X		write_time = time_now;
X		}
X
X	/* clear region for temporary newsgroups */
X	extra_groups = ex_group_base;
X
X	/* scan the header.  If there are fewer than 3 header items
X	   in it, it's a bogus article and is rejected */
X	if( reading_mode != FILE_FULL )
X		if( ask_for_header() == ERRCODE )
X			return ERRCODE;
X
X	if( read_header( art ) < 3 ) {
X		warning( 3, "Invalid header in file %s\n", article_filename );
X		return FALSE;
X		}
X
X
X	/* An article without a Newsgroups line is a bogus one */
X	if( newsgroups == (array *)0 ) {
X		warning( 3, "Missing Newsgroups in file %s\n", article_filename );
X		return FALSE;
X		}
X
X	/* Initialize the routines that might scan the article body */
X
X	prepare_body( io_mode );
X
X	/* If there isn't really a current newsgroup, invent one by
X	   taking the first group on the newsgroups line */
X
X	if( main_newsgroup == NULL_NEWSGROUP )
X		main_newsgroup = newsgroups->vals[0].uinteger;
X
X	/* Set the newsgroup expanded in ~N codes in filenames */
X
X	dir_newsgroup = main_newsgroup;
X
X	/* prepare the calculated items */
X	calc_items();
X
X	/* now run the code on the article */
X
X	art_desc = art;			/* external with file descriptor */
X
X	/* call the user routines that set the score and deal with it */
X
X	Uarticle();
X	Upost_article(score);
X
X
X	if( filemode && art )
X		fclose( art );
X
X	/* A positive score means accept.  0 or less is reject */
X
X	return score > 0;
X}
X
Xusername *rsender;		/* Sender, or From: if no sender */
Xusername *rreply_to;		/* Reply-to, or From: if no reply-to */
Xarray *rdistribution;		/* Distribution, or newsgroups if none */
Xarray *rfollowup_to;		/* followup-to, or newsgroups if none */
Xint followup;			/* indicates if the message is a followup */
X
X/* This routine generates the guaranteed defined field variables, for
X   header items that are optional, but which have a default should they
X   not be present */
X
Xcalc_items()
X{
X	/* various header items we calculate from */
X	extern username *sender;
X	extern username *from;
X	extern username *reply_to;
X	extern char *subject;
X	extern char *message_id;
X	extern array *distribution;
X	extern array *followup_to;
X	extern array *newsgroups;
X	extern int followup;
X	extern array *references;
X	extern bool wants_dist;			/* does the user need a
X						distribution calculation? */
X	extern int distribution_level;
X
X	/* Set up dummys if the required headers are missing */
X	/* Usually if this happens we have a manged article and should
X	   probably reject it */
X
X	if( from == (username *)0 )
X		from = &dummy_user;
X	if( subject == (char *)0 )
X		subject = "";
X	if( message_id == (char *)0 )
X		message_id = "<NULL>";
X
X	rsender = sender ? sender : from;
X	rreply_to = reply_to ? reply_to : from;
X	rdistribution = distribution && distribution->arsize > 0 ?
X				distribution : newsgroups;
X	rfollowup_to = followup_to ? followup_to : newsgroups;
X	followup = references && references->arsize > 0;
X
X	/* figure out the integer representing the widest distribution
X	   this article will get */
X
X	if( wants_dist ) {
X		int i;
X		int gdist;
X		int newsgroup_glevel;
X		distribution_level = 1;		/* local */
X
X		/* find the maximal distribution from dist line */
X		for( i = 0; i < rdistribution->arsize; i++ ) {
X			gdist = ngarray[rdistribution->vals[i].uinteger]
X						->distribution;
X			if( gdist > distribution_level )
X				distribution_level = gdist;
X			}
X		newsgroup_glevel = 1;
X		for( i = 0; i < newsgroups->arsize; i++ ) {
X			gdist = ngarray[newsgroups->vals[i].uinteger]
X						->distribution;
X			if( gdist > newsgroup_glevel )
X				newsgroup_glevel = gdist;
X			}
X		/* if the groups had a smaller distribution, use it, as
X			the distribution line only restricts distribution,
X			it does not expand it */
X		if( newsgroup_glevel < distribution_level )
X			distribution_level = newsgroup_glevel;
X		}
X
X}
X
X/* Routine called by body reading code to get file descriptor */
X
XFILE *
Xget_body_desc(size)
Xint size;
X{
X	extern int reading_mode;
X	if( reading_mode != FILE_FULL )
X		ask_for_body();
X	return art_desc;
X}
X
X/* This function gets the size of the article for user programs */
X
Xart_bytes()
X{
X	extern int header_size;
X	long calc_size;
X
X	if( article_size >= 0 )
X		return article_size;
X	 else
X		return makeint((long)header_size + byte_count(7));
X}
X
X/* When the current group changes, we call user code and clear global
X   accept/reject flags */
X
Xfinish_group()
X{
X	extern int accept_all, reject_all;	/* global flags */
X
X	Uendgroup();
X	accept_all = reject_all = FALSE;
X}
END_OF_FILE
if test 7411 -ne `wc -c <'article.c'`; then
    echo shar: \"'article.c'\" unpacked with wrong size!
fi
# end of 'article.c'
fi
if test -f 'comp/lex.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comp/lex.c'\"
else
echo shar: Extracting \"'comp/lex.c'\" \(6997 characters\)
sed "s/^X//" >'comp/lex.c' <<'END_OF_FILE'
X
X/*
X * Scanner for the Newsclip compiler.  A fairly simple scanner.
X * Handwritten scanners are not complex and are usually smaller and faster
X * than those generated by lex.
X *
X * The only thing fancy here are the 'newsgroup name' tokens, which are
X * always preceded by either a '#' or the 'is' operator/token.  These are
X * special because they are not otherwise quoted and can contain things
X * like dashes and plus signs, which would otherwise delimit tokens.
X */
X /*
X  * Newsclip(TM) Compiler 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 compiler for the
X  * use of licenced Newsclip customers.  Minor source code modifications
X  * are allowed before compiling.
X  * A short time evaluation of this product is also permitted.  See the file
X  * 'Licence' in the library source directory for details.
X  */
X
X#include "nc.h"
X
X#include "y.tab.h"
X
X
X#define EOFCHAR -1
X
Xchar yytext[MAX_LLEN];
Xint yylineno = 0;
Xint column = 0;
Xchar *yyfilename;
Xextern YYSTYPE yylval;
Xint lookaheadchar;
XFILE *yyin;			/* Input stream */
X
X#define scanerror yyerror
X
Xextern char *allocstring();
Xextern FILE *trace;
X
X/* number - string base for newsgroup names */
X
X
Xint
Xyylex()
X{
X	register c;
X	register char *cp;
X	int f;
X	int toktype;
X	char delim;
X/* Temporary violation of indent to give room for bit switch */
X
Xif (yylineno == 0)
X	yylineno++;
X
Xwhile(1) {
X	/*
X	 * skip white space
X	 */
X	if (c = lookaheadchar ) {
X		lookaheadchar = 0;
X		}
X	 else
X		c = readkey();
X
X	cp = yytext;
X	while (c == ' ' || c == '\t' || c == 0 || c == 12 /* FF */) {
X		c = readkey();
X	}
X	yytext[0] = c; yytext[1] = yytext[2] = 0;
X	if( isascii(c) && (isalpha( c ) || c == '_') ) {
X		do {
X			*cp++ = lowcase(c);
X			c = readkey();
X		} while (isascii(c) && (isalnum(c) || c == '_'));
X		*cp = 0;
X
X		lookaheadchar = c;
X		/* special newsgroup op */
X		if( strcmp( yytext, "is" ) == 0 ) {
X			scanng();
X			return YQUERYGROUP;
X			}
X		 else {
X			c = look_kw(yytext);
X			if (c == 0) {
X				yylval.strval = allocstring(yytext);
X				return (YID);
X				}
X			return c;
X			}
X		}
X
X	else if( isascii(c) && isdigit(c) ) {
X		do {
X			*cp++ = c;
X			c = readkey();
X		} while (isascii(c) && isdigit(c));
X		*cp = 0;
X		lookaheadchar = c;
X		yylval.intval = atoi(yytext);
X		return (YINT);
X		}
X	/* printt2("Select on char %d - %c\n", c, c ); */
X	switch (c) {
X	case EOF:
X		strcpy( yytext, "<End-of-File>" );
X		return 0;
X	case ' ':
X	case '\t':
X	case 12: /* form feed */
X		break;
X	case '"': {
X		int backflag;
X		backflag = FALSE;
X		for(;;){
X			c = readkey();
X			if( c == '"' && !backflag )
X				break;
X			if( c == '\n' ) {
X				parerror( "Newline in string constant" );
X				return YILLCH;
X				}
X			if( !backflag && c == '\\' ) 
X				backflag = TRUE;
X			 else
X				backflag = FALSE;
X			*cp++ = c;
X			}
X		*cp = 0;
X		yylval.strval = allocstring(yytext);
X		return (YSTRING);
X		}
X	case '\'':
X		c = readkey();
X		if( c == '\\' ) {
X			c = readkey();
X			switch(c) {
X				case 'n':
X					c = '\n';
X					break;
X				case 'r':
X					c = '\r';
X					break;
X				case 't':
X					c = '\t';
X					break;
X				case 'f':
X					c = '\f';
X					break;
X				}
X			}
X		if( readkey() != '\'' )
X			parerror( "Invalid character constant" );
X		yylval.intval = c;
X		return YINT;
X		
X	case '\n':
X		break;
X	case '/':
X		c = readkey();
X		if( c == '*' )  {/* comment */
X			char oldc;
X			c = 0;
X			do {
X				oldc = c;
X				c = readkey();
X				if( c == EOF )
X					parerror( "End of File inside comment" );
X				} while( c != '/' || oldc != '*' );
X			break;
X			}
X		 else {
X			lookaheadchar = c;
X			return '/';
X			}
X
X		/*NOTREACHED*/
X		break;
X	case '#':
X		/* newsgroup or query if a newsgroup is present */
X		scanng();
X		return YNEWSGROUP;
X	case '+':
X		if( (c=readkey()) == '+' ) 
X			return INC_OP;
X		lookaheadchar = c;
X		return '+';
X
X	case '-':
X		if( (c=readkey()) == '-' ) 
X			return DEC_OP;
X		lookaheadchar = c;
X		return '-';
X
X	case '<':
X		if( (c=readkey()) == '=' ) 
X			return LE_OP;
X		lookaheadchar = c;
X		return '<';
X
X	case '>':
X		if( (c=readkey()) == '=' ) 
X			return GE_OP;
X		lookaheadchar = c;
X		return '>';
X
X	case '!':
X		if( (c=readkey()) == '=' ) 
X			return NE_OP;
X		lookaheadchar = c;
X		return '!';
X
X	case '=':
X		if( (c=readkey()) == '=' ) 
X			return EQ_OP;
X		lookaheadchar = c;
X		return '=';
X	case '&':
X		if( (c=readkey()) == '&' ) 
X			return AND_OP;
X		lookaheadchar = c;
X		return '&';
X	case '|':
X		if( (c=readkey()) == '|' ) 
X			return OR_OP;
X		lookaheadchar = c;
X		return '|';
X
X	case ';':
X	case ',':
X	case '*':
X	case '[':
X	case ']':
X	case '{':
X	case '}':
X	case '%':
X	case '^':
X	case '?':
X	case '(':
X	case ')':
X	case '.':
X	case ':':
X		return c;
X
X	default:
X		if (c <= 0)
X			return (0);
X		do
X			lookaheadchar = readkey();
X		while (lookaheadchar == c);
X		/* printt1("illegal char in scanner %o\n", c); */
X		return (YILLCH);
X	}
X  
X  } /* big while */
X}
X
X
Xstruct kwtab {
X	char *word;
X	int ttype;
X} kwtab[] = {
X	"accept",	YACCEPT,
X	"adjust",	YADJUST,
X	"array",	YARRAY,
X	"break",	YBREAK,
X	"case",		YCASE,
X	"continue",	YCONTINUE,
X	"database",	YTDATABASE,
X	"datetime",	YTDATE,
X	"default",	YDEFAULT,
X	"else",		YELSE,
X	"extern",	YEXTERN,
X	"for",		YFOR,
X	"forward",	YFORWARD,
X	"goto",		YGOTO,
X	"has",		YHAS,
X	"header",	YHEADER,
X	"if",		YIF,
X	"in",		YIN,
X	"int",		YTINT,
X	"newsgroup",	YTNEWSGROUP,
X	"parse",	YPARSE,
X	"procedure",	YPROCEDURE,
X	"reject",	YREJECT,
X	"return",	YRETURN,
X	"string",	YTSTRING,
X	"switch",	YSWITCH,
X	"userid",	YTUSERID,
X	"while",	YWHILE,
X	0,		0,
X};
X
X
X
Xint
Xlook_kw(str)
Xregister char *str;
X{
X	register int l;
X	int h, m, r;
X
X	
X	l=0; h=sizeof(kwtab) / sizeof(kwtab[0]) - 2;
X	while (l <= h) {
X		m = (l+h)/2;
X		r = strcmp(kwtab[m].word, str);
X		if (r < 0)
X			l = m+1;
X		else if (r > 0)
X			h = m-1;
X		else {
X			return kwtab[m].ttype;
X			}
X	}
X	return 0;
X}
X
Xreadkey()
X{
X	int c;
X	extern int listing;
X
X	c = getc(yyin);
X
X	if( column == 0 && c == '#' ) {
X		char buf[MAX_FNAME+20];
X		char *p, *eq;
X		char *space;
X		int len;
X
X		fgets( buf, sizeof(buf), yyin );
X		/* accept both # and #line directives */
X		space = strchr( buf, ' ' );
X		if( space )
X			yylineno = atoi( space+1 );
X		/* get filename */
X		p = strchr( buf, '"' );
X		if( p ) {
X			p++;
X			eq = strchr( p, '"' );
X			if( eq )
X				*eq = 0;
X			if( strcmp( p, yyfilename ) != 0 )
X				yyfilename = allocstring( p );
X			}
X		c = getc(yyin);
X		}
X
X	if (c == '\n') {
X		yylineno++;
X		column = 0;
X		}
X	 else
X		column++;
X	return c;
X}
X
X/* numbered group database structure */
X
Xstruct stringmap thegroups = { 0, 0, 0, 0 };
X
X/* scan a newsgroup name after a newsgroup name prefix token (is|#) */
X
Xscanng()
X{
X	int c;			/* byte read */
X	char *cp;		/* pointer into yytext */
X
X	cp = yytext;
X
X	/* skip whitespace */
X
X	do {
X		c = readkey();
X	} while( isspace(c) );
X	while (isascii(c) && (isalnum(c) ||
X					strchr( "_-.+", c ) != NULL) ) {
X		*cp++ = c;
X		c = readkey();
X		}
X	*cp = 0;
X	yylval.intval = nums_lookup( &thegroups, yytext );
X	lookaheadchar = c;
X}
X
Xyyerror(s)
Xchar *s;
X{
X	parerror( "%s at or near %s", s, yytext );
X}
END_OF_FILE
if test 6997 -ne `wc -c <'comp/lex.c'`; then
    echo shar: \"'comp/lex.c'\" unpacked with wrong size!
fi
# end of 'comp/lex.c'
fi
if test -f 'comp/predef.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comp/predef.c'\"
else
echo shar: Extracting \"'comp/predef.c'\" \(6938 characters\)
sed "s/^X//" >'comp/predef.c' <<'END_OF_FILE'
X#include "nc.h"
X
X/*
X * The predefined symbols
X */
X
X/* These symbols come in a variety of types.   Most must be declared as
X   an external to be referenced.  Some can be referenced without being
X   declared.  Such variables have OSF_PREDEF on.
X
X   Some trigger special actions, such as the inclusion of a header line
X   in the header parse list, or the setting of a boolean variable in the
X   user code.  Header items have SPC_HEADER on.
X
X   Various other codes are defined in "nc.h"
X */
X /*
X  * Newsclip(TM) Compiler 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 compiler for the
X  * use of licenced Newsclip customers.  Minor source code modifications
X  * are allowed before compiling.
X  * A short time evaluation of this product is also permitted.  See the file
X  * 'Licence' in the library source directory for details.
X  */
X
X#define TL static struct typelist
X#define TLL static dtype
X
XTLL dta[] = {T_INTEGER};
XTL oneint = { 1, 1, 1, dta };
X
XTLL doneng[] = {T_NEWSGROUP};
XTL oneng = { 1, 1, 1, doneng };
X
XTLL dts[] = {T_STRING};
XTL onestring = { 1, 1, 1, dts };
X
XTLL dttext[] = {T_TEXT};
XTL onetext = { 1, 1, 1, dttext };
X
XTLL dt2s[] = {T_STRING,T_STRING};
XTL twostring = { 2, 2, 2, dt2s };
X
XTLL dtsi[] = {T_STRING, T_INTEGER};
XTL stringint = { 2, 2, 2, dtsi };
X
XTLL dunam[] = { T_USERNAME };
XTL oneuname = { 1, 1, 1, dunam };
X
XTLL donear[] = { T_GENARRAY };
XTL onearray = { 1, 1, 1, donear };
X
XTLL donedb[] = { T_DATABASE };
XTL onedb = { 1, 1, 1, donedb };
X
XTLL ddbstring[] = { T_DATABASE, T_STRING };
XTL dbstring = { 2, 2, 2, ddbstring };
X
XTLL darb[] = { T_STRING };		/* printf arbitrary arg stream */
XTL strarb = { 1, 255, 1, darb };
X
XTL none = { 0, 0, 0 };
X
XTLL dwrdb[] = { T_DATABASE, T_STRING, T_DATE };
XTL wrdb = { 3, 3, 3, dwrdb };
X
Xstruct outsym predefs[] = {
X{ "approved",		ST_VAR,	T_USERNAME,		SPC_HEADER },
X{ "control",		ST_VAR,	T_STRING,		SPC_HEADER },
X{ "date",		ST_VAR,	T_DATE,			SPC_HEADER },
X{ "distribution",	ST_VAR,	arrayof(T_NEWSGROUP),	SPC_HEADER },
X{ "rdistribution",	ST_VAR,	arrayof(T_NEWSGROUP),	SPC_NEWSGROUPS },
X{ "expires",		ST_VAR,	T_DATE,			SPC_HEADER },
X{ "followup_to",	ST_VAR,	arrayof(T_NEWSGROUP),	SPC_HEADER },
X{ "rfollowup_to",	ST_VAR,	arrayof(T_NEWSGROUP),	SPC_NEWSGROUPS },
X{ "from",		ST_VAR,	T_USERNAME,		SPC_HEADER },
X{ "keywords",		ST_VAR,	arrayof(T_STRING),	SPC_HEADER },
X{ "lines",		ST_VAR,	T_INTEGER,		SPC_HEADER },
X{ "message_id",		ST_VAR,	T_STRING,		SPC_HEADER },
X{ "newsgroups",		ST_VAR,	arrayof(T_NEWSGROUP),	OSF_PREDEF|SPC_HEADER|OSF_REFERENCED },
X{ "organization",	ST_VAR,	T_STRING,		SPC_HEADER },
X{ "path",		ST_VAR,	arrayof(T_STRING),	SPC_HEADER },
X{ "posting_version",	ST_VAR,	T_STRING,		SPC_HEADER },
X{ "references",		ST_VAR,	arrayof(T_STRING),	SPC_HEADER },
X{ "reply_to",		ST_VAR,	T_USERNAME,		SPC_HEADER },
X{ "rreply_to",		ST_VAR,	T_USERNAME,		SPC_FROM },
X{ "sender",		ST_VAR,	T_USERNAME,		SPC_HEADER },
X{ "rsender",		ST_VAR,	T_USERNAME,		SPC_FROM },
X{ "subject",		ST_VAR,	T_STRING,		SPC_HEADER },
X{ "summary",		ST_VAR,	T_STRING,		SPC_HEADER },
X{ "xref",		ST_VAR,	arrayof(T_STRING),	SPC_HEADER },
X/* useful variables */
X{ "followup",		ST_VAR,		T_INTEGER,	SPC_REF },
X{ "score",		ST_VAR,		T_INTEGER,	0 },
X{ "article_filename",	ST_VAR,		T_STRING,	0 },
X{ "article_number",	ST_VAR,		T_INTEGER,	0 },
X{ "article_bytes",	ST_VAR,		T_INTEGER,	OSF_CONST|SPC_STAT },
X{ "num_links",		ST_VAR,		T_INTEGER,	SPC_STAT },
X{ "write_time",		ST_VAR,		T_DATE,		SPC_STAT },
X/* size functions */
X{ "byte_count",		ST_FUNC,	T_INTEGER,	OSF_PREDEF, &onetext },
X{ "line_count",		ST_FUNC,	T_INTEGER,	OSF_PREDEF, &onetext },
X/* calculated variables */
X{ "distribution_level",	ST_VAR,		T_INTEGER,	SPC_DIST},
X/* control variables */
X{ "set_include_prefix",	ST_PROC,	0,			0, &onestring },
X{ "preserve_case",	ST_VAR,		T_INTEGER,		0 },
X{ "paragraph_scan",	ST_VAR,		T_INTEGER,		0 },
X{ "white_compress",	ST_VAR,		T_INTEGER,		0 },
X{ "set_signature_start",ST_PROC, 	0,			0, &onestring },
X{ "subscribe",		ST_PROC, 	0,			0, &onestring },
X{ "time_now",		ST_VAR,		T_DATE,			0 },
X{ "my_domain",		ST_VAR,		T_STRING,		0 },
X{ "my_mail_address",	ST_VAR,		T_STRING,		0 },
X{ "options",		ST_VAR,		arrayof(T_STRING),	0 },
X{ "accept_all",		ST_VAR,		T_INTEGER,	0 },
X{ "reject_all",		ST_VAR,		T_INTEGER,	0 },
X{ "main_newsgroup",	ST_VAR,		T_NEWSGROUP,	0 },
X{ "dir_newsgroup",	ST_VAR,		T_NEWSGROUP,	0 },
X{ "proc_mode",		ST_VAR,		T_STRING,	0 },
X/* time and date constants */
X{ "day",		ST_VAR,		T_DATE,		OSF_PREDEF|OSF_CONST },
X{ "week",		ST_VAR,		T_DATE,		OSF_PREDEF|OSF_CONST },
X{ "month",		ST_VAR,		T_DATE,		OSF_PREDEF|OSF_CONST },
X/* special text variables */
X{ "body",		ST_VAR,		T_TEXT,		OSF_PREDEF|OSF_CONST },
X{ "text",		ST_VAR,		T_TEXT,		OSF_PREDEF|OSF_CONST },
X{ "signature",		ST_VAR,		T_TEXT,		OSF_PREDEF|OSF_CONST },
X{ "newtext",		ST_VAR,		T_TEXT,		OSF_PREDEF|OSF_CONST },
X{ "included",		ST_VAR,		T_TEXT,		OSF_PREDEF|OSF_CONST },
X/* null constants */
X{ "false",		ST_VAR,		T_INTEGER,	OSF_PREDEF|OSF_CONST },
X{ "true",		ST_VAR,		T_INTEGER,	OSF_PREDEF|OSF_CONST },
X{ "nilstring",		ST_VAR,		T_STRING,	OSF_PREDEF|OSF_CONST },
X{ "niluserid",		ST_VAR,		T_USERNAME,	OSF_PREDEF|OSF_CONST },
X{ "nildatabase",	ST_VAR,		T_DATABASE,	OSF_PREDEF|OSF_CONST },
X{ "nilnewsgroup",	ST_VAR,		T_NEWSGROUP,	OSF_PREDEF|OSF_CONST },
X{ "nilarray",		ST_VAR,		T_ARRAY,	OSF_PREDEF|OSF_CONST },
X/* Functions, procedures and macros */
X{ "count",		ST_FUNC,	T_INTEGER,	OSF_CONST|OSF_PREDEF,
X								&onearray },
X{ "realname",		ST_FUNC,	T_STRING,	OSF_CONST|OSF_PREDEF,
X								&oneuname },
X{ "domain",		ST_FUNC,	T_STRING,	0, &onestring },
X{ "dlevel",		ST_FUNC,	T_INTEGER,	OSF_PREDEF, &oneng },
X{ "chindex",		ST_FUNC,	T_INTEGER,	OSF_CONST, &stringint },
X{ "read_database",	ST_FUNC,	T_DATABASE,	OSF_PREDEF, &onestring},
X{ "exists",		ST_FUNC,	T_INTEGER,	0, &onestring },
X{ "write_database",	ST_PROC,	0,		OSF_PREDEF, &wrdb },
X{ "free_database",	ST_PROC,	0,		OSF_PREDEF, &onedb },
X{ "fresh_database",	ST_FUNC,	T_DATABASE,	OSF_PREDEF, &oneint },
X{ "db_delete",		ST_PROC,	0,		OSF_PREDEF, &dbstring },
X{ "getenv",		ST_FUNC,	T_STRING,	0, &onestring },
X{ "dprintf",		ST_PROC,	0,		OSF_PREDEF, &strarb },
X{ "strlen",		ST_FUNC,	T_INTEGER,	0, &onestring },
X{ "left",		ST_FUNC,	T_STRING,	0, &stringint },
X{ "right",		ST_FUNC,	T_STRING,	0, &stringint },
X{ "concat",		ST_FUNC,	T_STRING,	0, &twostring },
X{ "drop_re",		ST_FUNC,	T_STRING,	0, &onestring },
X{ "permstring",		ST_FUNC,	T_STRING,	0, &onestring },
X{ "clipfront",		ST_FUNC,	T_STRING,	0, &stringint },
X{ "leftmost",		ST_FUNC,	T_STRING,	0, &stringint },
X{ "literal\_pattern",	ST_FUNC,	T_STRING,	0, &onestring },
X{ "lower",		ST_FUNC,	T_STRING,	0, &onestring },
X{ "named_group",	ST_FUNC,	T_INTEGER,	0, &oneng },
X{ "newsrc_group",	ST_FUNC,	T_INTEGER,	0, &oneng },
X{ 0, 0, 0, 0 }
X};
X
X/* some variables that get set based on what is used */
X
Xbool needs_stat = FALSE;
END_OF_FILE
if test 6938 -ne `wc -c <'comp/predef.c'`; then
    echo shar: \"'comp/predef.c'\" unpacked with wrong size!
fi
# end of 'comp/predef.c'
fi
if test -f 'group.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'group.c'\"
else
echo shar: Extracting \"'group.c'\" \(6763 characters\)
sed "s/^X//" >'group.c' <<'END_OF_FILE'
X
X/*
X * Routine to handle the processing of newsgroups
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#include "nl.h"
X
Xdbptr ng_base;		/* the database of known newsgroup names */
X
Xint group_count;	/* count of newsgroups in database */
X
Xngrec **ngarray;		/* newsgroup array */
Xngrec **ngarray_base;		/* alloced address for newsgroup array */
Xngrec *extra_g_mem;		/* memory for extra groups */
X
X/* Initialize the newsgroup database.  This may just involve the use
X   of the predefined newsgroup names in the user program, or we may read
X   in a pile of groups from the .newsrc, .nglas and active files. */
X
Xinitngs(rcmode)
Xbool rcmode;		/* true if reading newsrc */
X{
X	extern int user_gcount;	/* count of user referenced groups */
X	ngrec *group;	/* group record */
X	extern char *user_groupnames[];	/* groups referenced in user code */
X	int i;
X
X	group_count = 1;
X
X	ng_base = init_db( rcmode ? 400 : user_gcount, sizeof(ngrec) );
X
X	/* now add the newsgroup names from the user's program */
X
X	for( i = 0; user_groupnames[i]; i++ ) {
X		group = (ngrec *)add_rec(ng_base, user_groupnames[i],	
X						AR_CREATE|AR_NOALLOC);
X		if( group->ngnumber == 0 )
X			group->ngnumber = group_count++;
X		}
X
X	/* read in data needed to process a .newsrc file */
X	if( rcmode ) {
X		read_las();
X		read_newsrc();
X		read_active();
X		}
X	/* read in and set distributions */
X	handle_distrs();
X
X	/* now build the newsgroup array */
X	ngarray_base = (ngrec **)perm_alloc( (group_count+NG_EXTRAS) *
X					sizeof(ngrec *) );
X	ngarray = ngarray_base + NG_EXTRAS;
X	/* mem for temporary newsgroups */
X	extra_g_mem = (ngrec *)perm_alloc( NG_EXTRAS * sizeof(ngrec) );
X
X	/* There are NG_EXTRAS negative slots for undefined groups */
X
X	for( group = (ngrec *)first_rec(ng_base); group;
X					group =next_rec(ng_base,group) )
X		ngarray[group->ngnumber] = group;
X	ngarray[0] = (ngrec *)0;
X}
X
X/* Read the active file for the latest info on our groups */
X
X/* Note that we don't read in groups in the active file that we
X   haven't already seen in the program, .newsrc or .nglas files */
X
Xread_active()
X{
X	FILE *afile;
X	ngrec *group;
X	char buf[MAX_LLEN];		/* line buffer */
X	long highart, lowart;		/* article limits of newsgroup */
X	char *spp;			/* space position */
X
X
X	afile = dfopen( news_lib_dir, "active", "r" );
X	if( !afile ) 
X		error( "Missing active file" );
X
X
X	while( fgets( buf, sizeof(buf), afile ) ) {
X		spp = strchr( buf, ' ' );
X		if( !spp )
X			continue;		/* report error in active? */
X		*spp = 0;
X		group = (ngrec *)get_rec( ng_base, buf );
X		if( !group )
X			continue;		/* not a subscribed group */
X		if( sscanf( spp+1, "%lu %lu", &highart, &lowart ) == 2 ) {
X			group->highest = (int32)highart;
X			group->lowest = (int32)lowart;
X			group->gflag |= GF_ACTIVE;	/* it exists */
X			}
X		}
X	fclose( afile );
X}
X
X/* return the newsgroup number for a selected group 
X * If we get a group that we haven't seen, we assign it a negative
X * number and put a temporary negative entry in the newsgroup table
X * so that we can still refer to the group name.   Up to 20 unknown
X * groups can come in per article, this should be plenty.  The table
X * is reset with every article */
X
Xint extra_groups;		/* number of unknown groups, this article */
Xint ex_group_base;		/* base for extra groups */
X
Xint
Xng_number( str )
Xchar *str;		/* name of newsgroup */
X{
X	ngrec *group;
X	extern bool wants_dist;
X	extern ngrec *extra_g_mem;
X
X	group = (ngrec *)get_rec( ng_base, str );
X	if( group ) 
X		return group->ngnumber;
X	 else 
X		if( extra_groups < NG_EXTRAS ) {
X			group = &extra_g_mem[extra_groups];
X			++extra_groups;
X			zero( group, sizeof( ngrec ) );
X			group->key = str;	/* already temp alloced */
X			group->ngnumber = -extra_groups;
X			ngarray[-extra_groups] = group;
X			if( wants_dist )
X				set_distribution( group );
X			return -extra_groups;
X			}
X		 else
X			return -1;	/* 20 extra groups!  return first */
X		
X		
X}
X
Xstatic char nfname[MAX_NGLEN];
X
X/* Change a newsgroup name into a subdirectory name in the news spools */
X
Xchar *
Xngdir( groupname )
Xchar *groupname;		/* name of newsgroup, with dots */
X{
X	char *p;
X
X	strcpy( nfname, groupname );
X
X	/* map dots to slashes */
X	for( p = nfname; *p; p++ )
X		if( *p == '.' )
X			*p = '/';
X	return nfname;
X}
X
X/* Read in list of distributions, and assign a distribution to each
X   newsgroup */
X
X
Xhandle_distrs()
X{
X	char ng[MAX_NGLEN];	/* newsgroup distribution name */
X	FILE *distfile;		/* file of distribution codes */
X	char distline[MAX_LLEN];/* buffer line for distribution */
X	ngrec *group;
X	int dlevel;		/* distribution level */
X	extern char *news_lib_dir;
X	extern bool wants_dist;	/* are distributions even desired? */
X
X	distfile = dfopen( dotdir, DISTLIST, "r" );
X	if( !distfile )
X		distfile = dfopen( news_lib_dir, DISTLIST, "r" );
X	if( !distfile )
X		distfile = dfopen( ".", DISTLIST, "r" );
X	if( !distfile ) {
X		warning( 1, "No distribution file %s\n", DISTLIST );
X		wants_dist = FALSE;
X		return;
X		}
X
X	while( fgets( distline, sizeof(distline), distfile ) ) {
X		if( distline[0] == '#' )
X			continue;	/* comment */
X		if( sscanf( distline, "%s %d", ng, &dlevel ) != 2 )
X			continue;
X		group = (ngrec *)add_rec(ng_base, ng, AR_CREATE);
X		if( group->ngnumber == 0 )
X			group->ngnumber = group_count++;
X		group->distribution = dlevel;
X		}
X	fclose( distfile );
X
X	for( group = (ngrec *)first_rec(ng_base); group;
X					group =next_rec(ng_base,group) )
X		set_distribution( group );
X}
X
X/* Default distribution if we can't figure it out */
X/* It's hard to say what this should be or what meaning it has.  The
X   user can extern it and set it, I guess */
X 
Xint def_distribution = 10;
X
X/* figure out and set the distribution for a given group */
X
Xset_distribution( group )
Xngrec *group;
X{
X	ngrec *drec;			/* distribution record */
X	char gname[MAX_NGLEN];		/* buffer for placing group name */
X	char *p;
X
X	if( group->distribution == 0 ) {
X		strcpy( gname, group->key );
X		p = gname + strlen(gname) - 1;	/* point at end */
X		/* loop through stripping off suffixes, and testing to see
X		   if there is a distribution number for them */
X		while( p > gname ) {
X			if( *p == '.' ) {
X				*p = 0;
X				if( (drec = (ngrec*)get_rec( ng_base, gname)) &&
X						drec->distribution > 0) {
X					group->distribution =drec->distribution;
X					return;
X					}
X				}
X			p--;
X			}
X		/* set default as we found nothing */
X		group->distribution = def_distribution;
X		}
X
X		
X}
END_OF_FILE
if test 6763 -ne `wc -c <'group.c'`; then
    echo shar: \"'group.c'\" unpacked with wrong size!
fi
# end of 'group.c'
fi
if test -f 'userlib.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'userlib.c'\"
else
echo shar: Extracting \"'userlib.c'\" \(8132 characters\)
sed "s/^X//" >'userlib.c' <<'END_OF_FILE'
X
X
X/*
X * Routines and externals for access by the user program
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#include "nl.h"
X#include <ctype.h>
X
X/* externals */
X
Xchar *article_filename;			/* the filename of the article, if
X						available */
X
Xint score;				/* the score the article got */
X
Xint white_compress = FALSE;		/* compress white space */
Xint paragraph_scan = FALSE;		/* scan text as paragraphs */
Xint preserve_case = FALSE;		/* don't lower case text */
X
Xchar *signature_start = "^-- *$";	/* start of signature */
X
Xchar *include_prefix = "^[ \t]*[:>#%]";	/* included text */
X
X
Xchar *
Xarr_string( thear, type, dex )
Xarray *thear;
Xdtype type;		/* array type */
Xint dex;		/* dex into array */
X{
X
X	switch( type ) {
X		case T_NEWSGROUP:
X			return ngn(thear->vals[dex].uinteger);
X		case T_USERNAME:
X			return thear->vals[dex].uusername->emailname;
X		case T_STRING:
X			return thear->vals[dex].ustring;
X		default:
X			return "foo";
X		}
X}
X
X/*
X * Now we have the various "in" routines which implement the in
X * operator.  There is acutally a wide cross product of such routines,
X * based on the fact that you can search for a variety of types in
X * a variety of types.  Scalars and arrays can be searched for in arrays
X * and databases, and the various semi-compatible string-like types
X * are mostly dealt with
X */
X
X
Xarr_in_db( arr, db )
Xarray *arr;			/* array of string like objects */
Xdbptr db;			/* database to look in */
X{
X	register int i;
X	char *str;
X
X	if( !arr )
X		return FALSE;
X
X	for( i = 0; i < arr->arsize; i++ ) {
X		if( str_in_db( arr_string(arr,arr->artype,i), db ) )
X			return TRUE;
X		}
X	return FALSE;
X}
X
Xstr_in_db( str, db )
Xchar *str;			/* string to check for */
Xdbptr db;			/* database to look in */
X{
X	register userdb *rec;
X	extern long time_now;
X
X	if( rec = (userdb*)get_rec( db, str ) ) {
X		rec->access_date = time_now;
X		return TRUE;
X		}
X	 else
X		return FALSE;
X}
X
Xarr_in_arr( ar1, ar2 )
Xarray *ar1;			/* values to look for */
Xarray *ar2;			/* array to look in */
X{
X	/* we can search for ngs, usernames and strings in strings */
X	int i1, i2;			/* index vars */
X	dtype ty1, ty2;
X
X	if( !ar1 || !ar2 )
X		return FALSE;
X
X	ty1 = ar1->artype;
X	ty2 = ar2->artype;
X
X	if( ty1 == ty2 ) {
X		for( i1 = 0; i1 < ar1->arsize; i1++ )
X			for( i2 = 0; i2 < ar2->arsize; i2++ )
X				switch( ty1 ) {
X					case T_INTEGER:
X					case T_NEWSGROUP:
X						if( ar1->vals[i1].uinteger ==
X							ar2->vals[i2].uinteger)
X							return TRUE;
X						break;
X					case T_DATE:
X						if( ar1->vals[i1].udate ==
X							ar2->vals[i2].udate)
X							return TRUE;
X						break;
X					case T_STRING:
X						if(strcmp(ar1->vals[i1].ustring,
X							ar2->vals[i2].ustring)
X								== 0 )
X							return TRUE;
X						break;
X					case T_USERNAME:
X						if(strcmp(ar1->vals[i1].uusername->emailname,
X							ar2->vals[i2].uusername->emailname)
X								== 0 )
X							return TRUE;
X						break;
X						}
X		}
X	 else {
X		for( i1 = 0; i1 < ar1->arsize; i1++ )
X			for( i2 = 0; i2 < ar2->arsize; i2++ )
X				if( strcmp(arr_string(ar1,ty1,i1),
X						arr_string(ar2,ty2,i2)) == 0 )
X					return TRUE;
X		}
X	return FALSE;
X}
X
Xint_in_arr( val, arr )
Xint val;			/* value to search for */
Xarray *arr;			/* array to search in */
X{
X	int i;
X	if( !arr )
X		return FALSE;
X	for( i = 0; i < arr->arsize; i++ )
X		if( val == arr->vals[i].uinteger )
X			return TRUE;
X	return FALSE;
X}
X
X
Xdate_in_arr( val, arr )
Xdatehold val;			/* value to search for */
Xarray *arr;			/* array to search in */
X{
X	int i;
X	if( !arr )
X		return FALSE;
X	for( i = 0; i < arr->arsize; i++ )
X		if( val == arr->vals[i].udate )
X			return TRUE;
X	return FALSE;
X}
X
Xng_in_arr( val, arr )
Xnewsgroup val;			/* value to search for */
Xarray *arr;			/* array to search in */
X{
X	int i;
X	dtype ty;
X	if( !arr )
X		return FALSE;
X
X	ty = arr->artype;
X	if( ty == T_NEWSGROUP ) {
X		for( i = 0; i < arr->arsize; i++ )
X			if( val == arr->vals[i].uinteger )
X				return TRUE;
X		}
X	 else {
X		char *ngname;
X		ngname = ngn(val);
X		for( i = 0; i < arr->arsize; i++ )
X			if( cleq( ngname, arr_string(arr,ty,i) ) )
X				return TRUE;
X		}
X	return FALSE;
X}
X
Xstr_in_arr( val, arr )
Xchar * val;			/* value to search for */
Xarray *arr;			/* array to search in */
X{
X	int i;
X	dtype ty;
X	if( !arr )
X		return FALSE;
X
X	ty = arr->artype;
X	for( i = 0; i < arr->arsize; i++ )
X		if( strcmp( val, arr_string(arr,ty,i) ) == 0 )
X			return TRUE;
X	return FALSE;
X}
X
Xname_in_arr( val, arr )
Xusername *val;			/* value to search for */
Xarray *arr;			/* array to search in */
X{
X	int i;
X	char *mailn;		/* mail name */
X	dtype ty;
X	if( !arr )
X		return FALSE;
X
X	ty = arr->artype;
X
X	mailn = val->emailname;
X	for( i = 0; i < arr->arsize; i++ )
X		if( cleq( mailn, arr_string(arr,ty,i)) )
X			return TRUE;
X	return FALSE;
X}
X
X/* Functions to parse strings with dots */
X
X/* Return the leftmost part of the string, bound by 'num' dots */
X
Xchar *
Xleft( str, num )
Xchar *str;		/* string to parse */
Xint num;		/* how many sections to return */
X{
X	int i;
X	char *p;	/* scan through for dots */
X	char *ret;
X
X	p = str;
X
X	for( i = 0; i < num; i++ ) {
X		p = strchr( p, '.' );
X		if( p )
X			p++;
X		 else
X			break;
X		}
X	if( p && p > str) {
X		int len;
X		len = (p - str) - 1;
X		ret = temp_alloc( len + 1 );
X		strncpy( ret, str, len );
X		ret[len] = 0;
X		return ret;
X		}
X	 else
X		return num > 0 ? str : "";
X}
X
X/* return the rightmost substring based on dots */
X
Xchar *
Xright( str, num )
Xchar *str;
Xint num;
X{
X	int i;
X	if( num < 0 )
X		return "";
X
X	for( i = strlen(str)-1; i >= 0; i-- )
X		if( str[i] == '.' )
X			if( --num <= 0 )
X				return str + i + 1;
X	return str;
X}
X
X/* return the domain from a mail address */
X
Xchar *
Xdomain( str )
Xchar *str;
X{
X	char *at;
X	if( at = strchr( str, '@' ) )
X		return at+1;
X	 else
X		return str;
X}
X
X/* distribution level for a newsgroup */
X
Xint
Xdlevel( ng )
Xnewsgroup ng;
X{
X	return ngarray[ng]->distribution;
X}
X
X/* check for the presence of a newsgroup.  This is generated by the
X * 'is' operator 
X */
X
Xgquery( n )
Xregister newsgroup n;
X{
X	extern array *newsgroups;
X	register int i;
X	int ncount;
X
X	ncount = newsgroups->arsize;
X	for( i = 0; i < ncount; i++ )
X		if( newsgroups->vals[i].uinteger == n )
X			return TRUE;
X	return FALSE;
X}
X
X/* Concatenate two strings together and leave the result in new
X   temporary memory */
X
Xchar *
Xconcat( s1, s2 )
Xchar *s1;
Xchar *s2;
X{
X	unsigned int len, l1;
X	char *ret;
X
X	l1 = strlen(s1);
X	len = l1 + strlen(s2);
X	ret = temp_alloc( len+1 );
X	strcpy( ret, s1 );
X	strcpy( ret+l1, s2 );
X	return ret;
X}
X
X/* Remove any level of "Re: " from the front of a string, most probably
X * a subject line
X */
X
X
Xchar *
Xdrop_re( str )
Xchar *str;
X{
X	register char *p;
X
X	for( p = str; *p; p++ ) {
X		if( *p == ' ' )
X			continue;
X		 else if( lowerlet(p[0]) == 'r' && lowerlet(p[1]) == 'e' &&
X				p[2] == ':' )
X			p +=2;
X		 else
X			break;
X		}
X	return p;
X}
X
Xchar *
Xlower( str )
Xchar *str;
X{
X	char *ret;
X	ret = temp_string( str );
X	lowercase( ret );
X	return ret;
X}
X
Xint
Xnamed_group( n )
Xnewsgroup n;
X{
X	extern int user_gcount;
X	return n > 0 && n <= user_gcount;
X}
X
X/* is the group in the .newsrc */
Xint
Xnewsrc_group( n )
Xnewsgroup n;
X{
X	return !!(ngarray[n]->gflag & GF_RCGROUP);
X}
X
X/* string compare */
X
Xstr_eq( a, b )
Xchar *a, *b;		/* two strings to compare */
X{
X	if( a == b )
X		return TRUE;
X	if( !a || !b )
X		return FALSE;		/* one is nil string */
X	return strcmp( a, b ) == 0;
X}
X
X/* the string, missing N chars from the front */
X
Xchar *
Xclipfront( str, num )
Xchar *str;
Xint num;
X{
X	int len;
X	len = strlen(str);
X	if( num < len )
X		return str + num;
X	 else
X		return "";
X}
X
X/* The leftmost N chars of a string */
Xchar *
Xleftmost( str, num )
Xchar *str;
Xint num;
X{
X	int len;
X	char *ret;
X
X	len = strlen(str);
X	if( num >= len )
X		return str;
X	 else {
X		ret = temp_alloc( num + 1 );
X		strncpy( ret, str, num );
X		ret[num] = 0;
X		return ret;
X		}
X}
END_OF_FILE
if test 8132 -ne `wc -c <'userlib.c'`; then
    echo shar: \"'userlib.c'\" unpacked with wrong size!
fi
# end of 'userlib.c'
fi
if test -f 'whoami.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'whoami.c'\"
else
echo shar: Extracting \"'whoami.c'\" \(8532 characters\)
sed "s/^X//" >'whoami.c' <<'END_OF_FILE'
X
X/*
X * This file returns site-dependent and user-dependent information
X * It may be necessary to edit and ajust this file on different variants
X * of Unix, although we have tried to deal with many of them.  I wish
X * they wouldn't keep adding more.
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#include "nl.h"
X
X#include <pwd.h>
X
Xchar *homedir;		/* home directory */
Xchar *dotdir;		/* dot directory */
Xchar *userid;		/* the userid of the user */
Xchar *logname;		/* login name */
Xchar *fullname;		/* full name */
Xchar *sitename;		/* short site name of my site */
Xchar *my_domain;	/* domain name of my site */
Xchar *my_mail_address;	/* my internet mail address */
Xchar *newsrcname = 0;	/* name for .newsrc file */
Xchar *lasname = 0;	/* last article seen file */
Xchar *temprc = 0;	/* tempory newsrc to write out */
X
Xchar *news_lib_dir;
Xchar *news_spool_dir;
X
Xbool normal_newsrc = FALSE;	/* are we using the standard newsc file? */
X
X/* There is no reliable method for getting the full mail domain name that
X   is used in mail addresses.  The only way is for the person who compiled
X   the program to have set the proper defines.  Some day this routine will
X   do something.    We *can* get the site name, but that's not all that
X   useful to us for checking mail addresses, and it's a messy, multi-ifdef
X   procedure that we don't need to have around unless it's necessary. */
X
Xchar *
Xget_domain(site)
Xchar *site;
X{
X#ifdef MAILDOMAIN
X	return MAILDOMAIN;
X#else
X	char *ret;
X	ret = perm_alloc( strlen(site) + 5 + 1 );
X	/* default domain of uucp is quite probably wrong */
X	sprintf( ret, "%s.%s", site, DEFDOMAIN );
X	return ret;
X#endif
X}
X
X#ifdef HAS_UNAME
X#include <sys/utsname.h>
X#endif
X
Xchar *
Xget_sitename()
X{
X	char snbuf[50];
X	FILE *sysid;
X#ifdef GETHOSTNAME
X	gethostname(snbuf,sizeof(snbuf));
X	return allocstring(snbuf);
X#else
X	if( sysid = fopen( "/etc/systemid", "r" ) ) {
X		int len;
X		if( fgets( snbuf, sizeof(snbuf), sysid ) ) {
X			fclose( sysid );
X			len = strlen( snbuf );
X			if( len && snbuf[len-1] == '\n' )
X				snbuf[len-1] = 0;
X			return allocstring( snbuf );
X			}
X		fclose( sysid );
X		}
X# ifdef HAS_UNAME
X	{
X	struct utsname nam;
X	uname( &nam );
X	return allocstring( nam.nodename );
X	}
X# else /*HAS_UNAME*/
X
X#  ifndef sysname
X#  define sysname "unknown"
X	warning( 2, "System name is unknown.\n" );
X#  endif
X	return sysname;		/* often defined in whoami.h */
X	
X# endif /*HAS_UNAME*/
X
X#endif /*GETHOSTNAME*/
X
X}
X
X/* concatenate three strings together in a newly allocated buffer */
X/* used mostly to add a directory name, slash and filename together */
X
Xchar *
Xcatnames( s1, s2, s3 )
Xchar *s1, *s2, *s3;
X{
X	unsigned int len, l1, l2;
X	char *ret;
X
X	l1 = strlen(s1);
X	l2 = strlen(s2);
X	len = l1 + l2 + strlen(s3);
X	ret = perm_alloc( len+1 );
X	strcpy( ret, s1 );
X	strcpy( ret+l1, s2 );
X	strcpy( ret+l1+l2, s3 );
X	return ret;
X}
X
Xinit_whoami()
X{
X	int uid;
X	struct passwd *pwent, *getpwuid();
X	extern char *getlogin();
X	extern char *getenv AC((char *));
X	char buf[MAX_LLEN];
X
X
X	if( !( homedir = getenv( "HOME" ) ) )
X		homedir = getenv( "LOGDIR" );
X
X	fullname = getenv( "NAME" );
X
X	uid = getuid();
X
X	if( pwent = getpwuid( uid ) ) {
X		userid = allocstring( pwent->pw_name );
X		if( !fullname ) {
X			char *p;
X			/* theory is the comma should only be a delimiter
X			   on certain systems */
X			p = strchr( pwent->pw_gecos, ',' );
X			if( p )
X				*p = 0;
X			fullname = allocstring( pwent->pw_gecos );
X			/* theory is that '&' should map to the userid but
X			   I can't imagine why in hell that should be */
X			}
X		if( !homedir )
X			homedir = allocstring( pwent->pw_dir );
X		}
X	 else 	/* give up and use a dummy name */
X		userid = "fbaggins";
X
X	if( !( logname = getenv("USER") ) )
X		if( !( logname = getenv( "LOGNAME" ) ) )
X			if( logname = getlogin() )
X				logname = allocstring( logname );
X			 else
X				logname = userid;
X
X	/* Now get the site name */
X	sitename = get_sitename();
X	my_domain = get_domain( sitename );
X	sprintf( buf, "%s@%s", userid, my_domain );
X	my_mail_address = allocstring( buf );
X
X	/* if we still don't have a full name, use the mail address */
X	if( !fullname )
X		fullname = my_mail_address;
X
X	if( !dotdir )
X		if( !( dotdir = getenv( "DOTDIR" ) ) )
X			dotdir = homedir;
X	if( !newsrcname ) {
X		if( !( newsrcname = getenv( "NEWSRC" ) ) ) {
X			newsrcname = catnames( dotdir, "/", ".newsrc" );
X			normal_newsrc = TRUE;
X			}
X		}
X	if( !lasname )
X		lasname = catnames( newsrcname, "las", "" );
X	temprc = catnames( newsrcname, "new", "" );
X
X	/* set up news library and spool directories */
X	if( !news_lib_dir )
X		news_lib_dir = NEWSLIB;
X	if( !news_spool_dir )
X		news_spool_dir = NEWSSPOOL;
X}
X
X/* If your system has no getlogin, define this null one.  It's not important,
X   unless you can plug in some other username finding routine.  This is
X   the routine of last resort.  If it returns null, as this dummy does,
X   you get a dummy name.  That's ok.  Ther user simply can't use the
X   "my_mail_address" variable in this case -- they have to enter it by
X   hand. */
X
X#ifndef GETLOGIN
X
Xchar *
Xgetlogin()
X{
X	return (char *)0;
X}
X#endif
X
Xdatehold time_now;		/* the time we ran */
Xint zone_offset;		/* offset from GMT in minutes */
X#include <time.h>
X
X/* Find the time, and the timezone offset from GMT in minutes.  The way
X   to find this out varies from OS variant to OS variant, but I believe
X   I have a reasonably portable way below. */
X
Xinit_time()
X{
X	struct tm *tbuf;
X	long day1, time();
X
X	time_now = time((long*)0);
X	/* the most portable way I currently know to get the time zone
X	   adjustment is to get the time of day for Midnight on the second
X	   day of the epoch from the localtime function.  Some systems
X	   require an init routine to be called to set up time zone stuff
X	   properly, and that can be inserted here.  It's worth nothing
X	   that getting this value right isn't super crucial.  It has
X	   only a minor effect on the parsing of dates, and usually dates
X	   aren't even bothered with by newsclip programs */
X
X	day1 = 60*60*24;		/* one day of seconds */
X
X	tbuf = localtime( &day1 );
X	/* In this case, it's minutes WEST of GMT, which is positive for
X	   us western guys */
X	if( tbuf->tm_hour >= 12 )
X		zone_offset = 60 * (24-tbuf->tm_hour) - tbuf->tm_min;
X	 else
X		zone_offset = -60 * tbuf->tm_hour - tbuf->tm_min;
X}
X
X/* Some routines missing from BSD Unix */
X
X#ifdef NEED_SLIB
X
X
X/* strpbrk, like strchr, but searches for any one of a set of chars */
X
Xchar *
Xstrpbrk( str, lchars )
Xchar *str;		/* string to search in */
Xchar *lchars;
X{
X	register char *p;
X
X	for( p = str; *p; p++ )
X		if( strchr( lchars, *p ) )
X			return p;
X	return (char *)0;
X}
X
X/* strtok - string parsing routine */
X
Xstatic char *tokstring;
X
Xchar *
Xstrtok( string, delims )
Xchar *string;		/* string to parse, or null to continue old string */
Xchar *delims;		/* delimiters to parse on */
X{
X	register char *p, *del;		/* scanning pointer */
X	char *start;
X
X	p = string ? string : tokstring;
X
X	/* skip over initial delimiters */
X	while( *p ) {
X		for( del = delims; *del; del++ )
X			if( *del == *p )
X				goto nextc;
X		break;		/* not a delim */
X		nextc: p++;
X		}
X			
X	if( !*p )
X		return NULL;
X
X	start = p;
X
X	/* go to next delimiter */
X	while( *p ) {
X		for( del = delims; *del; del++ )
X			if( *del == *p )
X				goto tok_done;
X		p++;
X		}
X	tok_done:
X	/* store 0 on top of delimiter */
X	if( *p )
X		*p++ = 0;
X	/* save in static for next call */
X	tokstring = p;
X
X	return start;
X}
X
X/*
X * The following is provided for those people who do not have strcspn() in
X * their C libraries.  They should get off their butts and do something
X * about it; at least one public-domain implementation of those (highly
X * useful) string routines has been published on Usenet.
X */
X/*
X * strcspn - find length of initial segment of s1 consisting entirely
X * of characters not from s2  (Henry Spencer)
X */
X
Xstatic int
Xstrcspn(s1, s2)
Xchar *s1;
Xchar *s2;
X{
X	register char *scan1;
X	register char *scan2;
X	register int count;
X
X	count = 0;
X	for (scan1 = s1; *scan1 != '\0'; scan1++) {
X		for (scan2 = s2; *scan2 != '\0';)	/* ++ moved down. */
X			if (*scan1 == *scan2++)
X				return(count);
X		count++;
X	}
X	return(count);
X}
X#endif /*NEED_SLIB*/
END_OF_FILE
if test 8532 -ne `wc -c <'whoami.c'`; then
    echo shar: \"'whoami.c'\" unpacked with wrong size!
fi
# end of 'whoami.c'
fi
echo shar: End of archive 4 \(of 15\).
cp /dev/null ark4isdone
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