Newsgroups: alt.sources From: Olaf Titz Subject: makeover - maintain the news overview database Date: 06 Sep 1995 13:47:43 GMT Message-ID: <14J4BTDN@inka.de> Archive-name: makeover Submitted-by: Olaf.Titz@inka.de Environment: cnews, UNIX For old news systems which don't maintain an overview database: This program does it for you. A good time to run it is after newsrun has done its work, and with the -e option after doexpire. For details (there really aren't many) refer to the manpage. Olaf.Titz@inka.de #!/bin/sh # shar: Shell Archiver (v1.22) # # Run the following text with /bin/sh to create: # README # Makefile # mkover.8 # common.h # mkover.c # group.c # main.c # misc.c # lock.c # xover.c # xmain.c # sed 's/^X//' << 'SHAR_EOF' > README && XFor old news systems which don't maintain an overview database: XThis program does it for you. A good time to run it is after newsrun Xhas done its work, and with the -e option after doexpire. XFor details (there really aren't many) refer to the manpage. X XThis is a result of a weekend's worth of hacking and I haven't tested Xit on anything but Linux, so perhaps there is some minor tweaking Xneeded but I don't see big portability problems. This is basically Xpure ANSI C. Look at common.h for configurable items. X XLet me know if it doesn't work. X Xxover.c contains the necessary stuff to integrate this into nntpd, Xwhich I run (heavily modified anyway, so I don't have a patch ready.) X XOlaf.Titz@inka.de SHAR_EOF chmod 0664 README || echo "restore of README fails" sed 's/^X//' << 'SHAR_EOF' > Makefile && XCC = gcc -Wall -funsigned-char XCFLAGS = -O3 -fomit-frame-pointer XLD = gcc XLDFLAGS = -s XLIBS = X# for debugging XDCFLAGS = -g -DDEBUG XDLFLAGS = -g XDLIBS = ../debug_malloc/libmalloc.a #-lmalloc X XBIN = /usr/local/lib/news/bin XMAN8 = /usr/local/man/man8 X XSRCS = mkover.c group.c main.c misc.c lock.c xover.c xmain.c XHDRS = common.h XOBJS = mkover.o group.o misc.o lock.o main.o XXOBJS = mkover.o xover.o misc.o lock.o xmain.o XMISC = README Makefile mkover.8 X Xall: mkover xover X Xmkover: $(OBJS) X $(LD) $(LDFLAGS) -o mkover $(OBJS) $(LIBS) X Xinstall: mkover X install -s -m 755 mkover $(BIN) X install -m 644 mkover.8 $(MAN8) X Xxov.o: xover.o mkover.o lock.o X ld -r -o xov.o xover.o mkover.o lock.o X Xxover: $(XOBJS) X $(LD) $(LDFLAGS) -o xover $(XOBJS) $(LIBS) X Xdebug: X $(MAKE) CFLAGS="$(DCFLAGS)" LDFLAGS="$(DLFLAGS)" LIBS="$(DLIBS)" all X X.c.o: X $(CC) $(CFLAGS) $(IFLAGS) -c $< X Xclean: X rm -f *.o *.a core a.out X Xclobber: clean X rm -f *~ mkover xover mkover.shar X Xshar: X cp Blurb mkover.shar X shar $(MISC) $(HDRS) $(SRCS) >> mkover.shar X Xdepend: X sed -e "/^\#\#\# Dependencies/q" Makefile > Makefile.tmp &&\ X gcc -MM $(SRCS) >> Makefile.tmp && mv -f Makefile.tmp Makefile X X X### Dependencies Xmkover.o : mkover.c common.h Xgroup.o : group.c common.h Xmain.o : main.c common.h Xnewslib.o : newslib.c common.h Xxover.o : xover.c common.h Xxmain.o : xmain.c common.h SHAR_EOF chmod 0664 Makefile || echo "restore of Makefile fails" sed 's/^X//' << 'SHAR_EOF' > mkover.8 && X' # -*- nroff -*- X.TH MKOVER 8 "August 20, 1995" X.GN 2 X.SH NAME Xmkover \- maintain the news overview database X.SH SYNOPSIS X.B mkover X[ X.B -e X| X.B -p X] [ X.B -v X] [ X.B group... X] X.SH DESCRIPTION X.I mkover Xgenerates and updates the X.B overview Xdatabase for older news systems which don't do this by themselves. XIt does so by reading the header lines directly from the article files Xwithout further interaction with the news system. X.PP XWith the X.B -e Xflag, X.I mkover Xalso checks for expired articles (i.e. removes the respective entries Xfrom the X.B overview Xdatabase). Invoke it this way about once a day. X.PP XAlternatively, give the X.B -p Xflag to do only expires. X.PP XThe X.B -v Xflag causes verbose output. X.PP XIf a list of groups is given, only these groups and all of Xits 'sub-groups' are handled. The X.B ! Xexclusion notation is supported to a limited amount. XI.e. you can specify something like X.br X.I mkover -e comp !comp.sys.foo !comp.sources X.SH DIAGNOSTICS X.I mkover Xgives output about processed groups roughly the same way like the Xsimilar X.I mthreads Xprogram. For each group processed, one character is output. XFor regular runs via crontab, this can safely be redirected to X/dev/null. X.SH FILES X.B NEWSLIB/active XThe news system's active file. X.BR X.B NEWSLIB/LOCKmkov XLock file for X.I mkover Xruns. X.BR X.B NEWSARTS/(group)/.overview XEach individual group's overview file. X.SH AUTHOR XOlaf Titz . Public domain. X.SH BUGS XThis program should be integrated into mthreads to provide for its Xadvanced daemon/scheduling and logging facilities, as it does Xbasically the same thing for just another file format. X.SH SEE ALSO Xnewsoverview(5), mthreads(8) SHAR_EOF chmod 0664 mkover.8 || echo "restore of mkover.8 fails" sed 's/^X//' << 'SHAR_EOF' > common.h && X/* Configuration dependencies */ X X#define NEWSLIB "/var/lib/news" X#define NEWSARTS "/var/spool/news" X X/* System dependencies */ X X#define USE_MMAP /* if you can mmap for reading on a regular file */ X X#undef NO_POINTER_ARITH X/* Define this if you can't subtract arbitrary pointers, i.e. X char *a, *b; int c; X c=a-b; assert(b+c==a) would fail for some a,b. */ X X/* malloc optimization parm */ X#define CHUNK 1024 X/* must be >= PATH_MAX, should be >= avg header len */ X#define NUMLEN 12 /* enough to hold an int in ascii */ X X/* Should not need to be configured below here */ X X#include X#ifndef PATH_MAX X#define PATH_MAX 256 X#endif X#ifdef I_MMAN X#ifdef USE_MMAP X#include X#endif X#endif X X#include X#include X#include X#include X#include X#include X#include X#include X X/* The overview format */ X#define OV_SUBJECT 0 X#define OV_FROM 1 X#define OV_DATE 2 X#define OV_MESSAGEID 3 X#define OV_REFERENCES 4 X#define OV_BYTES 5 X#define OV_LINES 6 X#define OV_XREF 7 X X/* Globals */ Xextern int tot_grp, tot_add, tot_exp; Xextern int verbose; X X/* Internal routines */ Xchar *mkover(int art); Xchar dogroup(const char *gr, int mi, int ma, int ex, int ad); Xint matches(char *grp, char *list[], int i); Xvoid setsig(const char *f1); Xvoid finish(int e); Xvoid daemon(int secs); Xvoid xo_close(void); Xint xo_group(char *gr, int mi); Xchar *xo_xover(int n); X#ifdef USE_FLOCK X/* this doesn't work yet! */ Xextern inline int lock(void) { return 0; } Xextern inline int unlock(void) { return 0; } X#else Xint lock(void); Xint unlock(void); X#endif SHAR_EOF chmod 0664 common.h || echo "restore of common.h fails" sed 's/^X//' << 'SHAR_EOF' > mkover.c && X#define I_MMAN X#include "common.h" X X/* Produce one article's overview line */ X/* Returns a malloc'ed string */ X Xchar *mkover(int art) X{ X int f; X char bn[NUMLEN]; X#ifndef USE_MMAP X int rpos=0, apos=0; X int bufs=CHUNK, mrd=CHUNK-1, nrd; X#endif X char *buf, *n=NULL; X char *p, *q, *r, c; X int l, i; X struct stat st; X int overrec[OV_XREF+1]; X int overlen[OV_XREF+1] = {0, 0, 0, 0, 0, 0, 0, 0}; X X#define err_c { close(f); return NULL; } X#define err_f { goto fin; } X#define err_cf { close(f); goto fin; } X X sprintf(bn, "%d", art); X if ((f=open(bn, O_RDONLY))<0) X return NULL; X if ((fstat(f, &st)<0) || (!(S_ISREG(st.st_mode)))) X err_c; X X#ifdef USE_MMAP X if ((buf=mmap(NULL, st.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, X f, 0)) ==(char *)-1) X err_c; X p=buf; X#else X if (!(buf=malloc(CHUNK))) X err_c; X#endif X X /* read & analyze header */ X X#ifndef USE_MMAP X while (1) { X if ((nrd=read(f, buf+rpos, mrd))<=0) X err_cf; X buf[rpos+nrd]='\0'; X p=buf+apos; X#endif X X r=p; X while ((q=strchr(p, '\n'))) { X if (q[1]==' '||q[1]=='\t') { X p=q+1; X continue; X } X if (*++q=='\n') X goto eoh; X X#define grok(s,l,n,f) \ X if (!strncasecmp(r,(s),(l))) \ X { overrec[(f)]=r+(n)-buf; overlen[(f)]=q-r-(n); } X X grok("Subject: ", 9, 9, OV_SUBJECT); X grok("From: ", 6, 6, OV_FROM); X grok("Date: ", 6, 6, OV_DATE); X grok("Message-ID: ", 12, 12, OV_MESSAGEID); X grok("References: ", 12, 12, OV_REFERENCES); X grok("Lines: ", 7, 7, OV_LINES); X grok("Xref: ", 6, 0, OV_XREF); X r=p=q; X } X X#ifndef USE_MMAP X apos=p-buf; X rpos+=nrd; X if ((mrd-=nrd)<=0) { X bufs+=CHUNK; X if (!(buf=realloc(buf, bufs))) { X close(f); X return NULL; X } X mrd=CHUNK; X } X } X#endif X X eoh: X close(f); X sprintf(bn, "%ld\n", st.st_size-(q-buf)); X#ifndef NO_POINTER_ARITH X overrec[OV_BYTES]=bn-buf; X#endif X overlen[OV_BYTES]=strlen(bn); X X for (l=0,i=0; i<=OV_XREF; ++i) X l+=overlen[i]; X if (!(n=malloc(NUMLEN+l+OV_XREF+3))) /* leave space for artno & tabs */ X err_f; X sprintf(n, "%d\t", art); X p=n+strlen(n); X for (i=0; i<=OV_XREF; ++i) { X if (overlen[i]) { X#ifdef NO_POINTER_ARITH X if (i==OV_BYTES) X q=bn; X else X#endif X q=buf+overrec[i]; X l=overlen[i]; X while (--l) { X if ((c=*q++)=='\n') X continue; X *p++= (c<' ') ? ' ' : c; X } X } X *p++='\t'; X } X --p; X if (!overlen[OV_XREF]) X --p; X *p='\0'; X X fin: X#ifdef USE_MMAP X munmap(buf, st.st_size); X#else X free(buf); X#endif X return n; X} SHAR_EOF chmod 0664 mkover.c || echo "restore of mkover.c fails" sed 's/^X//' << 'SHAR_EOF' > group.c && X#include "common.h" X X/* Update one group's overview file */ X Xchar dogroup(const char *gr, int mi, int ma, int ex, int ad) X{ X char buf[CHUNK]; X char b2[NUMLEN]; X char *p1 = strdup(gr); X char *p2; X char c; X FILE *f1, *f2; X struct stat st; X int i, add=0, cop=0; X X if (!(p2=p1)) X return 'e'; X while((c=*p2)) { X if (c=='.') X *p2='/'; X ++p2; X } X sprintf(buf, "%s/%s", NEWSARTS, p1); X free(p1); X if (chdir(buf)<0) X return '*'; X X if (ex) { X setsig(".overview.n"); X if (!(f1=fopen(".overview.n", "w"))) { X setsig(NULL); X return '!'; X } X setlinebuf(f1); X if ((f2=fopen(".overview", "r"))) { X while (fgets(buf, sizeof(buf), f2)) { X i=atoi(buf); X if (i=mi) X mi=i+1; X while (buf[strlen(buf)-1] != '\n') X if (!fgets(buf, sizeof(buf), f1)) X goto mkm; X } X mkm: X } X /* Now write new overview lines to f1 */ X if (ad) X for (i=mi; i<=ma; ++i) { X if ((p1=mkover(i))) { X fputs(p1, f1); X fputc('\n', f1); X free(p1); X ++tot_add; X ++add; X } X } X fclose(f1); X if (ex) { X if (rename(".overview.n", ".overview")<0) { X unlink(".overview.n"); X setsig(NULL); X return '!'; X } X setsig(NULL); X } X if (verbose) X printf("%d %d ", cop, add); X return add ? '#' : (cop ? ':' : (ex ? '-' : '.')); X} SHAR_EOF chmod 0664 group.c || echo "restore of group.c fails" sed 's/^X//' << 'SHAR_EOF' > main.c && X#include "common.h" X X/* X mkover - maintain the overview database X Olaf Titz, Aug. 95. Public domain. X*/ X Xstatic void usage(const char *n) X{ X fprintf(stderr, "Usage: %s [options] [group...]\n", n); X fprintf(stderr, " -e: expiration checking\n"); X fprintf(stderr, " -p: only expire\n"); X fprintf(stderr, " -v: verbose\n"); X exit(127); X} X Xint tot_grp=0, tot_add=0, tot_exp=0; X Xstatic int expire=0, addnew=1; Xint verbose=0; Xstatic int daem=0; X X#ifdef DEBUG Xextern int malloc_usage(void); X Xstatic void testmode(char *argv[]) X{ X int mi, ma, i, me; X char *c; X ++verbose; X me=malloc_usage(); X mi=atoi(argv[optind++]); X ma=atoi(argv[optind]); X for(i=mi; i<=ma; i++) { X if ((c=mkover(i))) { X fputs(c, stdout); X fputc('\n', stdout); X free(c); X } X } X printf("malloc: %d, %d\n", me, malloc_usage()); X exit(0); X} X#endif X Xvoid main(int argc, char *argv[]) X{ X int i; X FILE *a; X char gr[128], fl[128]; X int mi, ma; X#ifdef DEBUG X int me; X#endif X X while ((i=getopt(argc, argv, "d:epvt"))>0) { X switch(i) { X case 'd': daem=atoi(optarg); X fprintf(stderr, "warning: daemon mode not implemented\n"); X break; X case 'e': ++expire; break; X case 'p': ++expire; addnew=0; break; X case 'v': ++verbose; break; X#ifdef DEBUG X case 't': testmode(argv); break; X#endif X default: usage(argv[0]); X } X } X { X char buf[PATH_MAX]; X sprintf(buf, "%s/active", NEWSLIB); X if (!(a=fopen(buf, "r"))) { X fprintf(stderr, "Can't open active: %s\n", strerror(errno)); X exit(1); X } X } X i=lock(); X if (i>0) { X fprintf(stderr, "Locked by process %d\n", i); X exit(2); X } X if (i<0) { X fprintf(stderr, "Can't lock: %s\n", strerror(errno)); X exit(3); X } X setsig(NULL); X setbuf(stdout, NULL); X#ifdef DEBUG X me=malloc_usage(); X#endif X while (fscanf(a, "%s %d %d %s\n", gr, &ma, &mi, fl)>0) { X if ((optind==argc) || (matches(gr, argv, optind))) { X if (fl[0]=='y' || fl[0]=='m' || fl[0]=='n') { X if (verbose) X printf("%s ", gr); X putchar(dogroup(gr, mi, ma, expire, addnew)); X if (verbose) X putchar('\n'); X ++tot_grp; X } else { X putchar('x'); X } X } X } X#ifdef DEBUG X if (verbose) X printf("malloc: %d, %d\n", me, malloc_usage()); X#endif X fclose(a); X finish(0); X} SHAR_EOF chmod 0664 main.c || echo "restore of main.c fails" sed 's/^X//' << 'SHAR_EOF' > misc.c && X#include "common.h" X Xint matches(char *grp, char *list[], int i) X{ X int r=0; X char *p; X while ((p=list[i])) { X if (*p=='!') { X ++p; X if (!strncmp(grp, p, strlen(p))) X r=0; X } else { X if (!strncmp(grp, p, strlen(p))) X r=1; X } X ++i; X } X return r; X} X Xstatic const char *toremove; X Xvoid finish(int e) X{ X if (toremove) X unlink(toremove); X unlock(); X putchar('\n'); X if (tot_grp) X printf("Processed %d groups: added %d, expired %d articles.\n", X tot_grp, tot_add, tot_exp); X exit(e); X} X Xstatic void sighandler(int i) X{ X fprintf(stderr, "Got signal %d\n", i); X finish(4); X} X Xvoid setsig(const char *f) X{ X toremove=f; X signal(SIGHUP, SIG_IGN); X signal(SIGTERM, sighandler); X signal(SIGINT, sighandler); X} SHAR_EOF chmod 0664 misc.c || echo "restore of misc.c fails" sed 's/^X//' << 'SHAR_EOF' > lock.c && X#include "common.h" X X#define LOCK "LOCKmkov" Xstatic int locked=0; X X/* Lock via a lockfile. Returns: 0 if successful, X (pid) if already locked by pid, <0 on error. */ X Xint lock(void) X{ X char f1[PATH_MAX], f2[PATH_MAX]; X int t=5; X int i; X FILE *f; X X sprintf(f1, "%s/L.%d", NEWSLIB, getpid()); X sprintf(f2, "%s/%s", NEWSLIB, LOCK); X if (!(f=fopen(f1, "w"))) X return -1; X fprintf(f, "%d\n", getpid()); X fclose(f); X while (link(f1, f2)<0) { X if ((f=fopen(f2, "r"))) { X fscanf(f, "%d", &i); X fclose(f); X if (i && ((!kill(i, 0)) || (errno!=ESRCH))) { X unlink(f1); X return i; /* Is locked */ X } X unlink(f2); /* Break stale lock */ X } X if (!--t) { X unlink(f1); X return -1; /* Error */ X } X sleep(2); X } X unlink(f1); X locked=1; X return 0; X} X Xint unlock(void) X{ X char f1[PATH_MAX]; X if (locked) { X sprintf(f1, "%s/%s", NEWSLIB, LOCK); X return (locked=unlink(f1)); X } X return 0; X} SHAR_EOF chmod 0664 lock.c || echo "restore of lock.c fails" sed 's/^X//' << 'SHAR_EOF' > xover.c && X#include "common.h" X X/* Automagic XOVER support for nntpd: read the overview file, generate X missing entries, update the file as entries are requested. Adjusts X itself to file availability and permission settings. Uses the "step X forward" algorithm as recommended by Davison. Big win for trn :-) X X -ot.1995-08-27 */ X Xstatic FILE *overfile=NULL; Xstatic int overpos; Xstatic int overmin=0; Xstatic int maywrite; Xstatic char *buf=NULL; Xstatic int bufl=CHUNK; X X/* Done with a group. */ Xvoid xo_close(void) X{ X fclose(overfile); X overfile=NULL; X unlock(); X} X X/* Step into a group. Returns 1 if successful, 0 if not. */ Xint xo_group(char *gr, int mi) X{ X char b[PATH_MAX]; X char *p; X char c; X X if (overfile) X xo_close(); X strcpy(b, NEWSARTS); X p=b+strlen(b); X *p++='/'; X do { X c=*gr++; X if (c=='.') X c='/'; X *p++=c; X } while (c); X if (chdir(b)<0) X return 0; X if (!buf) { X if (!(buf=malloc(bufl))) X return 0; X } X overmin=--mi; X maywrite=1; X if (lock()==0) { X if ((overfile=fopen(".overview", "a+"))) { X rewind(overfile); X overpos=0; X } else if (errno==EACCES) { X maywrite=0; X overfile=fopen(".overview", "r"); X } X } X#ifdef DEBUG X if (verbose) X fprintf(stderr, "%c%c ", (overfile)?'R':'-', X (overfile && maywrite)?'W':'-'); X#endif X return 1; X} X X/* Give overview line for one article, after xo_group was called. X Returns a malloc'ed string */ Xchar *xo_xover(int n) X{ X char *b=NULL; X int l; X X if (!overfile) X return mkover(n); X X if (overpos>n) { X#ifdef DEBUG X if (verbose) X fprintf(stderr, ".0 "); X#endif X rewind(overfile); X overpos=0; X } X assert(buf); X X /* Read the overview file... */ X while ((overpos=bufl) { X do { X bufl+=CHUNK; X } while (l>=bufl); X if (!(buf=realloc(buf, bufl))) { X free(b); xo_close(); return NULL; X } X } X strcpy(buf, b); X return b; X } X free(b); X } X } X return NULL; X} SHAR_EOF chmod 0664 xover.c || echo "restore of xover.c fails" sed 's/^X//' << 'SHAR_EOF' > xmain.c && X#include "common.h" X X/* X Wrapper for testing the xover facility X Simulates an nntp dialog X*/ X Xint verbose=0; Xint tot_grp=0, tot_add=0, tot_exp=0; Xstatic int ingroup=0; X Xstatic void xov(char *a) X{ X char *c; X int mi, ma, i; X X if (sscanf(a, "%d-%d", &mi, &ma)!=2) { X fputs("510 Usage: XOVER artno-artno\n", stdout); X } else { X fputs("221 overview lines follow\n", stdout); X for (i=mi; i<=ma; ++i) { X c=xo_xover(i); X if (c) { X fputs(c, stdout); X fputc('\n', stdout); X } X } X fputs(".\n", stdout); X } X} X Xvoid main(int argc, char *argv[]) X{ X int i, mi, ma; X char buf[1024]; X char b2[1024]; X FILE *f; X X while ((i=getopt(argc, argv, "v"))>0) { X switch(i) { X case 'v': ++verbose; break; X default: fprintf(stderr, "usage: %s [-v]\n", argv[0]); exit(127); X } X } X setbuf(stdout, NULL); X fputs("201 xover testing facility ready.\n", stdout); X X while(1) { X if (!fgets(buf, sizeof(buf), stdin)) X break; X buf[strlen(buf)-1]='\0'; X if (!strncasecmp(buf, "QUIT", 4)) X break; X if (!strncasecmp(buf, "GROUP ", 5)) { X#if 0 X if (xo_group(buf+6)) { X setsig(NULL); X sprintf(b2, "awk '/^%s / {printf \"211 %%d %%d %%d %%s\\n\", $2-$3, $3, $2, $1}' %s/active", buf+6, NEWSLIB); X system(b2); /* look away, this is a test program */ X ingroup=1; X } else { X fprintf(stdout, "411 Invalid group: %s\n", buf+6); X ingroup=0; X } X#else X sprintf(b2, "grep '^%s ' %s/active", buf+6, NEWSLIB); X if ((f=popen(b2, "r"))) { X if (fscanf(f, "%*s %d %d %*s\n", &ma, &mi)==2) { X if (xo_group(buf+6, mi)) { X fprintf(stdout, "211 %d %d %d %s\n", ma-mi, mi, ma, buf+6); X pclose(f); X ingroup=1; X continue; X } X } X pclose(f); X } X fprintf(stdout, "411 Invalid group: %s\n", buf+6); X ingroup=0; X#endif X continue; X } X if (!strncasecmp(buf, "XOVER ", 5)) { X if (ingroup) { X xov(buf+6); X } else { X fputs("412 Not in a group\n", stdout); X } X continue; X } X fputs("500 What?\n", stdout); X } X fputs("205 Exiting.\n", stdout); X} SHAR_EOF chmod 0664 xmain.c || echo "restore of xmain.c fails" exit 0 -- ___ Olaf.Titz@inka.de or @{stud,informatik}.uni-karlsruhe.de ____ __ o __/<_ >> Just as long as the wheels keep on turning round _)>(_)______________ I will live for the groove 'til the sun goes down << ____