home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2159 < prev    next >
Internet Message Format  |  1990-12-28  |  25KB

  1. From: dt@yenta.alb.nm.us (David B. Thomas)
  2. Newsgroups: alt.sources,unix-pc.sources
  3. Subject: reap: expire news as space needed
  4. Message-ID: <1990Nov27.073400.7561@yenta.alb.nm.us>
  5. Date: 27 Nov 90 07:34:00 GMT
  6.  
  7. Hello, netters!  Here's an expire alternative.  If you have a small disk,
  8. this is for you.  It was inspired by Mike Murphy's "trasher" posted a few
  9. weeks ago.
  10.  
  11. Problem:
  12.     News volume fluctuates wildly...sites with small disks must either
  13. expire aggresively, often deleting more than necessary, or worry that an
  14. unexpected huge dose of news might overfill the disk.
  15.  
  16.  
  17. Solution:
  18.     Implement a tool that expires according to a user-defined scheme
  19. until sufficient freespace is reclaimed, then stops, leaving as much
  20. juicy news online as is feasible.  Reap does this.
  21.  
  22.  
  23. Expire Does Two Jobs:
  24.     Both Bnews and Cnews expires really do two jobs:
  25.         1. trim history files
  26.         2. delete outdated articles
  27. Thanks to some inspired jootsing (acronym for "jumping out of the system")
  28. by Mike Murphy (mrm@sceard.com) and others, it is more than possible to
  29. separate those two functions.  This is, of course, in keeping with the
  30. unix philosophy of one tool doing one job well!
  31.  
  32.  
  33. What Reap Does:
  34.     Reap only takes care of the second job: deleting old articles.
  35. It works by checking freespace, and processing one line at a time from a
  36. list of expire functions, until the desired freespace is attained.
  37. Each expire function consists of an age limit in days (decimals okay)
  38. and a list of newsgroups to process or not process, sys file style.  Ex:
  39.  
  40.     .5    alt.sex.pictures,talk,!talk.bizarre,junk
  41.     1    rec,!rec.games,!rec.humor
  42.  
  43. This example would check freespace, and if more space is needed, expire
  44. to .5 days everything in alt.sex.pictures,talk (except for talk.bizarre)
  45. and junk.  Then it would stop and check freespace again.  If still more
  46. space is needed, it would expire to 1 day everything in rec except
  47. rec.games.* and rec.humor.*.  It's that simple.
  48.  
  49.  
  50. Okay...So How Do I Arrange To Trim The History Files, Since Reap Can't:
  51.     Included in this distribution is a shell script (mostly written
  52. by Mike Murphy) to handle Cnews history files.  It shouldn't be too
  53. difficult to do something similar for Bnews, or you can give in and use
  54. the original expire utility with options that tell it to expire the
  55. history files only...but I think that would be slow.  It just comes down
  56. to removing lines from ordinary text files, based on their contents.
  57. Murphy and I used awk.
  58.  
  59.  
  60. But Is It Fast:
  61.     Yes, largely because it doesn't have to do much.  Even "find | rm"
  62. is slower because find is repeatedly exec-ing rm.
  63.  
  64.     Since the functions of deleting articles and trimming history
  65. are separate, I now run reap every few hours, but trim the history list
  66. just once a day.  That effectively keeps my disk space up to snuff, but
  67. only thrashes at the history file in the middle of the night.
  68.  
  69.  
  70. Credits:
  71.     I owe a lot to Mike Murphy for inspiring me with his "trasher"
  72. system.  I also owe a lot to all of your netters who will flood me with
  73. suggestions and improvements in the coming weeks (hint, hint!).
  74.  
  75. Note: this uses ustat(2), a system-V call to find disk freespace.  It should
  76. be a very easy hack to use the BSD equivalent.  I just don't know how to do
  77. it.
  78.  
  79.                         little david
  80.                         dt@yenta.alb.nm.us
  81.  
  82. -----snip-----------snip-----------snip-----------snip-----------snip------
  83. #!/bin/sh
  84. # This is a shell archive (shar 3.44)
  85. # made 11/27/1990 07:20 UTC by dt@yenta
  86. # Source directory /u/dt/reap
  87. #
  88. # existing files will NOT be overwritten unless -c is specified
  89. #
  90. # This shar contains:
  91. # length  mode       name
  92. # ------ ---------- ------------------------------------------
  93. #    730 -rw-rw-r-- INSTALL
  94. #    339 -rw-rw-r-- MANIFEST
  95. #    900 -rw-rw-r-- Makefile
  96. #   2792 -rw-rw-r-- README
  97. #    969 -rwxrwxr-x exphist
  98. #   2539 -rw-rw-r-- lib.c
  99. #    873 -rw-rw-r-- list.sample
  100. #   2028 -rw-rw-r-- main.c
  101. #   2335 -rw-r--r-- reap.8
  102. #   2150 -rw-rw-r-- reap.c
  103. #    676 -rw-rw-r-- reap.h
  104. #
  105. # ============= INSTALL ==============
  106. if test -f 'INSTALL' -a X"$1" != X"-c"; then
  107.     echo 'x - skipping INSTALL (File already exists)'
  108. else
  109. echo 'x - extracting INSTALL (Text)'
  110. sed 's/^X//' << 'SHAR_EOF' > 'INSTALL' &&
  111. X
  112. To install:
  113. X
  114. 1. Edit tops of Makefile and main.c to fit your system.
  115. 2. make
  116. 3. Compose a sample function list file or use the sample provided to
  117. X    test reap (use the -n option to avoid really removing files).
  118. 4. su and make install.
  119. 5. Arrange to trim your news system's history list regularly.  If you have
  120. X    Cnews, try the "exphist" script provided, run from cron.  That
  121. X    script requires that a tiny file, /usr/lib/news/histdays, contain
  122. X    the number of days of history data to keep, or else you can hardcode
  123. X    in your favorite number.  If you have Bnews, I don't have a nifty
  124. X    suggestion, other than using expire(8) with a ridiculously long
  125. X    article expire time, but a sane history expire time.
  126. 6. Arrange to run reap from cron.
  127. SHAR_EOF
  128. chmod 0664 INSTALL ||
  129. echo 'restore of INSTALL failed'
  130. Wc_c="`wc -c < 'INSTALL'`"
  131. test 730 -eq "$Wc_c" ||
  132.     echo 'INSTALL: original size 730, current size' "$Wc_c"
  133. fi
  134. # ============= MANIFEST ==============
  135. if test -f 'MANIFEST' -a X"$1" != X"-c"; then
  136.     echo 'x - skipping MANIFEST (File already exists)'
  137. else
  138. echo 'x - extracting MANIFEST (Text)'
  139. sed 's/^X//' << 'SHAR_EOF' > 'MANIFEST' &&
  140. Included with this distribution are:
  141. X
  142. README        - general description and credits
  143. INSTALL        - hints on how to install reap and get it going
  144. Xexphist        - history trimmer utility for Cnews
  145. list.sample    - sample function script file for reap
  146. reap.8        - man page
  147. lib.c        - source
  148. main.c        - source
  149. reap.c        - source
  150. reap.h        - source
  151. Makefile    - makefile
  152. SHAR_EOF
  153. chmod 0664 MANIFEST ||
  154. echo 'restore of MANIFEST failed'
  155. Wc_c="`wc -c < 'MANIFEST'`"
  156. test 339 -eq "$Wc_c" ||
  157.     echo 'MANIFEST: original size 339, current size' "$Wc_c"
  158. fi
  159. # ============= Makefile ==============
  160. if test -f 'Makefile' -a X"$1" != X"-c"; then
  161.     echo 'x - skipping Makefile (File already exists)'
  162. else
  163. echo 'x - extracting Makefile (Text)'
  164. sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
  165. #----------------------------
  166. # parameters for installation
  167. #----------------------------
  168. X
  169. # directories where manual and executable are to be installed
  170. BINDIR = /usr/lib/newsbin/expire
  171. MANDIR = /usr/man/man8
  172. X
  173. # owner, group and file mode for manual and executable
  174. EXEOWNER = bin
  175. EXEGROUP = bin
  176. EXEMODE = 775
  177. MANOWNER = bin
  178. MANGROUP = bin
  179. MANMODE = 664
  180. X
  181. X
  182. # your favorite C compiler
  183. #CC = cc
  184. CC = shcc
  185. X
  186. # directory access functions library, if not in standard C library.
  187. LIB = -ldirent
  188. X
  189. X
  190. #-------------
  191. # boring stuff
  192. #-------------
  193. X
  194. OBJ = main.o lib.o reap.o
  195. X
  196. reap: $(OBJ)
  197. X    $(CC) -s -o reap $(SHLIB) $(OBJ) $(LIB)
  198. X
  199. $(OBJ): reap.h
  200. X
  201. install: reap reap.8
  202. X    cp reap $(BINDIR)
  203. X    chown $(EXEOWNER) $(BINDIR)/reap
  204. X    chgrp $(EXEGROUP) $(BINDIR)/reap
  205. X    chmod $(EXEMODE) $(BINDIR)/reap
  206. X    cp reap.8 $(MANDIR)
  207. X    chmod $(MANMODE) $(MANDIR)/reap.8
  208. X    chown $(MANOWNER) $(MANDIR)/reap.8
  209. X    chgrp $(MANGROUP) $(MANDIR)/reap.8
  210. SHAR_EOF
  211. chmod 0664 Makefile ||
  212. echo 'restore of Makefile failed'
  213. Wc_c="`wc -c < 'Makefile'`"
  214. test 900 -eq "$Wc_c" ||
  215.     echo 'Makefile: original size 900, current size' "$Wc_c"
  216. fi
  217. # ============= README ==============
  218. if test -f 'README' -a X"$1" != X"-c"; then
  219.     echo 'x - skipping README (File already exists)'
  220. else
  221. echo 'x - extracting README (Text)'
  222. sed 's/^X//' << 'SHAR_EOF' > 'README' &&
  223. X
  224. A Problem:
  225. X    News volume fluctuates wildly...sites with small disks must either
  226. Xexpire aggresively, often deleting more than necessary, or worry that an
  227. unexpected huge dose of news might overfill the disk.
  228. X
  229. X
  230. A Solution:
  231. X    Implement a tool that expires according to a user-defined scheme
  232. until sufficient freespace is reclaimed, then stops, leaving as much
  233. juicy news online as is feasible.  Reap does this.
  234. X
  235. X
  236. Expire Does Two Jobs:
  237. X    Both Bnews and Cnews expires really do two jobs:
  238. X        1. trim history files
  239. X        2. delete outdated articles
  240. Thanks to some inspired jootsing (acronym for "jumping out of the system")
  241. by Mike Murphy (mrm@sceard.com) and others, it is more than possible to
  242. separate those two functions.  This is, of course, in keeping with the
  243. unix philosophy of one tool doing one job well!
  244. X
  245. X
  246. What Reap Does:
  247. X    Reap only takes care of the second job: deleting old articles.
  248. It works by checking freespace, and processing one line at a time from a
  249. list of expire functions, until the desired freespace is attained.
  250. Each expire function consists of an age limit in days (decimals okay)
  251. and a list of newsgroups to process or not process, sys file style.  Ex:
  252. X
  253. X    .5    alt.sex.pictures,talk,!talk.bizarre,junk
  254. X    1    rec,!rec.games,!rec.humor
  255. X
  256. This example would check freespace, and if more space is needed, expire
  257. to .5 days everything in alt.sex.pictures,talk (except for talk.bizarre)
  258. and junk.  Then it would stop and check freespace again.  If still more
  259. space is needed, it would expire to 1 day everything in rec except
  260. rec.games.* and rec.humor.*.  It's that simple.
  261. X
  262. X
  263. Okay...So How Do I Arrange To Trim The History Files, Since Reap Can't:
  264. X    Included in this distribution is a shell script (mostly written
  265. by Mike Murphy) to handle Cnews history files.  It shouldn't be too
  266. difficult to do something similar for Bnews, or you can give in and use
  267. the original expire utility with options that tell it to expire the
  268. history files only...but I think this will be slow.  It just comes down
  269. to removing lines from ordinary text files, based on their contents.
  270. Murphy and I used awk.
  271. X
  272. X
  273. But Is It Fast:
  274. X    Yes, largely because it doesn't have to do much.  Even "find | rm"
  275. is slower because find is repeatedly exec-ing rm.  "rm -rf" has me beat,
  276. though, I'll bet! :-)
  277. X
  278. X    Since the functions of deleting articles and trimming history
  279. are separate, I now run reap every six hours, but trim the history list
  280. just once a day.  That effectively keeps my disk space up to snuff, but
  281. only thrashes at the history file in the middle of the night.
  282. X
  283. X
  284. Credits:
  285. X    I owe a lot to Mike Murphy for inspiring me with his "trasher"
  286. system.  I also owe a lot to all of your netters who will flood me with
  287. suggestions and improvements in the coming weeks (hint, hint!).
  288. X
  289. X                        little david
  290. X                        dt@yenta.alb.nm.us
  291. SHAR_EOF
  292. chmod 0664 README ||
  293. echo 'restore of README failed'
  294. Wc_c="`wc -c < 'README'`"
  295. test 2792 -eq "$Wc_c" ||
  296.     echo 'README: original size 2792, current size' "$Wc_c"
  297. fi
  298. # ============= exphist ==============
  299. if test -f 'exphist' -a X"$1" != X"-c"; then
  300.     echo 'x - skipping exphist (File already exists)'
  301. else
  302. echo 'x - extracting exphist (Text)'
  303. sed 's/^X//' << 'SHAR_EOF' > 'exphist' &&
  304. #! /bin/sh
  305. # exphist - expire history file
  306. X
  307. # =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
  308. . ${NEWSCONFIG-/usr/lib/news/bin/config}
  309. X
  310. PATH=$NEWSCTL/bin:$NEWSBIN/expire:$NEWSBIN:$NEWSPATH ; export PATH
  311. umask $NEWSUMASK
  312. X
  313. days=`cat /usr/lib/news/histdays`
  314. X
  315. lock="$NEWSCTL/LOCKexpire"
  316. ltemp="$NEWSCTL/L.$$"
  317. Xecho $$ >$ltemp
  318. trap "rm -f $ltemp ; exit 0" 0 1 2 15
  319. if newslock $ltemp $lock
  320. then
  321. X    trap "rm -f $ltemp $lock ; exit 0" 0 1 2 15
  322. Xelse
  323. X    echo "$0: expire apparently already running" | mail "$NEWSMASTER"
  324. X    exit 1
  325. fi
  326. X
  327. cd $NEWSCTL
  328. rm -f history.n history.n.pag history.n.dir
  329. now=`getdate now`
  330. age=`expr 86400 \* $days`
  331. ago=`expr $now - $age`
  332. awk "{split(\$2,dates,\"~\");if(dates[1]>$ago)print \$0}" history >history.n
  333. mkdbm history.n
  334. [ -s history.n ] &&
  335. mv history history.o &&    # install new ASCII history file
  336. mv history.n history &&
  337. rm -f history.pag &&       # and related dbm files
  338. rm -f history.dir &&
  339. mv history.n.pag history.pag &&
  340. mv history.n.dir history.dir
  341. Xexit 0
  342. SHAR_EOF
  343. chmod 0775 exphist ||
  344. echo 'restore of exphist failed'
  345. Wc_c="`wc -c < 'exphist'`"
  346. test 969 -eq "$Wc_c" ||
  347.     echo 'exphist: original size 969, current size' "$Wc_c"
  348. fi
  349. # ============= lib.c ==============
  350. if test -f 'lib.c' -a X"$1" != X"-c"; then
  351.     echo 'x - skipping lib.c (File already exists)'
  352. else
  353. echo 'x - extracting lib.c (Text)'
  354. sed 's/^X//' << 'SHAR_EOF' > 'lib.c' &&
  355. X
  356. /*
  357. X * lib.c
  358. X *    subroutines needed by reap utility
  359. X */
  360. X
  361. #include "reap.h"
  362. X
  363. /*
  364. X * freeblox(devno)
  365. X *
  366. X *    assuming devno is the device number of a file system,
  367. X *    this function returns the free space on that file system,
  368. X *    in blocks (whatever that means for that filesystem) as a
  369. X *    long int.
  370. X *
  371. X */
  372. X
  373. freeblox(devno)
  374. int    devno;
  375. {
  376. X    static struct ustat    ust;
  377. X
  378. X    if (ustat(devno, &ust)) {
  379. X        fprintf (stderr, "%s: ustat() failed\n", progname);
  380. X        exit (-1);
  381. X    }
  382. X    return (ust.f_tfree);
  383. X
  384. } /* freeblox() */
  385. X
  386. X
  387. X
  388. X
  389. /*
  390. X * parse(cmd)
  391. X *    execute a line from the script file
  392. X *
  393. X * valid command lines:
  394. X *    a blank line is ignored
  395. X *    # comment (ignored)
  396. X *    days filespecs
  397. X *    
  398. X */
  399. X
  400. parse(cmd)
  401. char    *cmd;
  402. {
  403. X    double        days;
  404. X    char        *t,
  405. X            *p,
  406. X            *q,
  407. X            *seps = ",\n ";
  408. X
  409. X
  410. /* skip initial whitespace */
  411. X    p = cmd;
  412. X    while (isspace(*p))
  413. X        ++p;
  414. X
  415. /* ignore blank lines or comments */
  416. X    if (*p == '\0' || *p == '#')
  417. X        return(1);
  418. X
  419. /* read the expire time from the line as a floating point number,
  420. X * then figure out what the timestamp on a file that old would be */
  421. X    age = now - (time_t) (atof(p) * SECINDAY);
  422. X
  423. /* skip ahead to the filespec list */
  424. X    while (!isspace(*p))
  425. X        ++p;
  426. X    while (isspace(*p))
  427. X        ++p;
  428. X
  429. X    preclude();
  430. X    for (t = strtok(p, seps); t; t = strtok(NULL, seps)) {
  431. X    /* forbid starting with a slash */
  432. X        if (*t == '/')
  433. X            *t = '.';
  434. X    /* change dots to slashes except in column 1 */
  435. X        while ( (q = strchr(t+1,'.')) != NULL)
  436. X            *q = '/';
  437. X        if (*t == '!')
  438. X            exclude (t+1);
  439. X        else
  440. X            include (t);
  441. X    }
  442. X
  443. X    reap();
  444. X
  445. X    return (0);
  446. X
  447. } /* parse() */
  448. X
  449. X
  450. X
  451. /*
  452. X * newnode,exclude, include, preclude
  453. X *
  454. X *    functions to maintain global linked lists:
  455. X *        incl    include this path in list of dirs to recursively reap
  456. X *        excl    leave out this directory and descendants
  457. X *
  458. X *    the functions are:
  459. X *        include (text)    add text to incl list
  460. X *        exclude (text)    add text to excl list
  461. X *        preclude()    clear both lists
  462. X */
  463. struct filspec *
  464. newnode(text)
  465. char    *text;
  466. {
  467. X    struct filspec    *f;
  468. X
  469. X    if ( (f = (struct filspec *) malloc (sizeof(struct filspec))) == NULL ||
  470. X        (f->name = malloc (strlen(text)+1)) == NULL) {
  471. X        fprintf (stderr, "%s: out of memory\n", progname);
  472. X        exit (-1);
  473. X    }
  474. X    strcpy (f->name, text);
  475. X    return (f);
  476. }
  477. Xexclude(text)
  478. char        *text;
  479. {
  480. X    struct filspec    *f;
  481. X
  482. X    f = newnode(text);
  483. X    f->next = excl;
  484. X    excl = f;
  485. }
  486. include(text)
  487. char    *text;
  488. {
  489. X    struct filspec    *f;
  490. X
  491. X    f = newnode(text);
  492. X    f->next = incl;
  493. X    incl = f;
  494. }
  495. preclude()
  496. {
  497. X    struct filspec    *p;
  498. X
  499. X    while (incl != NULL) {
  500. X        p = incl;
  501. X        incl = incl->next;
  502. X        free (p);
  503. X    }
  504. X    while (excl != NULL) {
  505. X        p = excl;
  506. X        excl = excl->next;
  507. X        free (p);
  508. X    }
  509. }
  510. SHAR_EOF
  511. chmod 0664 lib.c ||
  512. echo 'restore of lib.c failed'
  513. Wc_c="`wc -c < 'lib.c'`"
  514. test 2539 -eq "$Wc_c" ||
  515.     echo 'lib.c: original size 2539, current size' "$Wc_c"
  516. fi
  517. # ============= list.sample ==============
  518. if test -f 'list.sample' -a X"$1" != X"-c"; then
  519.     echo 'x - skipping list.sample (File already exists)'
  520. else
  521. echo 'x - extracting list.sample (Text)'
  522. sed 's/^X//' << 'SHAR_EOF' > 'list.sample' &&
  523. .1 junk,alt.flame,control,general
  524. 1 alt.sex.pictures
  525. 1 comp.os.vms
  526. .5 talk
  527. 1 sci,!sci.skeptic
  528. 2 sci.skeptic
  529. .5 misc.jobs,misc.handicap
  530. 2 soc
  531. 2 alt.tv
  532. 1 biz
  533. .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
  534. 1 rec.music,!rec.music.synth
  535. 3 rec.music.synth
  536. 2 rec.arts.anime,rec.arts.animation,rec.arts.movies,rec.ham-radio,rec.autos,rec.arts.tv,rec.audio,rec.video
  537. 3 rec.games.hack
  538. 1 misc.headlines,misc.consumers,misc.kids,misc.legal
  539. 1 alt.sex,!alt.sex.bondage,!alt.sex.pictures
  540. 1 comp.sources
  541. 1 alt.sources
  542. 2 comp,!comp.os.vms
  543. 1 news
  544. 3 misc
  545. 3 pubnet
  546. 3 alt
  547. 3 unix-pc
  548. # up to here is "normal expire"... now let's get desparate
  549. .1 alt.sex.pictures
  550. .1 rec.arts.anime,rec.arts.animation,rec.arts.movies,rec.ham-radio,rec.autos,rec.arts.tv,rec.audio,rec.video
  551. .1 biz
  552. .1 talk
  553. .1 news
  554. SHAR_EOF
  555. chmod 0664 list.sample ||
  556. echo 'restore of list.sample failed'
  557. Wc_c="`wc -c < 'list.sample'`"
  558. test 873 -eq "$Wc_c" ||
  559.     echo 'list.sample: original size 873, current size' "$Wc_c"
  560. fi
  561. # ============= main.c ==============
  562. if test -f 'main.c' -a X"$1" != X"-c"; then
  563.     echo 'x - skipping main.c (File already exists)'
  564. else
  565. echo 'x - extracting main.c (Text)'
  566. sed 's/^X//' << 'SHAR_EOF' > 'main.c' &&
  567. X
  568. /*
  569. X * main.c
  570. X *    main routine and globals for reap utility.
  571. X */
  572. X
  573. X
  574. /* directory containing news articles */
  575. #define NEWSDIR        "/usr/spool/news"
  576. X
  577. X
  578. #include "reap.h"
  579. X
  580. /* linked lists for included and excluded file specs */
  581. struct filspec
  582. X        *incl = NULL,
  583. X        *excl = NULL
  584. ;
  585. char
  586. X        *scriptfile = "/usr/lib/news/reaplist",
  587. X        *progname,
  588. X        *newsdir = NEWSDIR
  589. ;
  590. int
  591. X        verbose = 0,    /* verbosity flag */
  592. X        nodel = 0    /* do-not-unlink flag */
  593. ;
  594. time_t
  595. X        age,        /* unlink files older than this    */
  596. X        now        /* current time            */
  597. ;
  598. X
  599. X
  600. X
  601. main(argc, argv)
  602. int    argc;
  603. char    *argv[];
  604. {
  605. X    int        device,        /* dev.no. of disk containing newsdir */
  606. X            c,
  607. X            errflag = 0;
  608. X    long        wantblox;    /* desired free space */
  609. X    char        *p;
  610. X    static char    line[MAXLINE];
  611. X    FILE        *script;    /* file ptr for function script */
  612. X    struct stat    st;
  613. X
  614. X
  615. /* parse args */
  616. X    progname = argv[0];
  617. X
  618. X    while ( (c=getopt(argc,argv,"vnf:")) != EOF)
  619. X        switch(c) {
  620. X            case 'n':
  621. X            ++nodel;
  622. X            break;
  623. X            case 'v':
  624. X            ++verbose;
  625. X            break;
  626. X            case 'f':
  627. X            scriptfile = optarg;
  628. X            break;
  629. X            case '?':
  630. X            ++errflag;
  631. X            break;
  632. X        }
  633. X    
  634. X    if (argc - optind != 1 ||
  635. X        sscanf(argv[optind], "%ld", &wantblox) != 1 )
  636. X        ++errflag;
  637. X
  638. X    if (errflag) {
  639. X        fprintf (stderr,
  640. X            "usage: %s [-v] [-n] [-f funclist] freeblocks\n", progname);
  641. X        exit (-1);
  642. X    }
  643. X
  644. X
  645. /* stat newsdir to find the number of the device it's on */
  646. X    if (stat(newsdir, &st)) {
  647. X        fprintf (stderr, "%s: can't stat %s\n", progname, newsdir);
  648. X        exit (-1);
  649. X    }
  650. X    device = st.st_dev;
  651. X
  652. /* open function file for reading */
  653. X    if ( (script = fopen (scriptfile, "r")) == NULL) {
  654. X        fprintf (stderr, "%s: can't read %s\n", progname, scriptfile);
  655. X        exit (-1);
  656. X    }
  657. X
  658. /* when am i? */
  659. X    time (&now);
  660. X
  661. /* chdir to newsdir */
  662. X    chdir (newsdir);
  663. X
  664. /* main loop is right here ... process until goal reached or end of script */
  665. X
  666. X    while (freeblox(device) < wantblox &&
  667. X        (p = fgets (line, MAXLINE, script)) != NULL )
  668. X        parse (line);
  669. X
  670. X    fclose (script);
  671. X
  672. /* return 0 if freespace goal was met at exit, 1 if not */
  673. X    exit (freeblox(device) >= wantblox ? 0 : 1);
  674. X
  675. } /* main() */
  676. X
  677. SHAR_EOF
  678. chmod 0664 main.c ||
  679. echo 'restore of main.c failed'
  680. Wc_c="`wc -c < 'main.c'`"
  681. test 2028 -eq "$Wc_c" ||
  682.     echo 'main.c: original size 2028, current size' "$Wc_c"
  683. fi
  684. # ============= reap.8 ==============
  685. if test -f 'reap.8' -a X"$1" != X"-c"; then
  686.     echo 'x - skipping reap.8 (File already exists)'
  687. else
  688. echo 'x - extracting reap.8 (Text)'
  689. sed 's/^X//' << 'SHAR_EOF' > 'reap.8' &&
  690. .TH REAP 8 LOCAL
  691. .SH NAME
  692. reap - delete news articles as space needed
  693. .SH SYNOPSIS
  694. .B reap
  695. [-v] [-n] [-f funcfile] freeblocks
  696. .SH DESCRIPTION
  697. .I Reap
  698. checks disk freespace and deletes articles according to a user-specified
  699. scheme until
  700. .I freeblocks
  701. minimum freespace is available.
  702. .PP
  703. The
  704. .B -n
  705. causes a dry run (as in make(1)).  When this
  706. option is specified, reap merely reports which files it would delete if
  707. the option were left off.  This is useful for debugging function scripts.
  708. However, since no space is actually freed in this mode,
  709. it will either do nothing
  710. (there was enough space to begin with) or proceed through the entire function
  711. script file, indicating that reap would delete all applicable articles.
  712. .PP
  713. With the
  714. .B
  715. -v
  716. option, reap prints a verbose commentary on its progress to the standard
  717. output.
  718. .PP
  719. By default, reap looks in /usr/lib/news/reaplist for its list of functions.
  720. The
  721. .B -f
  722. option is used to specify an alternate function script file.
  723. .SH FUNCTION SCRIPT FILE FORMAT
  724. A function script file consists of a series of expire functions, one per line.
  725. Each expire function contains an age (in days, decimals okay), followed
  726. by a list of newsgroups to be expired.
  727. Blank lines and lines beginning with a "#" are ignored.
  728. A sample function script file:
  729. .PP
  730. .nf
  731. X    # function script file
  732. X    0.5    talk,junk,alt.sex.pictures
  733. X    2    rec,!rec.games,!rec.humor
  734. X    3    misc
  735. .fi
  736. .PP
  737. In the example,
  738. if space is needed, reap
  739. Xexpires to .5 days the entire talk and junk
  740. hierarchies, and alt.sex.pictures.  If
  741. still more space is needed,
  742. rec hierarchy (excepting rec.games.* and rec.humor.*) is expired
  743. to 2 days.  If freespace is still short, reap then expires all of misc
  744. to 2 days.
  745. .PP
  746. Note that, while it would be possible to consolidate the second and
  747. third lines, leaving them separate makes it posisble for reap to stop
  748. in between them if sufficient space is cleared.
  749. .SH FILES
  750. .TP 25
  751. /usr/spool/news
  752. News spool directory
  753. .TP 25
  754. /usr/lib/news/reaplist
  755. Default function list file
  756. .SH DIAGNOSTICS
  757. Returns zero if sufficient space was free at exit, nonzero otherwise.
  758. .SH BUGS
  759. The use of the ustat() system call is not very portable, and your humble
  760. author isn't aware of its non-system-V equivalents.
  761. .PP
  762. Lines in the function file are limited to 512 characters.
  763. .PP
  764. What constitutes a disk "block" is implementation-dependent.
  765. SHAR_EOF
  766. chmod 0644 reap.8 ||
  767. echo 'restore of reap.8 failed'
  768. Wc_c="`wc -c < 'reap.8'`"
  769. test 2335 -eq "$Wc_c" ||
  770.     echo 'reap.8: original size 2335, current size' "$Wc_c"
  771. fi
  772. # ============= reap.c ==============
  773. if test -f 'reap.c' -a X"$1" != X"-c"; then
  774.     echo 'x - skipping reap.c (File already exists)'
  775. else
  776. echo 'x - extracting reap.c (Text)'
  777. sed 's/^X//' << 'SHAR_EOF' > 'reap.c' &&
  778. X
  779. /*
  780. X * reap.c
  781. X *    contains the reap() function.
  782. X *
  783. X * Once the global linked lists incl and excl have been stuffed,
  784. X * reap() actually scans the filesystem for files that meet the specs
  785. X * and unlinks them if they are older than the age global variable.
  786. X */
  787. X
  788. #include "reap.h"
  789. X
  790. X
  791. reap()
  792. {
  793. X    struct filspec    *f;
  794. X
  795. X    for (f = incl; f != NULL; f = f->next) {
  796. X        if (verbose)
  797. X            printf ("scanning %s ...\n", f->name);
  798. X        dodir (f->name);
  799. X    }
  800. X
  801. } /* reap() */
  802. X
  803. X
  804. X
  805. dodir(name)
  806. char    *name;
  807. {
  808. X    /* the following can be overwritten safely during recursion */
  809. X    static struct filspec    *e;
  810. X    static struct stat    st;
  811. X    static struct dirent    *dp;
  812. X    /* the following must be preserved during recursion */
  813. X    char            *fullpath;
  814. X    DIR            *dirp;
  815. X
  816. X
  817. X    if ( (dirp = opendir(name)) == NULL) {
  818. X        fprintf (stderr, "%s: can't read directory %s\n", progname,
  819. X            name);
  820. X        return (-1);
  821. X    }
  822. X
  823. X    while ( (dp = readdir(dirp)) != NULL) {
  824. X
  825. X    /* skip dot and dotdot */
  826. X        if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
  827. X            continue;
  828. X
  829. X    /* build the full pathname of each object */
  830. X        if ( (fullpath =
  831. X            malloc(strlen(name)+strlen(dp->d_name)+2)) == NULL) {
  832. X            fprintf (stderr, "%s: out of memory\n", progname);
  833. X            exit (-1);
  834. X        }
  835. X        sprintf (fullpath, "%s/%s", name, dp->d_name);
  836. X
  837. X    /* check for excluded paths */
  838. X        for (e = excl; e != NULL; e = e->next)
  839. X            if (!strcmp (fullpath, e->name))
  840. X                break;
  841. X        if (e != NULL) {
  842. X            free (fullpath);
  843. X            continue;
  844. X        }
  845. X
  846. X    /* try to stat the object */
  847. X        if (stat(fullpath,&st)) {
  848. X            fprintf (stderr, "%s: can't stat %s\n",
  849. X                progname, fullpath);
  850. X            free (fullpath);
  851. X            continue;
  852. X        }
  853. X
  854. X    /* recurse if it's a directory */
  855. X        if ( st.st_mode & S_IFDIR ) {
  856. X            dodir (fullpath);
  857. X            free (fullpath);
  858. X            continue;
  859. X        }
  860. X
  861. X    /* it's a file... leave it alone if it's new enough */
  862. X        if (st.st_mtime > age) {
  863. X            free (fullpath);
  864. X            continue;
  865. X        }
  866. X
  867. X        if (nodel) {
  868. X            printf ("Would unlink %s\n", fullpath);
  869. X            free (fullpath);
  870. X            continue;
  871. X        }
  872. X        if (verbose)
  873. X            printf ("Unlinking %s\n", fullpath);
  874. X
  875. X        if (unlink (fullpath) == -1)
  876. X            fprintf (stderr,
  877. X                "%s: cannot unlink %s\n", progname, fullpath);
  878. X
  879. X        free (fullpath);
  880. X
  881. X    } /* while */
  882. X    closedir (dirp);
  883. X
  884. } /* dodir() */
  885. SHAR_EOF
  886. chmod 0664 reap.c ||
  887. echo 'restore of reap.c failed'
  888. Wc_c="`wc -c < 'reap.c'`"
  889. test 2150 -eq "$Wc_c" ||
  890.     echo 'reap.c: original size 2150, current size' "$Wc_c"
  891. fi
  892. # ============= reap.h ==============
  893. if test -f 'reap.h' -a X"$1" != X"-c"; then
  894.     echo 'x - skipping reap.h (File already exists)'
  895. else
  896. echo 'x - extracting reap.h (Text)'
  897. sed 's/^X//' << 'SHAR_EOF' > 'reap.h' &&
  898. X
  899. /*
  900. X * reap.h
  901. X *    header file for reap utility
  902. X */
  903. X
  904. #include <stdio.h>
  905. #include <string.h>
  906. #include <ctype.h>
  907. #include <time.h>
  908. #include <malloc.h>
  909. #include <sys/stat.h>
  910. #include <ustat.h>
  911. #include <dirent.h>
  912. X
  913. #define MAXLINE        512        /* max len of line in script    */
  914. #define SECINDAY    (3600 * 24)    /* seconds in a day        */
  915. X
  916. /* structure for linked lists of included and excluded file specs */
  917. struct filspec {
  918. X    char        *name;
  919. X    struct filspec    *next;
  920. };
  921. X
  922. Xextern struct filspec
  923. X        *incl,
  924. X        *excl
  925. ;
  926. Xextern char
  927. X        *progname,
  928. X        *scriptfile,
  929. X        *newsdir
  930. ;
  931. Xextern int
  932. X        verbose,
  933. X        nodel,
  934. X        optind
  935. ;
  936. Xextern char    *optarg;
  937. Xextern double    atof();
  938. Xextern long    freeblox();
  939. Xextern time_t
  940. X        age,
  941. X        now
  942. ;
  943. SHAR_EOF
  944. chmod 0664 reap.h ||
  945. echo 'restore of reap.h failed'
  946. Wc_c="`wc -c < 'reap.h'`"
  947. test 676 -eq "$Wc_c" ||
  948.     echo 'reap.h: original size 676, current size' "$Wc_c"
  949. fi
  950. exit 0
  951. -- 
  952. My friend Franky said he wanted to know something about computers that I
  953. didn't know, so I suggested he learn COBOL.
  954.