home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume10 / agef next >
Encoding:
Internet Message Format  |  1987-08-06  |  24.1 KB

  1. Path: uunet!rs
  2. From: rs@uunet.UU.NET (Rich Salz)
  3. Newsgroups: comp.sources.unix
  4. Subject: v10i100:  Show disk usage by file age
  5. Message-ID: <792@uunet.UU.NET>
  6. Date: 7 Aug 87 11:28:29 GMT
  7. Organization: UUNET Communications Services, Arlington, VA
  8. Lines: 905
  9. Approved: rs@uunet.UU.NET
  10.  
  11. Submitted-by: sundc!hqda-ai!merlin@seismo.CSS.GOV (David S. Hayes)
  12. Posting-number: Volume 10, Issue 100
  13. Archive-name: agef
  14.  
  15. [  This is like "du" for files that are, e.g., 1-5 days old, 6-10, etc.
  16.    As David says, it's useful for Usenet administration.  --r$  ]
  17.  
  18. This doesn't seem like much, but it is very helpful when working
  19. with news spooling directories.  I run this over my news every
  20. night, to see where my disk space is being used.
  21.  
  22. Rich:  when you get this, please send an knowledgement back so I
  23. know it didn't get lost in transit.
  24.  
  25.  
  26. David S. Hayes, The Merlin of Avalon    PhoneNet:  (202) 694-6900
  27. UUCP:  *!seismo!sundc!hqda-ai!merlin    ARPA:  merlin%hqda-ai@seismo.css.gov
  28.  
  29.  
  30.  
  31. # This is a shell archive.  Remove anything before this line, then
  32. # unpack it by saving it in a file and typing "sh file".  (Files
  33. # unpacked will be owned by you and have default permissions.)
  34. #
  35. # This archive contains:
  36. # README Makefile agef.8 agef.c customize.h direct.c hash.c hash.h patchlevel.h
  37. #
  38. #!/bin/sh
  39. echo x - README
  40. cat > "README" << '//E*O*F README//'
  41.      This is the third revision of the AGEF (please pronounce this
  42. AGE-F, for "age files") program, which was initially posted to
  43. net.sources March 2, 1987.  I expected this to be a sleeper, but
  44. many people seem to use it, and I have received far more comments
  45. and suggestions than I anticipated.
  46.  
  47. CHANGES:
  48.      Multiply-linked inodes are only counted once.  As each inode
  49. is examined, the device/inode pair in entered into a hash table.
  50. The hashing code is included; you don't need any library support
  51. for it.
  52.  
  53.      AGEF has been dependent on the UCB directory reading
  54. routines.  Public-domain routines for System V have been released
  55. to the Usenet (comp.sources.unix) by Doug Gwyn (gwyn@brl.mil).
  56. AGEF has been modified to use them.  If you don't have them,
  57. they're worth your trouble to get.  Still, you may be able to use
  58. the System III configuration of the Makefile as a stopgap measure.
  59.  
  60.      The age categories may now be entered on the command line.
  61. Use the -d (days) switch.
  62.  
  63.      The program can now age by inode change time (-c), file
  64. modification time (-m), or time of last access (-a).
  65.  
  66. THANKS:
  67.      I am particularly indebted to the following people. Paul
  68. Czarnecki (harvard!munsell!pac) suggested the display of sizes in
  69. megabytes when the numbers get too big, the use of st_blocks to
  70. show actual disk blocks used, and gave me the code for
  71. user-specified age categories.
  72.  
  73.      Anders Andersson (enea!kuling!andersa) suggested the method
  74. of handling multiply-linked inodes.  His suggestion neatly
  75. prevented double-counting, and also allows the handling of "." and
  76. ".." as arguments.  AGEF previously choked on those.
  77.  
  78.      Paul Czarnecki, Anders Andersson, Karl Nyberg, Andrew Partan,
  79. and Joel McClung acted as my alpha-testers.  Cyrus Rahman, Sid
  80. Shapiro, Lyndon Nerenberg, and Lloyd Taylor were the beta-test
  81. crew. My thanks to all of them.
  82.  
  83.      I am pleased to see that my work has been useful.  If you
  84. find bugs in it, I'd like to hear about them.  Happy hacking,
  85.  
  86. David S. Hayes, The Merlin of Avalon
  87. Phone: (202) 694-6900
  88. UUCP:  *!mimsy!hqda-ai!merlin
  89. ARPA:  merlin%hqda-ai@mimsy.umd.EDU
  90. Smail: merlin@hqda-ai.UUCP
  91. //E*O*F README//
  92.  
  93. echo x - Makefile
  94. cat > "Makefile" << '//E*O*F Makefile//'
  95. #    Build AGEF v3
  96. #
  97. #    SCCS ID:    @(#)Makefile    1.6    7/9/87
  98. #
  99. #    Define the type of system we're working with.  Three
  100. # choices:
  101. #
  102. #   1.    BSD Unix 4.2 or 4.3.  Directory read support in the
  103. # standard library, so we don't have to do much.  Select BSD.
  104. #
  105. #   2.    System V.  I depend on Doug Gwyn's directory reading
  106. # routines.  They were posted to Usenet "comp.sources" early in
  107. # May 1987.  They're worth the effort to get, if you don't have
  108. # them already.  Select SYS_V.  Be sure to define NLIB to be the
  109. # 'cc' option to include the directory library.
  110. #
  111. #   3.  System III, or machines without any directory read
  112. # packages.  I have a minimal kludge.  Select SYS_III.
  113. #
  114.  
  115. # Case 1:
  116. SYS=    -DBSD
  117. NLIB=
  118.  
  119. # Case 2:
  120. #SYS=    -DSYS_V
  121. #NLIB=    -lndir
  122.  
  123. # Case 3:
  124. #SYS=    -DSYS_III
  125. #NLIB=
  126.  
  127.  
  128. #    Standard things you may wish to change:
  129. #
  130. #    INSTALL        directory to install agef in
  131.  
  132. INSTALL    =    /usr/local/bin
  133.  
  134.  
  135. #    The following OPTIONS may be defined:
  136. #
  137. #    LSTAT        we have the lstat(2) system call (BSD only)
  138. #    HSTATS        print hashing statistics at end of run
  139. #
  140. #    Define LSTAT, HSTATS here
  141.  
  142. OPTIONS    =    -DLSTAT
  143.  
  144. #  END OF USER-DEFINED OPTIONS
  145.  
  146.  
  147. CFLAGS=    -O $(SYS) $(OPTIONS)
  148. SRCS=    agef.c    hash.c    direct.c    \
  149.     hash.h    customize.h    patchlevel.h
  150. OBJS=    agef.o    hash.o
  151.  
  152. install:    agef
  153.     install -m 0511 agef $(INSTALL)
  154.  
  155. clean:
  156.     rm -f $(OBJS) agef *~
  157.  
  158. agef:    $(OBJS)
  159.     cc -o agef $(CFLAGS) $(OBJS) $(NLIB)
  160.  
  161. agef.o:    agef.c direct.c hash.h customize.h patchlevel.h
  162.  
  163. hash.o:    hash.c hash.h customize.h patchlevel.h
  164. //E*O*F Makefile//
  165.  
  166. echo x - agef.8
  167. cat > "agef.8" << '//E*O*F agef.8//'
  168. .\"SCCS ID    @(#)agef.8    1.6    7/9/87
  169. .TH AGEF 8 "28 March 1987"
  170. .SH SYNOPSIS
  171. .B agef
  172. [-m | -a | -c ]
  173. [-l]
  174. [-d days-list]
  175. .I file file ...
  176. .SH DESCRPITION
  177. .B Agef
  178. is a tool intended to help manage the expiration of Usenet news.
  179. It displays a table of file sizes and counts, sorted by age.
  180. Each argument has one line in the table.  
  181. The columns show the number of files, and their total size.
  182. Normally, each argument would be a directory.
  183. .B Agef
  184. displays the total for all files in the directory,
  185. and recursively through all subdirectories.
  186. If no arguments are given, 
  187. .B agef
  188. examines the current directory.
  189. .PP
  190. .B Agef
  191. works with inodes, not files.
  192. Each inode examined is remembered internally.
  193. If it is subsequently encountered again, it is ignored.
  194. This will occur in the case of news articles cross-posted to
  195. several different newsgroups.  There is only one file, but there
  196. is a link to it in the directory of every newsgroup where the
  197. article was posted.
  198. .PP
  199. Because 
  200. .B agef
  201. is designed to work with news articles, it does not count
  202. the sizes of directory files in its tallies.  
  203. .PP
  204. File ages are based on the modification time (default).
  205. This value is set whenever the file is written.
  206. Changes in ownership and permission do not affect it.
  207. .SH OPTIONS
  208. .IP -l
  209. Do not follow symbolic links.
  210. This applies to BSD systems only.
  211. Without this switch, 
  212. .B agef
  213. counts the file the link refers to.
  214. With -l, it counts the link itself.
  215. .IP -m
  216. Use date of last modification.
  217. .IP -c
  218. Use date of last inode change.
  219. .IP -a
  220. Use date of last access.
  221. .IP "-d \fIdays-list\fP"
  222. Specify the age categories.
  223. .I days-list
  224. is a list of comma-separated numbers.
  225. The default is "7,14,30,45".
  226. .B Agef
  227. will add two additional columns.
  228. The first counts files older than the oldest specified time.
  229. The second is a total for all files.
  230. The times must be specified in ascending numerical sequence.
  231. .ne 15v
  232. .SH EXAMPLE
  233. .nf
  234. % cd /usr/man
  235. % agef -a -d 7,14,21 man[1-8]
  236.     7 days    14 days    21 days    Over 21      Total  Name
  237. ---------- ---------- ---------- ---------- ----------  ----
  238.   25  195k    4   26k   11   76k  398 1398k  438 1695k  man1
  239.    2    5k                        133  394k  135  399k  man2
  240.   10   34k               1    5k  784 1218k  795 1257k  man3
  241.                                    61  351k   61  351k  man4
  242.    3    5k               3   12k   66  300k   72  317k  man5
  243.                                    28   50k   28   50k  man6
  244.                                    10   45k   10   45k  man7
  245.    5   26k               3   20k  152  434k  160  480k  man8
  246.  
  247.   45  265k    4   26k   18  113k 1632 4190k 1699 4594k  Grand Total
  248. .fi
  249. .SH AUTHOR
  250. David S. Hayes, Site Manager, US Army Artificial Intelligence
  251. Center.
  252. This program is in the public domain.
  253. This manual page describes version 3, which is a considerable
  254. improvement over the original.
  255. Much of the credit for this goes to Paul Czarnecki and
  256. Anders Andersson for their suggestions and bug fixes.
  257. .SH BUGS
  258. This program uses the directory reading routines of 4.2BSD.
  259. Suitable directory routines are available from the
  260. Usenet comp.sources archives, courtesy of Doug Gwyn (doug@brl.mil),
  261. to allow this program to run under System V.
  262. .LP
  263. .B Agef
  264. uses the
  265. .I st_blocks
  266. value from
  267. .I stat(2)
  268. to determine file size.
  269. Files under 4.2BSD may contain "holes", that is, a 1-megabyte file
  270. may not actually have enough disk blocks allocated to hold a
  271. megabyte. 
  272. The sizes reported are indicative of the actual number of disk
  273. blocks used.
  274. This is not a bug, just a word of caution.
  275. .LP
  276. Other bugs may be reported to the author via e-mail to 
  277. .sp
  278. .nf
  279. .in +.5i
  280. Internet:       merlin%hqda-ai@seismo.CSS.GOV
  281. UUCP:           seismo!sundc!hqda-ai!merlin
  282. Smart mailers:  merlin@hqda-ai.UUCP
  283. //E*O*F agef.8//
  284.  
  285. echo x - agef.c
  286. cat > "agef.c" << '//E*O*F agef.c//'
  287. /* agef
  288.   
  289.    SCCS ID    @(#)agef.c    1.6    7/9/87
  290.   
  291.    David S. Hayes, Site Manager
  292.    Army Artificial Intelligence Center
  293.    Pentagon HQDA (DACS-DMA)
  294.    Washington, DC  20310-0200
  295.   
  296.    Phone:  202-694-6900
  297.    Email:  merlin@hqda-ai.UUCP   merlin%hqda-ai@seismo.CSS.GOV
  298.   
  299.    +=======================================+
  300.    | This program is in the public domain. |
  301.    +=======================================+
  302.   
  303.    This program scans determines the amount of disk space taken up
  304.    by files in a named directory.  The space is broken down
  305.    according to the age of the files.  The typical use for this
  306.    program is to determine what the aging breakdown of news
  307.    articles is, so that appropriate expiration times can be
  308.    established.
  309.   
  310.    Call via
  311.   
  312.     agef fn1 fn2 fn3 ...
  313.   
  314.    If any of the given filenames is a directory (the usual case),
  315.    agef will recursively descend into it, and the output line will
  316.    reflect the accumulated totals for all files in the directory.
  317. */
  318.  
  319. #include "patchlevel.h"
  320.  
  321. #include <ctype.h>
  322. #include <sys/types.h>
  323. #include <sys/stat.h>
  324. #include <sys/param.h>
  325. #include <stdio.h>
  326.  
  327. #include "customize.h"
  328. #include "hash.h"
  329.  
  330. #define SECS_DAY    (24L * 60L * 60L)    /* seconds in one day */
  331. #define TOTAL        (n_ages-1)    /* column number of total
  332.                      * column */
  333. #define SAME        0    /* for strcmp */
  334. #define MAXUNS        ((unsigned) -1L)
  335. #define MAXAGES        40    /* max number of age columns */
  336. #define BLOCKSIZE    512    /* size of a disk block */
  337.  
  338. #define K(x)        ((x+1) >> 1)    /* convert stat(2) blocks into
  339.                      * k's.  On my machine, a block
  340.                      * is 512 bytes. */
  341.  
  342.  
  343. extern char    *optarg;        /* from getopt(3) */
  344. extern int      optind,
  345.                 opterr;
  346.  
  347.  
  348. char           *Program;    /* our name */
  349.  
  350. short           sw_follow_links = 1;    /* follow symbolic links */
  351.  
  352. /* Types of inode times for sw_time. */
  353. #define    MODIFIED    1
  354. #define CHANGED        2
  355. #define ACCESSED    3
  356. short           sw_time = MODIFIED;
  357. short           sw_summary;    /* print Grand Total line */
  358.  
  359.  
  360. short           n_ages = 0;    /* how many age categories */
  361. unsigned        ages[MAXAGES];    /* age categories */
  362. int             inodes[MAXAGES];/* inode count */
  363. long            sizes[MAXAGES];    /* block count */
  364.  
  365. char            topdir[NAMELEN];/* our starting directory */
  366. long            today,
  367.                 time();        /* today's date */
  368.  
  369.  
  370.  
  371. main(argc, argv)
  372.     int             argc;
  373.     char           *argv[];
  374. {
  375.     int             i,
  376.                     j;
  377.     int             option;
  378.     int             total_inodes[MAXAGES];    /* for grand totals */
  379.     long            total_sizes[MAXAGES];
  380.  
  381.     Program = *argv;        /* save our name for error messages */
  382.  
  383.     /* Pick up options from command line */
  384.     while ((option = getopt(argc, argv, "smacd:")) != EOF) {
  385.     switch (option) {
  386.       case 's':
  387.         sw_follow_links = 0;
  388.         break;
  389.  
  390.       case 'm':
  391.         sw_time = MODIFIED;
  392.         break;
  393.  
  394.       case 'a':
  395.         sw_time = ACCESSED;
  396.         break;
  397.  
  398.       case 'c':
  399.         sw_time = CHANGED;
  400.         break;
  401.  
  402.       case 'd':
  403.         n_ages = 0;
  404.         while (*optarg) {
  405.         ages[n_ages] = atoi(optarg);    /* get day number */
  406.         if (ages[n_ages] == 0)    /* check, was it a number */
  407.             break;    /* no, exit the while loop */
  408.         n_ages++;
  409.         while (isdigit(*optarg))    /* advance over the
  410.                          * digits */
  411.             optarg++;
  412.         if (*optarg == ',')    /* skip a comma separator */
  413.             optarg++;
  414.  
  415.         if (n_ages > (MAXAGES - 2)) {
  416.             fprintf(stderr, "too many ages, max is %d\n", MAXAGES - 2);
  417.             exit(-1);
  418.         };
  419.         };
  420.         ages[n_ages++] = MAXUNS;    /* "Over" column */
  421.         ages[n_ages++] = MAXUNS;    /* "Total" column */
  422.         break;
  423.     };
  424.     };
  425.  
  426.     /* If user didn't specify ages, make up some that sound good. */
  427.     if (!n_ages) {
  428.     n_ages = 6;
  429.     ages[0] = 7;
  430.     ages[1] = 14;
  431.     ages[2] = 30;
  432.     ages[3] = 45;
  433.     ages[4] = MAXUNS;
  434.     ages[5] = MAXUNS;
  435.     };
  436.  
  437.     /* If user didn't specify targets, inspect current directory. */
  438.     if (optind >= argc) {
  439.     argc = 2;
  440.     argv[1] = ".";
  441.     };
  442.  
  443.     sw_summary = argc > 2;    /* should we do a grant total? */
  444.  
  445.     getwd(topdir);        /* find out where we are */
  446.     today = time(0) / SECS_DAY;
  447.  
  448.     make_headers();        /* print column heades */
  449.  
  450.     /* Zero out grand totals */
  451.     for (i = 0; i < n_ages; i++)
  452.     total_inodes[i] = total_sizes[i] = 0;
  453.  
  454.     /* Inspect each argument */
  455.     for (i = optind; i < argc; i++) {
  456.     for (j = 0; j < n_ages; j++)
  457.         inodes[j] = sizes[j] = 0;
  458.  
  459.     chdir(topdir);        /* be sure to start from the same place */
  460.     get_data(argv[i]);    /* this may change our cwd */
  461.  
  462.     display(argv[i], inodes, sizes);
  463.     for (j = 0; j < n_ages; j++) {
  464.         total_inodes[j] += inodes[j];
  465.         total_sizes[j] += sizes[j];
  466.     };
  467.     };
  468.  
  469.     if (sw_summary) {
  470.     putchar('\n');        /* blank line */
  471.     display("Grand Total", total_inodes, total_sizes);
  472.     };
  473.  
  474. #ifdef HSTATS
  475.     fflush(stdout);
  476.     h_stats();
  477. #endif
  478.     exit(0);
  479. };
  480.  
  481.  
  482.  
  483.  /*
  484.   * Get the aged data on a file whose name is given.  If the file is a
  485.   * directory, go down into it, and get the data from all files inside. 
  486.   */
  487. get_data(path)
  488.     char           *path;
  489. {
  490.     struct stat     stb;
  491.     int             i;
  492.     long            age;    /* file age in days */
  493.  
  494. #ifdef LSTAT
  495.     if (sw_follow_links)
  496.     stat(path, &stb);    /* follows symbolic links */
  497.     else
  498.     lstat(path, &stb);    /* doesn't follow symbolic links */
  499. #else
  500.     stat(path, &stb);
  501. #endif
  502.  
  503.     /* Don't do it again if we've already done it once. */
  504.     if (h_enter(stb.st_dev, stb.st_ino) == OLD)
  505.     return;
  506.  
  507.     if ((stb.st_mode & S_IFMT) == S_IFDIR)
  508.     down(path);
  509.     if ((stb.st_mode & S_IFMT) == S_IFREG) {
  510.     switch (sw_time) {
  511.       case MODIFIED:
  512.         age = today - stb.st_mtime / SECS_DAY;
  513.         break;
  514.       case CHANGED:
  515.         age = today - stb.st_ctime / SECS_DAY;
  516.         break;
  517.       case ACCESSED:
  518.         age = today - stb.st_atime / SECS_DAY;
  519.         break;
  520.     };
  521.  
  522.     for (i = 0; i < TOTAL; i++) {
  523.         if (age <= ages[i]) {
  524.         inodes[i]++;
  525.         sizes[i] += K(stb.st_blocks);
  526.         break;
  527.         };
  528.     };
  529.     inodes[TOTAL]++;
  530.     sizes[TOTAL] += K(stb.st_blocks);
  531.     };
  532. }
  533.  
  534.  
  535.  
  536.  /*
  537.   * We ran into a subdirectory.  Go down into it, and read everything
  538.   * in there. 
  539.   */
  540.  
  541. down(subdir)
  542.     char           *subdir;
  543. {
  544.     OPEN           *dp;        /* stream from a directory */
  545.     char            cwd[NAMELEN];
  546.     READ           *file;    /* directory entry */
  547.  
  548.     if ((dp = opendir(subdir)) == NULL) {
  549.     fprintf(stderr, "%s: can't read %s/%s\n", Program, topdir, subdir);
  550.     return;
  551.     };
  552.  
  553.     getwd(cwd);            /* remember where we are */
  554.     chdir(subdir);        /* go into subdir */
  555.     for (file = readdir(dp); file != NULL; file = readdir(dp))
  556.     if (strcmp(NAME(*file), "..") != SAME)
  557.         get_data(NAME(*file));
  558.  
  559.     chdir(cwd);            /* go back where we were */
  560.     closedir(dp);        /* shut down the directory */
  561. }
  562.  
  563.  
  564.  
  565.  /*
  566.   * Print one line of the table. 
  567.   */
  568. display(name, inodes, sizes)
  569.     char           *name;
  570.     int             inodes[];
  571. long            sizes[];
  572. {
  573.     char            tmpstr[30];
  574.     int             i;
  575.  
  576.     for (i = 0; i < n_ages; i++) {
  577.     tmpstr[0] = '\0';
  578.     if (inodes[i] || i == TOTAL) {
  579.         if (sizes[i] < 10000)
  580.         sprintf(tmpstr, "%d %4ldk", inodes[i], sizes[i]);
  581.         else
  582.         sprintf(tmpstr, FLOAT_FORMAT, inodes[i], sizes[i] / 1000.0);
  583.     };
  584.     printf("%10s ", tmpstr);
  585.     };
  586.     printf(" %s\n", name);
  587. }
  588.  
  589.  
  590.  /*
  591.   * Print column headers, given the ages. 
  592.   */
  593. make_headers()
  594. {
  595.     char            header[15];
  596.     int             i;
  597.  
  598.     for (i = 0; i < TOTAL; i++) {
  599.     if (ages[i] == MAXUNS)
  600.         sprintf(header, "Over %d", ages[i - 1]);
  601.     else
  602.         sprintf(header, "%d %s", ages[i], ages[i] > 1 ? "days" : "day");
  603.     printf("%10s ", header);
  604.     };
  605.     printf("     Total  Name\n");
  606.     for (i = 0; i < n_ages; i++)
  607.     printf("---------- ");
  608.     printf(" ----\n");
  609. }
  610. //E*O*F agef.c//
  611.  
  612. echo x - customize.h
  613. cat > "customize.h" << '//E*O*F customize.h//'
  614. /* agef
  615.   
  616.    SCCS ID    @(#)customize.h    1.6    7/9/87
  617.   
  618.    This is the customizations file.  It changes our ideas of
  619.    how to read directories.
  620. */
  621.  
  622. #define FLOAT_FORMAT    "%d %#4.1fM"    /* if your printf does %# */
  623. /*#define FLOAT_FORMAT    "%d %4.1fM" /* if it doesn't do %# */
  624.  
  625. #define NAMELEN    512        /* max size of a full pathname */
  626.  
  627. #ifdef BSD
  628. #    include        <sys/dir.h>
  629. #    define    OPEN    DIR
  630. #    define    READ    struct direct
  631. #    define    NAME(x)    ((x).d_name)
  632. #else
  633. #ifdef SYS_V
  634.  /* Customize this.  This is part of Doug Gwyn's package for */
  635.  /* reading directories.  If you've put this file somewhere */
  636.  /* else, edit the next line. */
  637.  
  638. #    include        <sys/dirent.h>
  639.  
  640. #    define    OPEN    struct direct
  641. #    define    READ    struct dirent
  642. #    define    NAME(x)    ((x).d_name)
  643. #else
  644. #ifdef SYS_III
  645. #    define    OPEN    FILE
  646. #    define    READ    struct direct
  647. #    define    NAME(x)    ((x).d_name)
  648. #    define    INO(x)    ((x).d_ino)
  649.  
  650. #    include        "direct.c"
  651.  
  652. #endif
  653. #endif
  654. #endif
  655. //E*O*F customize.h//
  656.  
  657. echo x - direct.c
  658. cat > "direct.c" << '//E*O*F direct.c//'
  659. /* direct.c
  660.   
  661.    SCCS ID    @(#)direct.c    1.6    7/9/87
  662.   
  663.  *
  664.  *    My own substitution for the berkeley reading routines,
  665.  *    for use on System III machines that don't have any other
  666.  *    alternative.
  667.  */
  668.  
  669. #define NAMELENGTH    14
  670. #define opendir(name)    fopen(name, "r")
  671. #define closedir(fp)    fclose(fp)
  672.  
  673. struct dir_entry {        /* What the system uses internally. */
  674.     ino_t           d_ino;
  675.     char            d_name[NAMELENGTH];
  676. };
  677.  
  678. struct direct {            /* What these routines return. */
  679.     ino_t           d_ino;
  680.     char            d_name[NAMELENGTH];
  681.     char            terminator;
  682. };
  683.  
  684.  
  685.  /*
  686.   * Read a directory, returning the next (non-empty) slot. 
  687.   */
  688.  
  689. READ           *
  690. readdir(dp)
  691.     OPEN           *dp;
  692. {
  693.     static READ     direct;
  694.  
  695.     /* This read depends on direct being similar to dir_entry. */
  696.  
  697.     while (fread(&direct, sizeof(struct dir_entry), 1, dp) != 0) {
  698.     direct.terminator = '\0';
  699.     if (INO(direct) != 0)
  700.         return &direct;
  701.     };
  702.  
  703.     return (READ *) NULL;
  704. }
  705. //E*O*F direct.c//
  706.  
  707. echo x - hash.c
  708. cat > "hash.c" << '//E*O*F hash.c//'
  709. /* hash.c
  710.   
  711.    SCCS ID    @(#)hash.c    1.6    7/9/87
  712.   
  713.  * Hash table routines for AGEF.  These routines keep the program from
  714.  * counting the same inode twice.  This can happen in the case of a
  715.  * file with multiple links, as in a news article posted to several
  716.  * groups.  The use of a hashing scheme was suggested by Anders
  717.  * Andersson of Uppsala University, Sweden.  (enea!kuling!andersa) 
  718.  */
  719.  
  720. /* hash.c change history:
  721.  28 March 1987        David S. Hayes (merlin@hqda-ai.UUCP)
  722.     Initial version.
  723. */
  724.  
  725. #include <stdio.h>
  726. #include <sys/types.h>
  727. #include "hash.h"
  728.  
  729.  
  730. static struct htable *tables[TABLES];
  731.  
  732. /* These are for statistical use later on. */
  733. static int      hs_tables = 0,    /* number of tables allocated */
  734.                 hs_duplicates = 0,    /* number of OLD's returned */
  735.                 hs_buckets = 0,    /* number of buckets allocated */
  736.                 hs_extensions = 0,    /* number of bucket extensions */
  737.                 hs_searches = 0,/* number of searches */
  738.                 hs_compares = 0,/* total key comparisons */
  739.                 hs_longsearch = 0;    /* longest search */
  740.  
  741.  
  742.  /*
  743.   * This routine takes in a device/inode, and tells whether it's been
  744.   * entered in the table before.  If it hasn't, then the inode is added
  745.   * to the table.  A separate table is maintained for each major device
  746.   * number, so separate file systems each have their own table. 
  747.   */
  748.  
  749. h_enter(dev, ino)
  750.     dev_t           dev;
  751.     ino_t           ino;
  752. {
  753.     static struct htable *tablep = (struct htable *) 0;
  754.     register struct hbucket *bucketp;
  755.     register ino_t *keyp;
  756.     int             i;
  757.  
  758.     hs_searches++;        /* stat, total number of calls */
  759.  
  760.     /*
  761.      * Find the hash table for this device. We keep the table pointer
  762.      * around between calls to h_enter, so that we don't have to locate
  763.      * the correct hash table every time we're called.  I don't expect
  764.      * to jump from device to device very often. 
  765.      */
  766.     if (!tablep || tablep->device != dev) {
  767.     for (i = 0; tables[i] && tables[i]->device != dev;)
  768.         i++;
  769.     if (!tables[i]) {
  770.         tables[i] = (struct htable *) malloc(sizeof(struct htable));
  771.         if (tables[i] == NULL) {
  772.         perror("can't malloc hash table");
  773.         return NEW;
  774.         };
  775.         bzero(tables[i], sizeof(struct htable));
  776.         tables[i]->device = dev;
  777.         hs_tables++;    /* stat, new table allocated */
  778.     };
  779.     tablep = tables[i];
  780.     };
  781.  
  782.     /* Which bucket is this inode assigned to? */
  783.     bucketp = &tablep->buckets[ino % BUCKETS];
  784.  
  785.     /*
  786.      * Now check the key list for that bucket.  Just a simple linear
  787.      * search. 
  788.      */
  789.     keyp = bucketp->keys;
  790.     for (i = 0; i < bucketp->filled && *keyp != ino;)
  791.     i++, keyp++;
  792.  
  793.     hs_compares += i + 1;    /* stat, total key comparisons */
  794.  
  795.     if (i && *keyp == ino) {
  796.     hs_duplicates++;    /* stat, duplicate inodes */
  797.     return OLD;
  798.     };
  799.  
  800.     /* Longest search.  Only new entries could be the longest. */
  801.     if (bucketp->filled >= hs_longsearch)
  802.     hs_longsearch = bucketp->filled + 1;
  803.  
  804.     /* Make room at the end of the bucket's key list. */
  805.     if (bucketp->filled == bucketp->length) {
  806.     /* No room, extend the key list. */
  807.     if (!bucketp->length) {
  808.         bucketp->keys = (ino_t *) calloc(EXTEND, sizeof(ino_t));
  809.         if (bucketp->keys == NULL) {
  810.         perror("can't malloc hash bucket");
  811.         return NEW;
  812.         };
  813.         hs_buckets++;
  814.     } else {
  815.         bucketp->keys = (ino_t *)
  816.         realloc(bucketp->keys,
  817.             (EXTEND + bucketp->length) * sizeof(ino_t));
  818.         if (bucketp->keys == NULL) {
  819.         perror("can't extend hash bucket");
  820.         return NEW;
  821.         };
  822.         hs_extensions++;
  823.     };
  824.     bucketp->length += EXTEND;
  825.     };
  826.  
  827.     bucketp->keys[++(bucketp->filled)] = ino;
  828.     return NEW;
  829. }
  830.  
  831.  
  832.  /* Buffer statistics functions.  Print 'em out. */
  833.  
  834. #ifdef HSTATS
  835. void
  836. h_stats()
  837. {
  838.     fprintf(stderr, "\nHash table management statistics:\n");
  839.     fprintf(stderr, "  Tables allocated: %d\n", hs_tables);
  840.     fprintf(stderr, "  Buckets used: %d\n", hs_buckets);
  841.     fprintf(stderr, "  Bucket extensions: %d\n\n", hs_extensions);
  842.     fprintf(stderr, "  Total searches: %d\n", hs_searches);
  843.     fprintf(stderr, "  Duplicate keys found: %d\n", hs_duplicates);
  844.     if (hs_searches)
  845.     fprintf(stderr, "  Average key search: %d\n",
  846.         hs_compares / hs_searches);
  847.     fprintf(stderr, "  Longest key search: %d\n", hs_longsearch);
  848.     fflush(stderr);
  849. }
  850.  
  851. #endif
  852. //E*O*F hash.c//
  853.  
  854. echo x - hash.h
  855. cat > "hash.h" << '//E*O*F hash.h//'
  856. /* Defines for the agef hashing functions.
  857.  
  858.    SCCS ID    @(#)hash.h    1.6    7/9/87
  859.  */
  860.  
  861. #define BUCKETS        257    /* buckets per hash table */
  862. #define TABLES        50    /* hash tables */
  863. #define EXTEND        100    /* how much space to add to a bucket */
  864.  
  865. struct hbucket {
  866.     int             length;    /* key space allocated */
  867.     int             filled;    /* key space used */
  868.     ino_t          *keys;
  869. };
  870.  
  871. struct htable {
  872.     dev_t           device;    /* device this table is for */
  873.     struct hbucket  buckets[BUCKETS];    /* the buckets of the table */
  874. };
  875.  
  876. #define OLD    0        /* inode was in hash already */
  877. #define NEW    1        /* inode has been added to hash */
  878. //E*O*F hash.h//
  879.  
  880. echo x - patchlevel.h
  881. cat > "patchlevel.h" << '//E*O*F patchlevel.h//'
  882. /* Patchlevel for AGEF.
  883.  
  884.    SCCS ID    @(#)patchlevel.h    1.6    7/9/87
  885. */
  886.  
  887. #define PATCHLEVEL V3.0
  888. //E*O*F patchlevel.h//
  889.  
  890. echo Possible errors detected by \'wc\' [hopefully none]:
  891. temp=/tmp/shar$$
  892. trap "rm -f $temp; exit" 0 1 2 3 15
  893. cat > $temp <<\!!!
  894.       50     333    2136 README
  895.       69     252    1458 Makefile
  896.      115     603    3716 agef.8
  897.      323    1088    7614 agef.c
  898.       41     148     900 customize.h
  899.       46     128     970 direct.c
  900.      143     597    4283 hash.c
  901.       22     106     625 hash.h
  902.        6      13      92 patchlevel.h
  903.      815    3268   21794 total
  904. !!!
  905. wc  README Makefile agef.8 agef.c customize.h direct.c hash.c hash.h patchlevel.h | sed 's=[^ ]*/==' | diff -b $temp -
  906. exit 0
  907. -- 
  908. David S. Hayes, The Merlin of Avalon    PhoneNet:  (202) 694-6900
  909. UUCP:  *!seismo!sundc!hqda-ai!merlin    ARPA:  merlin%hqda-ai@seismo.css.gov
  910.  
  911. -- 
  912.  
  913. Rich $alz            "Anger is an energy"
  914. Cronus Project, BBN Labs    rsalz@bbn.com
  915. Moderator, comp.sources.unix    sources@uunet.uu.net
  916.