[news.software.notes] Notes and mod.map and mod.recipes

rs@mirror.TMC.COM (Rich Salz) (03/10/87)

This package will let Notesfiles sites unpack and save the articles
in mod.map and mod.recipes using the standard News scripts.
The mod.recipes software is posted regularly by that moderator; if
you need uuhosts, contact your nearest mod.sources archive, or send
mail to mirror!sources-request.

I hope folks find this useful; please bang on it, try not to enhance
it too much, and send me bugs.  In a month or so I'll publish it
in mod.sources.  There's no README, but the manpage is a good one;
before compiling, see the parameters in the Makefile (qdnnf.mk) and the
source.
	/rich $alz

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If this archive is complete, you will see the message:
#		"End of shell archive."
# Contents:  qdnnf.mk qdnnf.1 qdnnf.c qdnnf.how qdnnf.rckeep
# Wrapped by rs@mirror on Tue Mar 10 12:56:14 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"qdnnf.mk\" \(671 characters\)
if test -f qdnnf.mk ; then 
  echo shar: Will not over-write existing file \"qdnnf.mk\"
else
sed "s/^X//" >qdnnf.mk <<'END_OF_qdnnf.mk'
X##
X##  MAKEFILE FOR QDNNF
X##  $Header: qdnnf.mk,v 2.0 87/03/10 12:55:00 rs Rel $
X##
X
X##  Edit appropriately; the -I is for the 4.2BSD <time.h> mistake.
XCFLAGS	= -O -I/usr/include/sys
XLINTF	= -p -hba -I/usr/include/sys
XDEST	= /usr/bin/qdnnf
XMAN	= /usr/man/man1/qdnnf.1
X
Xall:		qdnnf qlint
X
Xinstall:	all
X	@echo You better be the super-user...
X	rm -f $(DEST)
X	cp qdnnf $(DEST)
X	strip $(DEST)
X	chmod 755 $(DEST)
X	cp qdnnf.1 $(MAN)
X
Xclean:
X	rm -f foo core a.out qdnnf tags qlint
X
Xqdnnf:		qdnnf.c
X	$(CC) $(CFLAGS) -o qdnnf qdnnf.c
X
Xqlint:		qdnnf
X	lint $(LINTF) qdnnf.c >qlint
X
XFILES	= qdnnf.mk qdnnf.1 qdnnf.c qdnnf.how qdnnf.rckeep
XQSHAR:		clean $(FILES)
X	shar $(FILES) >QSHAR
END_OF_qdnnf.mk
if test 671 -ne `wc -c <qdnnf.mk`; then
    echo shar: \"qdnnf.mk\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"qdnnf.1\" \(2688 characters\)
if test -f qdnnf.1 ; then 
  echo shar: Will not over-write existing file \"qdnnf.1\"
