dt@yenta.alb.nm.us (David B. Thomas) (11/27/90)
Hello, netters! Here's an expire alternative. If you have a small disk, this is for you. It was inspired by Mike Murphy's "trasher" posted a few weeks ago. Problem: News volume fluctuates wildly...sites with small disks must either expire aggresively, often deleting more than necessary, or worry that an unexpected huge dose of news might overfill the disk. Solution: Implement a tool that expires according to a user-defined scheme until sufficient freespace is reclaimed, then stops, leaving as much juicy news online as is feasible. Reap does this. Expire Does Two Jobs: Both Bnews and Cnews expires really do two jobs: 1. trim history files 2. delete outdated articles Thanks to some inspired jootsing (acronym for "jumping out of the system") by Mike Murphy (mrm@sceard.com) and others, it is more than possible to separate those two functions. This is, of course, in keeping with the unix philosophy of one tool doing one job well! What Reap Does: Reap only takes care of the second job: deleting old articles. It works by checking freespace, and processing one line at a time from a list of expire functions, until the desired freespace is attained. Each expire function consists of an age limit in days (decimals okay) and a list of newsgroups to process or not process, sys file style. Ex: .5 alt.sex.pictures,talk,!talk.bizarre,junk 1 rec,!rec.games,!rec.humor This example would check freespace, and if more space is needed, expire to .5 days everything in alt.sex.pictures,talk (except for talk.bizarre) and junk. Then it would stop and check freespace again. If still more space is needed, it would expire to 1 day everything in rec except rec.games.* and rec.humor.*. It's that simple. Okay...So How Do I Arrange To Trim The History Files, Since Reap Can't: Included in this distribution is a shell script (mostly written by Mike Murphy) to handle Cnews history files. It shouldn't be too difficult to do something similar for Bnews, or you can give in and use the original expire utility with options that tell it to expire the history files only...but I think that would be slow. It just comes down to removing lines from ordinary text files, based on their contents. Murphy and I used awk. But Is It Fast: Yes, largely because it doesn't have to do much. Even "find | rm" is slower because find is repeatedly exec-ing rm. Since the functions of deleting articles and trimming history are separate, I now run reap every few hours, but trim the history list just once a day. That effectively keeps my disk space up to snuff, but only thrashes at the history file in the middle of the night. Credits: I owe a lot to Mike Murphy for inspiring me with his "trasher" system. I also owe a lot to all of your netters who will flood me with suggestions and improvements in the coming weeks (hint, hint!). Note: this uses ustat(2), a system-V call to find disk freespace. It should be a very easy hack to use the BSD equivalent. I just don't know how to do it. little david dt@yenta.alb.nm.us -----snip-----------snip-----------snip-----------snip-----------snip------ #!/bin/sh # This is a shell archive (shar 3.44) # made 11/27/1990 07:20 UTC by dt@yenta # Source directory /u/dt/reap # # existing files will NOT be overwritten unless -c is specified # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 730 -rw-rw-r-- INSTALL # 339 -rw-rw-r-- MANIFEST # 900 -rw-rw-r-- Makefile # 2792 -rw-rw-r-- README # 969 -rwxrwxr-x exphist # 2539 -rw-rw-r-- lib.c # 873 -rw-rw-r-- list.sample # 2028 -rw-rw-r-- main.c # 2335 -rw-r--r-- reap.8 # 2150 -rw-rw-r-- reap.c # 676 -rw-rw-r-- reap.h # # ============= INSTALL ============== if test -f 'INSTALL' -a X"$1" != X"-c"; then echo 'x - skipping INSTALL (File already exists)' else echo 'x - extracting INSTALL (Text)' sed 's/^X//' << 'SHAR_EOF' > 'INSTALL' && X To install: X 1. Edit tops of Makefile and main.c to fit your system. 2. make 3. Compose a sample function list file or use the sample provided to X test reap (use the -n option to avoid really removing files). 4. su and make install. 5. Arrange to trim your news system's history list regularly. If you have X Cnews, try the "exphist" script provided, run from cron. That X script requires that a tiny file, /usr/lib/news/histdays, contain X the number of days of history data to keep, or else you can hardcode X in your favorite number. If you have Bnews, I don't have a nifty X suggestion, other than using expire(8) with a ridiculously long X article expire time, but a sane history expire time. 6. Arrange to run reap from cron. SHAR_EOF chmod 0664 INSTALL || echo 'restore of INSTALL failed' Wc_c="`wc -c < 'INSTALL'`" test 730 -eq "$Wc_c" || echo 'INSTALL: original size 730, current size' "$Wc_c" fi # ============= MANIFEST ============== if test -f 'MANIFEST' -a X"$1" != X"-c"; then echo 'x - skipping MANIFEST (File already exists)' else echo 'x - extracting MANIFEST (Text)' sed 's/^X//' << 'SHAR_EOF' > 'MANIFEST' && Included with this distribution are: X README - general description and credits INSTALL - hints on how to install reap and get it going Xexphist - history trimmer utility for Cnews list.sample - sample function script file for reap reap.8 - man page lib.c - source main.c - source reap.c - source reap.h - source Makefile - makefile SHAR_EOF chmod 0664 MANIFEST || echo 'restore of MANIFEST failed' Wc_c="`wc -c < 'MANIFEST'`" test 339 -eq "$Wc_c" || echo 'MANIFEST: original size 339, current size' "$Wc_c" fi # ============= Makefile ============== if test -f 'Makefile' -a X"$1" != X"-c"; then echo 'x - skipping Makefile (File already exists)' else echo 'x - extracting Makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile' && #---------------------------- # parameters for installation #---------------------------- X # directories where manual and executable are to be installed BINDIR = /usr/lib/newsbin/expire MANDIR = /usr/man/man8 X # owner, group and file mode for manual and executable EXEOWNER = bin EXEGROUP = bin EXEMODE = 775 MANOWNER = bin MANGROUP = bin MANMODE = 664 X X # your favorite C compiler #CC = cc CC = shcc X # directory access functions library, if not in standard C library. LIB = -ldirent X X #------------- # boring stuff #------------- X OBJ = main.o lib.o reap.o X reap: $(OBJ) X $(CC) -s -o reap $(SHLIB) $(OBJ) $(LIB) X $(OBJ): reap.h X install: reap reap.8 X cp reap $(BINDIR) X chown $(EXEOWNER) $(BINDIR)/reap X chgrp $(EXEGROUP) $(BINDIR)/reap X chmod $(EXEMODE) $(BINDIR)/reap X cp reap.8 $(MANDIR) X chmod $(MANMODE) $(MANDIR)/reap.8 X chown $(MANOWNER) $(MANDIR)/reap.8 X chgrp $(MANGROUP) $(MANDIR)/reap.8 SHAR_EOF chmod 0664 Makefile || echo 'restore of Makefile failed' Wc_c="`wc -c < 'Makefile'`" test 900 -eq "$Wc_c" || echo 'Makefile: original size 900, current size' "$Wc_c" fi # ============= README ============== if test -f 'README' -a X"$1" != X"-c"; then echo 'x - skipping README (File already exists)' else echo 'x - extracting README (Text)' sed 's/^X//' << 'SHAR_EOF' > 'README' && X A Problem: X News volume fluctuates wildly...sites with small disks must either Xexpire aggresively, often deleting more than necessary, or worry that an unexpected huge dose of news might overfill the disk. X X A Solution: X Implement a tool that expires according to a user-defined scheme until sufficient freespace is reclaimed, then stops, leaving as much juicy news online as is feasible. Reap does this. X X Expire Does Two Jobs: X Both Bnews and Cnews expires really do two jobs: X 1. trim history files X 2. delete outdated articles Thanks to some inspired jootsing (acronym for "jumping out of the system") by Mike Murphy (mrm@sceard.com) and others, it is more than possible to separate those two functions. This is, of course, in keeping with the unix philosophy of one tool doing one job well! X X What Reap Does: X Reap only takes care of the second job: deleting old articles. It works by checking freespace, and processing one line at a time from a list of expire functions, until the desired freespace is attained. Each expire function consists of an age limit in days (decimals okay) and a list of newsgroups to process or not process, sys file style. Ex: X X .5 alt.sex.pictures,talk,!talk.bizarre,junk X 1 rec,!rec.games,!rec.humor X This example would check freespace, and if more space is needed, expire to .5 days everything in alt.sex.pictures,talk (except for talk.bizarre) and junk. Then it would stop and check freespace again. If still more space is needed, it would expire to 1 day everything in rec except rec.games.* and rec.humor.*. It's that simple. X X Okay...So How Do I Arrange To Trim The History Files, Since Reap Can't: X Included in this distribution is a shell script (mostly written by Mike Murphy) to handle Cnews history files. It shouldn't be too difficult to do something similar for Bnews, or you can give in and use the original expire utility with options that tell it to expire the history files only...but I think this will be slow. It just comes down to removing lines from ordinary text files, based on their contents. Murphy and I used awk. X X But Is It Fast: X Yes, largely because it doesn't have to do much. Even "find | rm" is slower because find is repeatedly exec-ing rm. "rm -rf" has me beat, though, I'll bet! :-) X X Since the functions of deleting articles and trimming history are separate, I now run reap every six hours, but trim the history list just once a day. That effectively keeps my disk space up to snuff, but only thrashes at the history file in the middle of the night. X X Credits: X I owe a lot to Mike Murphy for inspiring me with his "trasher" system. I also owe a lot to all of your netters who will flood me with suggestions and improvements in the coming weeks (hint, hint!). X X little david X dt@yenta.alb.nm.us SHAR_EOF chmod 0664 README || echo 'restore of README failed' Wc_c="`wc -c < 'README'`" test 2792 -eq "$Wc_c" || echo 'README: original size 2792, current size' "$Wc_c" fi # ============= exphist ============== if test -f 'exphist' -a X"$1" != X"-c"; then echo 'x - skipping exphist (File already exists)' else echo 'x - extracting exphist (Text)' sed 's/^X//' << 'SHAR_EOF' > 'exphist' && #! /bin/sh # exphist - expire history file X # =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()= . ${NEWSCONFIG-/usr/lib/news/bin/config} X PATH=$NEWSCTL/bin:$NEWSBIN/expire:$NEWSBIN:$NEWSPATH ; export PATH umask $NEWSUMASK X days=`cat /usr/lib/news/histdays` X lock="$NEWSCTL/LOCKexpire" ltemp="$NEWSCTL/L.$$" Xecho $$ >$ltemp trap "rm -f $ltemp ; exit 0" 0 1 2 15 if newslock $ltemp $lock then X trap "rm -f $ltemp $lock ; exit 0" 0 1 2 15 Xelse X echo "$0: expire apparently already running" | mail "$NEWSMASTER" X exit 1 fi X cd $NEWSCTL rm -f history.n history.n.pag history.n.dir now=`getdate now` age=`expr 86400 \* $days` ago=`expr $now - $age` awk "{split(\$2,dates,\"~\");if(dates[1]>$ago)print \$0}" history >history.n mkdbm history.n [ -s history.n ] && mv history history.o && # install new ASCII history file mv history.n history && rm -f history.pag && # and related dbm files rm -f history.dir && mv history.n.pag history.pag && mv history.n.dir history.dir Xexit 0 SHAR_EOF chmod 0775 exphist || echo 'restore of exphist failed' Wc_c="`wc -c < 'exphist'`" test 969 -eq "$Wc_c" || echo 'exphist: original size 969, current size' "$Wc_c" fi # ============= lib.c ============== if test -f 'lib.c' -a X"$1" != X"-c"; then echo 'x - skipping lib.c (File already exists)' else echo 'x - extracting lib.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'lib.c' && X /* X * lib.c X * subroutines needed by reap utility X */ X #include "reap.h" X /* X * freeblox(devno) X * X * assuming devno is the device number of a file system, X * this function returns the free space on that file system, X * in blocks (whatever that means for that filesystem) as a X * long int. X * X */ X freeblox(devno) int devno; { X static struct ustat ust; X X if (ustat(devno, &ust)) { X fprintf (stderr, "%s: ustat() failed\n", progname); X exit (-1); X } X return (ust.f_tfree); X } /* freeblox() */ X X X X /* X * parse(cmd) X * execute a line from the script file X * X * valid command lines: X * a blank line is ignored X * # comment (ignored) X * days filespecs X * X */ X parse(cmd) char *cmd; { X double days; X char *t, X *p, X *q, X *seps = ",\n "; X X /* skip initial whitespace */ X p = cmd; X while (isspace(*p)) X ++p; X /* ignore blank lines or comments */ X if (*p == '\0' || *p == '#') X return(1); X /* read the expire time from the line as a floating point number, X * then figure out what the timestamp on a file that old would be */ X age = now - (time_t) (atof(p) * SECINDAY); X /* skip ahead to the filespec list */ X while (!isspace(*p)) X ++p; X while (isspace(*p)) X ++p; X X preclude(); X for (t = strtok(p, seps); t; t = strtok(NULL, seps)) { X /* forbid starting with a slash */ X if (*t == '/') X *t = '.'; X /* change dots to slashes except in column 1 */ X while ( (q = strchr(t+1,'.')) != NULL) X *q = '/'; X if (*t == '!') X exclude (t+1); X else X include (t); X } X X reap(); X X return (0); X } /* parse() */ X X X /* X * newnode,exclude, include, preclude X * X * functions to maintain global linked lists: X * incl include this path in list of dirs to recursively reap X * excl leave out this directory and descendants X * X * the functions are: X * include (text) add text to incl list X * exclude (text) add text to excl list X * preclude() clear both lists X */ struct filspec * newnode(text) char *text; { X struct filspec *f; X X if ( (f = (struct filspec *) malloc (sizeof(struct filspec))) == NULL || X (f->name = malloc (strlen(text)+1)) == NULL) { X fprintf (stderr, "%s: out of memory\n", progname); X exit (-1); X } X strcpy (f->name, text); X return (f); } Xexclude(text) char *text; { X struct filspec *f; X X f = newnode(text); X f->next = excl; X excl = f; } include(text) char *text; { X struct filspec *f; X X f = newnode(text); X f->next = incl; X incl = f; } preclude() { X struct filspec *p; X X while (incl != NULL) { X p = incl; X incl = incl->next; X free (p); X } X while (excl != NULL) { X p = excl; X excl = excl->next; X free (p); X } } SHAR_EOF chmod 0664 lib.c || echo 'restore of lib.c failed' Wc_c="`wc -c < 'lib.c'`" test 2539 -eq "$Wc_c" || echo 'lib.c: original size 2539, current size' "$Wc_c" fi # ============= list.sample ============== if test -f 'list.sample' -a X"$1" != X"-c"; then echo 'x - skipping list.sample (File already exists)' else echo 'x - extracting list.sample (Text)' sed 's/^X//' << 'SHAR_EOF' > 'list.sample' && .1 junk,alt.flame,control,general 1 alt.sex.pictures 1 comp.os.vms .5 talk 1 sci,!sci.skeptic 2 sci.skeptic .5 misc.jobs,misc.handicap 2 soc 2 alt.tv 1 biz .5 rec,!rec.arts.anime,!rec.arts.animation,!rec.arts.movies,!rec.games.hack,!rec.ham-radio,!rec.autos,!rec.arts.tv,!rec.audio,!rec.video,!rec.music 1 rec.music,!rec.music.synth 3 rec.music.synth 2 rec.arts.anime,rec.arts.animation,rec.arts.movies,rec.ham-radio,rec.autos,rec.arts.tv,rec.audio,rec.video 3 rec.games.hack 1 misc.headlines,misc.consumers,misc.kids,misc.legal 1 alt.sex,!alt.sex.bondage,!alt.sex.pictures 1 comp.sources 1 alt.sources 2 comp,!comp.os.vms 1 news 3 misc 3 pubnet 3 alt 3 unix-pc # up to here is "normal expire"... now let's get desparate .1 alt.sex.pictures .1 rec.arts.anime,rec.arts.animation,rec.arts.movies,rec.ham-radio,rec.autos,rec.arts.tv,rec.audio,rec.video .1 biz .1 talk .1 news SHAR_EOF chmod 0664 list.sample || echo 'restore of list.sample failed' Wc_c="`wc -c < 'list.sample'`" test 873 -eq "$Wc_c" || echo 'list.sample: original size 873, current size' "$Wc_c" fi # ============= main.c ============== if test -f 'main.c' -a X"$1" != X"-c"; then echo 'x - skipping main.c (File already exists)' else echo 'x - extracting main.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'main.c' && X /* X * main.c X * main routine and globals for reap utility. X */ X X /* directory containing news articles */ #define NEWSDIR "/usr/spool/news" X X #include "reap.h" X /* linked lists for included and excluded file specs */ struct filspec X *incl = NULL, X *excl = NULL ; char X *scriptfile = "/usr/lib/news/reaplist", X *progname, X *newsdir = NEWSDIR ; int X verbose = 0, /* verbosity flag */ X nodel = 0 /* do-not-unlink flag */ ; time_t X age, /* unlink files older than this */ X now /* current time */ ; X X X main(argc, argv) int argc; char *argv[]; { X int device, /* dev.no. of disk containing newsdir */ X c, X errflag = 0; X long wantblox; /* desired free space */ X char *p; X static char line[MAXLINE]; X FILE *script; /* file ptr for function script */ X struct stat st; X X /* parse args */ X progname = argv[0]; X X while ( (c=getopt(argc,argv,"vnf:")) != EOF) X switch(c) { X case 'n': X ++nodel; X break; X case 'v': X ++verbose; X break; X case 'f': X scriptfile = optarg; X break; X case '?': X ++errflag; X break; X } X X if (argc - optind != 1 || X sscanf(argv[optind], "%ld", &wantblox) != 1 ) X ++errflag; X X if (errflag) { X fprintf (stderr, X "usage: %s [-v] [-n] [-f funclist] freeblocks\n", progname); X exit (-1); X } X X /* stat newsdir to find the number of the device it's on */ X if (stat(newsdir, &st)) { X fprintf (stderr, "%s: can't stat %s\n", progname, newsdir); X exit (-1); X } X device = st.st_dev; X /* open function file for reading */ X if ( (script = fopen (scriptfile, "r")) == NULL) { X fprintf (stderr, "%s: can't read %s\n", progname, scriptfile); X exit (-1); X } X /* when am i? */ X time (&now); X /* chdir to newsdir */ X chdir (newsdir); X /* main loop is right here ... process until goal reached or end of script */ X X while (freeblox(device) < wantblox && X (p = fgets (line, MAXLINE, script)) != NULL ) X parse (line); X X fclose (script); X /* return 0 if freespace goal was met at exit, 1 if not */ X exit (freeblox(device) >= wantblox ? 0 : 1); X } /* main() */ X SHAR_EOF chmod 0664 main.c || echo 'restore of main.c failed' Wc_c="`wc -c < 'main.c'`" test 2028 -eq "$Wc_c" || echo 'main.c: original size 2028, current size' "$Wc_c" fi # ============= reap.8 ============== if test -f 'reap.8' -a X"$1" != X"-c"; then echo 'x - skipping reap.8 (File already exists)' else echo 'x - extracting reap.8 (Text)' sed 's/^X//' << 'SHAR_EOF' > 'reap.8' && .TH REAP 8 LOCAL .SH NAME reap - delete news articles as space needed .SH SYNOPSIS .B reap [-v] [-n] [-f funcfile] freeblocks .SH DESCRIPTION .I Reap checks disk freespace and deletes articles according to a user-specified scheme until .I freeblocks minimum freespace is available. .PP The .B -n causes a dry run (as in make(1)). When this option is specified, reap merely reports which files it would delete if the option were left off. This is useful for debugging function scripts. However, since no space is actually freed in this mode, it will either do nothing (there was enough space to begin with) or proceed through the entire function script file, indicating that reap would delete all applicable articles. .PP With the .B -v option, reap prints a verbose commentary on its progress to the standard output. .PP By default, reap looks in /usr/lib/news/reaplist for its list of functions. The .B -f option is used to specify an alternate function script file. .SH FUNCTION SCRIPT FILE FORMAT A function script file consists of a series of expire functions, one per line. Each expire function contains an age (in days, decimals okay), followed by a list of newsgroups to be expired. Blank lines and lines beginning with a "#" are ignored. A sample function script file: .PP .nf X # function script file X 0.5 talk,junk,alt.sex.pictures X 2 rec,!rec.games,!rec.humor X 3 misc .fi .PP In the example, if space is needed, reap Xexpires to .5 days the entire talk and junk hierarchies, and alt.sex.pictures. If still more space is needed, rec hierarchy (excepting rec.games.* and rec.humor.*) is expired to 2 days. If freespace is still short, reap then expires all of misc to 2 days. .PP Note that, while it would be possible to consolidate the second and third lines, leaving them separate makes it posisble for reap to stop in between them if sufficient space is cleared. .SH FILES .TP 25 /usr/spool/news News spool directory .TP 25 /usr/lib/news/reaplist Default function list file .SH DIAGNOSTICS Returns zero if sufficient space was free at exit, nonzero otherwise. .SH BUGS The use of the ustat() system call is not very portable, and your humble author isn't aware of its non-system-V equivalents. .PP Lines in the function file are limited to 512 characters. .PP What constitutes a disk "block" is implementation-dependent. SHAR_EOF chmod 0644 reap.8 || echo 'restore of reap.8 failed' Wc_c="`wc -c < 'reap.8'`" test 2335 -eq "$Wc_c" || echo 'reap.8: original size 2335, current size' "$Wc_c" fi # ============= reap.c ============== if test -f 'reap.c' -a X"$1" != X"-c"; then echo 'x - skipping reap.c (File already exists)' else echo 'x - extracting reap.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'reap.c' && X /* X * reap.c X * contains the reap() function. X * X * Once the global linked lists incl and excl have been stuffed, X * reap() actually scans the filesystem for files that meet the specs X * and unlinks them if they are older than the age global variable. X */ X #include "reap.h" X X reap() { X struct filspec *f; X X for (f = incl; f != NULL; f = f->next) { X if (verbose) X printf ("scanning %s ...\n", f->name); X dodir (f->name); X } X } /* reap() */ X X X dodir(name) char *name; { X /* the following can be overwritten safely during recursion */ X static struct filspec *e; X static struct stat st; X static struct dirent *dp; X /* the following must be preserved during recursion */ X char *fullpath; X DIR *dirp; X X X if ( (dirp = opendir(name)) == NULL) { X fprintf (stderr, "%s: can't read directory %s\n", progname, X name); X return (-1); X } X X while ( (dp = readdir(dirp)) != NULL) { X X /* skip dot and dotdot */ X if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) X continue; X X /* build the full pathname of each object */ X if ( (fullpath = X malloc(strlen(name)+strlen(dp->d_name)+2)) == NULL) { X fprintf (stderr, "%s: out of memory\n", progname); X exit (-1); X } X sprintf (fullpath, "%s/%s", name, dp->d_name); X X /* check for excluded paths */ X for (e = excl; e != NULL; e = e->next) X if (!strcmp (fullpath, e->name)) X break; X if (e != NULL) { X free (fullpath); X continue; X } X X /* try to stat the object */ X if (stat(fullpath,&st)) { X fprintf (stderr, "%s: can't stat %s\n", X progname, fullpath); X free (fullpath); X continue; X } X X /* recurse if it's a directory */ X if ( st.st_mode & S_IFDIR ) { X dodir (fullpath); X free (fullpath); X continue; X } X X /* it's a file... leave it alone if it's new enough */ X if (st.st_mtime > age) { X free (fullpath); X continue; X } X X if (nodel) { X printf ("Would unlink %s\n", fullpath); X free (fullpath); X continue; X } X if (verbose) X printf ("Unlinking %s\n", fullpath); X X if (unlink (fullpath) == -1) X fprintf (stderr, X "%s: cannot unlink %s\n", progname, fullpath); X X free (fullpath); X X } /* while */ X closedir (dirp); X } /* dodir() */ SHAR_EOF chmod 0664 reap.c || echo 'restore of reap.c failed' Wc_c="`wc -c < 'reap.c'`" test 2150 -eq "$Wc_c" || echo 'reap.c: original size 2150, current size' "$Wc_c" fi # ============= reap.h ============== if test -f 'reap.h' -a X"$1" != X"-c"; then echo 'x - skipping reap.h (File already exists)' else echo 'x - extracting reap.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'reap.h' && X /* X * reap.h X * header file for reap utility X */ X #include <stdio.h> #include <string.h> #include <ctype.h> #include <time.h> #include <malloc.h> #include <sys/stat.h> #include <ustat.h> #include <dirent.h> X #define MAXLINE 512 /* max len of line in script */ #define SECINDAY (3600 * 24) /* seconds in a day */ X /* structure for linked lists of included and excluded file specs */ struct filspec { X char *name; X struct filspec *next; }; X Xextern struct filspec X *incl, X *excl ; Xextern char X *progname, X *scriptfile, X *newsdir ; Xextern int X verbose, X nodel, X optind ; Xextern char *optarg; Xextern double atof(); Xextern long freeblox(); Xextern time_t X age, X now ; SHAR_EOF chmod 0664 reap.h || echo 'restore of reap.h failed' Wc_c="`wc -c < 'reap.h'`" test 676 -eq "$Wc_c" || echo 'reap.h: original size 676, current size' "$Wc_c" fi exit 0 -- My friend Franky said he wanted to know something about computers that I didn't know, so I suggested he learn COBOL.