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