else
sed "s/^X//" >qdnnf.1 <<'END_OF_qdnnf.1'
X.TH QDNNF 1 LOCAL
X\" $Header: qdnnf.1,v 2.0 87/03/10 12:54:31 rs Rel $
X.SH NAME
Xqdnnf \- ``quick-and-dirty'' notes to news filter
X.SH SYNOPSIS
X.B qdnnf
Xnotesfile
X.SH DESCRIPTION
X.I Qdnnf
Xis a quick hack that lets
X.SM NOTESFILES
Xsites use the programs that are available to automatically operate on
X.SM USENET
Xnews articles.
XExamples of this are John Quarterman's
X.I uuhosts
Xpackage to process
X.I mod.map
Xdata and Brian Reid's
X.I mod.recipes
Xsoftware.
X.PP
XThe program expects a single argument, the name of the notesfile being
Xsent.
X.I Qdnnf
Xthen parses its standard input, expecting to receive ``canonical'' notesfile
Xarticles, as generated by the
X.IR nfxmit (1)
Xprogram.
X.I Qdnnf
Xsplits the incoming stream into individual news-style articles with
Xthe following minimal headers:
X.RS
X.nf
XPath: qdnnf!NOTES!mirror.UUCP
XFrom: sources-request@mirror.UUCP
XNewsgroups: mod.sources
XSubject: INFO01:  Mod.sources index and arch
XMessage-ID: <169@mirror.UUCP>
XDate: 8 Jul 86 21:24:48 GMT
XLines: 343
X.fi
X.RE
X.PP
XEach article is then piped (via
X.IR popen (3))
Xinto a command as directed by the file
X.IR qdnnf.how .
XThe format of this file resembles that of the standard
X.I net.how
Xfile in that blank lines and lines starting with a pound sign
X.RI (`` # '')
Xare ignored.
XData lines consist of two fields, separated by a colon:  the notesfile name,
Xand the command line to execute.
XFor example, most
X.I qdnnf.how
Xfiles will look like this:
X.RS
X.nf
Xmod.map:uuhosts -x
Xmod.recipes:rckeep /usr/pub/recipes
X# Archive sources
Xmod.sources:archsrc \-n mod.sources \-p
X.fi
X.RE
XIf no line is found for a notesfile, a site-specific default may be used.
X.SH INSTALLATION
XAfter editing the source and installing the program in a convenient
Xdirectory you must arrange for it to receive the data; this is a
Xtwo-step process.
XFirst, you must tell
X.SM NOTES
Xhow to send the new articles to
X.IR qdnnf .
XThis is typically done by adding the following line to your
X.I net.how
Xfile:
X.RS
Xqdnnf:x:::qdnnf %s
X.RE
XSecond, you must arrange for the transmissions to happen; this is
Xusually done by adding a line like the following to your crontab file (see
X.IR cron (8)):
X.RS
X0 20 * * * nfxmit -a -dqdnnf mod.map mod.sources mod.recipes
X.RE
X.SH FILES
X.ta \w'qdnnf.how   'u
Xnet.how	Must be edited so that data is sent to this program.
X.br
Xqdnnf.how	List of commands to call for each notesfile.
X.SH "SEE ALSO"
XThe notes documentation.
X.SH BUGS
XAs distributed,
X.I qdnnf
Xdoes not run ``setuid'' or provide any security mechanisms; this
Xis the responsibility of the programs it calls.
X.br
XThe program ignores any error codes returned by the programs it calls;
Xthis is a features.
X.SH AUTHOR
XRich $alz (mirror!rs, rs@mirror.TMC.COM)
END_OF_qdnnf.1
if test 2688 -ne `wc -c <qdnnf.1`; then
    echo shar: \"qdnnf.1\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"qdnnf.c\" \(6352 characters\)
if test -f qdnnf.c ; then 
  echo shar: Will not over-write existing file \"qdnnf.c\"
else
sed "s/^X//" >qdnnf.c <<'END_OF_qdnnf.c'
X/*
X**  QDNNF
X**  Quick and dirty notes to news filter.
X**
X**  This program is a serious hack; I wrote it so that notes sites
X**  could use John Quarterman's uuhosts package, and my archsrc program
X**  without bringing up a complete news interface.  It gets nfxmit
X**  output output on stdin, and reformats it so that it looks a little
X**  like a news article.  Said articles are then piped through a
X**  command specified on a per-notesfile basis.
X**
X**  When I wrote this program, here is what nfxmit output looked like
X**  for a base note:
X**	N:system:unique_id:response_count
X**	title
X**	author_name:author_uid:author_site:
X**	year:month:day:hour:minute:seconds_since_1970
X**	flags:char_count
X**	...text here, char_count bytes of it...
X**  And here is what it looked like for a response:
X**	R:original_system:original_system_id:system:unique_id:number
X**	author_name:author_uid:author_site:
X**	year:month:day:hour:minute:seconds_since_1970
X**	flags:char_count
X**	...text here, char_count bytes of it...
X*/
X#include <stdio.h>
X#include <sys/types.h>
X#include <time.h>
X#ifndef	lint
Xstatic char RCS[] = "$Header: qdnnf.c,v 2.0 87/03/10 12:54:43 rs Rel $";
X#endif	/* lint */
X
X
X/*
X**  Compilation controls.  Edit as necessary.
X*/
X#define IDX		index		/* Maybe strchr?		*/
X#define RDX		rindex		/* Maybe strrchr?		*/
X#define TIMETYPE	time_t		/* Maybe long?			*/
X#define LENGTH		256		/* Maximum line length		*/
X#define DATESIZE	25		/* Don't touch this one!	*/
X#define CMDFILE		"/usr/spool/notes/.utilities/qdnnf.how"
X/* #define DEFAULT_CMD	"cat"		/* For debugging only		*/
X
X
X/*
X**  Linked in later.
X*/
Xextern FILE		*popen();
Xextern TIMETYPE		 time();
Xextern long		 atol();
Xextern char		*IDX();
Xextern char		*RDX();
Xextern char		*mktemp();
Xextern char		*strcpy();
X
X
X/*
X**  Return a pointer to the i'th colon-separated field in Line.  Returns
X**  a pointer to static space which is overwritten on subsequent calls.
X*/
Xchar *
XField(i, Line)
X    register int	 i;
X    char		*Line;
X{
X    static char		 buff[LENGTH];
X    register char	*p;
X    register char	*q;
X
X    /* To get field i, skip i - 1 colons. */
X    for (p = Line; --i > 0 && (p = IDX(p, ':')); p++)
X	;
X
X    if (p == NULL) {
X	/* Oops! */
X	p = "";
X	q = NULL;
X    }
X    else if (q = IDX(p, ':'))
X	*q = '\0';
X    (void)strcpy(buff, p);
X    if (q)
X	*q = ':';
X
X    return(buff);
X}
X
X
X/*
X**  Convert a time to official format.  That is, convert:
X**	Tue Feb 10 10:11:12 1960  to  10 Feb 60 10:11:12 GMT
X**	.123456789.123456789.123      .123456789.123456789.1
X**  Based on the "arpadate" routine from the news sources.  Returns a
X**  pointer to static space which is overwritten on subsequent calls.
X*/
Xchar *
XDate(TmTime)
X    struct tm		*TmTime;
X{
X    static char		 buff[DATESIZE];
X    register char	*p;
X    register char	*q;
X    register char	*r;
X    register int	 i;
X
X    p = buff;
X    q = asctime(TmTime);
X    if (q[8] != ' ')
X	*p++ = q[8];
X    *p++ = q[9];
X    *p++ = ' ';
X    *p++ = q[4];
X    *p++ = q[5];
X    *p++ = q[6];
X    *p++ = ' ';
X    *p++ = q[22];
X    *p++ = q[23];
X    *p++ = ' ';
X    for (r = &q[11], i = 8; --i >= 0; )
X	*p++ = *r++;
X    *p = '\0';
X    return(buff);
X}
X
X
Xmain(ac, av)
X    int			 ac;
X    register char	*av[];
X{
X    static char		 TMP[] = "/tmp/qdnnfXXXXXX";
X#ifdef 2_10_NEWS
X    static char		 Version[] = "QDNNF $Revision: 2.0 $";
X#endif /* 2_10_NEWS */
X    register FILE	*F;
X    register FILE	*Out;
X    register char	*p;
X    register long	 Chars;
X    register long	 Lines;
X    register int	 C;
X    TIMETYPE		 Time;
X    char		 Combuf[LENGTH];
X    char		 Line1[LENGTH];
X    char		 Title[LENGTH];
X    char		 Line2[LENGTH];
X    char		 Line3[LENGTH];
X    char		 buff[LENGTH];
X    char		 Now[DATESIZE];
X
X    /* Parse JCL and set up. */
X    if (ac != 2) {
X	perror("Bad calling sequence");
X	exit(1);
X    }
X    if (p = RDX(av[0], '/'))
X	av[0] = ++p;
X    Time = time((TIMETYPE *)NULL);
X    (void)strcpy(Now, Date(gmtime(&Time)));
X
X    /* Find the command to send. */
X    Combuf[0] = '\0';
X    if (F = fopen(CMDFILE, "r")) {
X	/* Read lines until we find the line for this notesfile. */
X	while (fgets(buff, sizeof buff, F) && !Combuf[0])
X	    if (p = IDX(buff, '\n')) {
X		*p = '\0';
X		/* If not a comment line, see if it's the one we want. */
X		if (buff[0] && buff[0] != '#' && (p = IDX(buff, ':'))) {
X		    *p++ = '\0';
X		    if (strcmp(buff, av[1]) == 0)
X			(void)strcpy(Combuf, p);
X		}
X	    }
X	(void)fclose(F);
X    }
X#ifdef	DEFAULT_CMD
X    if (Combuf[0] == '\0')
X	(void)strcpy(Combuf, DEFAULT_CMD);
X#else
X    if (Combuf[0] == '\0') {
X	perror("No command found");
X	exit(1);
X    }
X#endif	/* DEFAULT_CMD */
X
X    /* Read stuff on stdin. */
X    while (fgets(Line1, sizeof Line1, stdin)) {
X	/* Get header lines. */
X	if (Line1[0] == 'N'
X	 && fgets(Title, sizeof Title, stdin) && (p = IDX(Title, '\n')))
X	    *p = '\0';
X	if (fgets(Line2, sizeof Line2, stdin) && (p = IDX(Line2, '\n')))
X	    *p = '\0';
X	if (fgets(Line3, sizeof Line3, stdin) && (p = IDX(Line3, '\n')))
X	    *p = '\0';
X
X	/* Only thing worthwhile in Line4 is the length, so just get that. */
X	if (fgets(buff, sizeof buff, stdin) && (p = IDX(buff, '\n')))
X	    *p = '\0';
X	Chars = atol(Field(2, buff));
X
X	/* Get note body, and count lines therein. */
X	F = fopen(mktemp(TMP), "w");
X	for (Lines = 0L; --Chars >= 0 && (C = getchar()) != EOF; ) {
X	    (void)putc(C, F);
X	    if (C == '\n')
X		Lines++;
X	}
X	(void)fclose(F);
X
X	/* Crank up the command and dump the headers. */
X	if ((Out = popen(Combuf, "w")) == NULL)
X	    exit(1);
X#ifdef 2_10_NEWS
X	fprintf(Out, "Relay-Version: %s\n", Version);
X#endif /* 2_10_NEWS */
X	fprintf(Out, "Path: %s!NOTES!%s\n", av[0], Field(2, Line1));
X	fprintf(Out, "From: %s@", Field(1, Line2));
X	fprintf(Out, "%s\n", Field(3, Line2));
X	fprintf(Out, "Newsgroups: %s\n", av[1]);
X	fprintf(Out, "Subject: %s\n", Title);
X	fprintf(Out, "Message-ID: <%s@",
X		     Field(Line1[0] == 'N' ? 3 : 5, Line1));
X	fprintf(Out, "%s>\n", Field(3, Line2));
X	Time = atol(Field(6, Line3));
X	fprintf(Out, "Date: %s GMT\n", Date(gmtime(&Time)));
X#ifdef 2_10_NEWS
X	fprintf(Out, "Date-Received: %s GMT\n", Now);
X#endif /* 2_10_NEWS */
X	fprintf(Out, "Lines: %ld\n\n", Lines);
X
X	/* Dump the text. */
X	for (F = fopen(TMP, "r"); fgets(buff, sizeof buff, F); )
X	    fprintf(Out, "%s", buff);
X	(void)fclose(F);
X
X	/* Clean up and go back for more. */
X	(void)pclose(Out);
X	(void)unlink(TMP);
X    }
X
X    /* That's all she wrote. */
X    exit(0);
X}
END_OF_qdnnf.c
if test 6352 -ne `wc -c <qdnnf.c`; then
    echo shar: \"qdnnf.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"qdnnf.how\" \(459 characters\)
if test -f qdnnf.how ; then 
  echo shar: Will not over-write existing file \"qdnnf.how\"
else
sed "s/^X//" >qdnnf.how <<'END_OF_qdnnf.how'
X##
X##  Sample "qdnnf.how" file.
X##  You will probably have to change the paths given below.
X##  $Header: qdnnf.how,v 2.0 87/03/10 12:54:52 rs Rel $
X
X##  Keep the cookbook.
Xmod.recipes:/usr/spool/recipes/bin/rckeep
X
X##  Keep the map.
Xmod.map:/usr/local/bin/uuhosts -x
X
X##  Archive sources -- you probably don't have this program (yet).
X#mod.sources:/usr/local/bin/archsrc -n mod.sources -p
X
X## A local test -- you don't want this.
X#ms.test:/bin/cat >>/tmp/foo
END_OF_qdnnf.how
if test 459 -ne `wc -c <qdnnf.how`; then
    echo shar: \"qdnnf.how\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"qdnnf.rckeep\" \(642 characters\)
if test -f qdnnf.rckeep ; then 
  echo shar: Will not over-write existing file \"qdnnf.rckeep\"
else
sed "s/^X//" >qdnnf.rckeep <<'END_OF_qdnnf.rckeep'
X#! /bin/sh
X##  You might find this small front-end to rckeep useful.  Quick usage
X##  shows that rckeep leaves its temp file around if given something other
X##  than a recipe, so we try to make sure we only give it recipes.
X##  $Header: qdnnf.rckeep,v 2.0 87/03/10 12:55:04 rs Rel $
X
X##  Where is rckeep on your system?
XRCKEEP=/usr/spool/recipes/bin/rckeep
X
X##  Arrange to clean up after ourselves.
Xtrap "exec rm -f /tmp/rc$$" 0 1 2 3 15
X
X##  Keep the spooled input
Xcat $@ >/tmp/rc$$
X
X##  Not all greps return an appropriate status, sigh...
Xcase `grep -l "^.RH MOD.RECIPES-SOURCE" /tmp/rc$$` in
X    /tmp/rc$$)
X	${RCKEEP} < /tmp/rc$$
X	;;
Xesac
END_OF_qdnnf.rckeep
if test 642 -ne `wc -c <qdnnf.rckeep`; then
    echo shar: \"qdnnf.rckeep\" unpacked with wrong size!?
fi
chmod +x qdnnf.rckeep
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0
-- 
--
Rich $alz					"Drug tests p**s me off"
Mirror Systems, Cambridge Massachusetts		rs@mirror.TMC.COM
{adelie, mit-eddie, ihnp4, harvard!wjh12, cca, cbosgd, seismo}!mirror!rs