home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume39 / enh-du2 / part01 / duentry.c < prev    next >
C/C++ Source or Header  |  1993-08-21  |  10KB  |  370 lines

  1. #ifndef lint
  2. static char SCCSID[] = "@(#) duentry.c 1.18 93/08/18 00:09:03";
  3. #endif
  4.  
  5. /*
  6.  * "du" enhanced disk usage summary - version 2.
  7.  *
  8.  * Copyright 1990-1993, Unicom Systems Development.  All rights reserved.
  9.  * See accompanying README file for terms of distribution and use.
  10.  *
  11.  * Edit at tabstops=4.
  12.  */
  13.  
  14. #include "config.h"
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #ifdef USE_UNISTD
  19. # include <unistd.h>
  20. #endif
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include "du.h"
  24.  
  25. #ifdef USE_DIR_DIRENT
  26. #    include <dirent.h>
  27. #endif
  28. #ifdef USE_DIR_SYSNDIR
  29. #    include <sys/ndir.h>
  30. #    define dirent direct
  31. #endif
  32. #ifdef USE_DIR_SYSDIR
  33. #    include <sys/dir.h>
  34. #    define dirent direct
  35. #endif
  36.  
  37. #ifndef USE_SYMLINKS
  38. #    define lstat stat
  39. #endif
  40.  
  41.  
  42. /*
  43.  * Determine maximum pathlength of an entry on the filesystem containing "file".
  44.  */
  45. int max_path_len(fname)
  46. register char *fname;
  47. {
  48. #ifdef USE_PATHCONF
  49.     register int len;
  50.     if ((len = pathconf(fname, _PC_PATH_MAX)) > 0)
  51.         return len;
  52. #ifdef notdef
  53.     errmssg(ERR_WARN, errno, "could not get max pathname for \"%s\"", fname);
  54. #endif
  55. #endif
  56.  
  57. /*
  58.  * If pathconf() is not available or fails, we need to take a stab.  If
  59.  * the POSIX PATH_MAX parameter is available we will use that.  If not,
  60.  * we will try to use the MAXNAMLEN that should be in whatever header
  61.  * file USE_DIR_XXXX pulled in.  If all that fails, we'll just make a
  62.  * guess of 1024.  Note that we do NOT want to use _POSIX_PATH_MAX.  That
  63.  * specifies the minimum value that a conformant system must support.
  64.  */
  65. #ifndef PATH_MAX
  66. # ifdef MAXNAMLEN
  67. #  define PATH_MAX MAXNAMLEN
  68. # else
  69. #  define PATH_MAX 1024
  70. # endif
  71. #endif
  72.     return PATH_MAX;
  73. }
  74.  
  75.  
  76. /*
  77.  * du_entry - Initiate a disk usage report for a specified filesystem entry.
  78.  *
  79.  * This routine is called once per command line argument to setup a disk
  80.  * usage scan.  This routine prepares to call "du_dir()" recursively to
  81.  * scan through a directory to accumulate its usage.
  82.  */
  83. void du_entry(entry, ent_usage)
  84. char *entry;                    /* pathname of the entry to check        */
  85. struct dusage *ent_usage;        /* space in which to return disk usage    */
  86. {
  87.     struct stat sbuf;
  88.     struct fsinfo *fsinfo;
  89.     static char *curr_dir = NULL;
  90.  
  91. #ifdef TIMEOUT
  92.     alarm(TIMEOUT);
  93. #endif
  94.  
  95.     /*
  96.      * Get the information on this entry.
  97.      */
  98.     if (lstat(entry, &sbuf) != 0) {
  99.         errmssg(ERR_WARN, errno, "could not stat \"%s\"", entry);
  100.         return;
  101.     }
  102.     if ((fsinfo = fs_getinfo(&sbuf)) == NULL) {
  103.         errmssg(ERR_WARN, errno,
  104.             "could not get filesystem info for \"%s\"", entry);
  105.         return;
  106.     }
  107.  
  108.     /*
  109.      * If this isn't a directory then simply get its size.  Note that we
  110.      * won't say what size is unless "-s" or "-a" was specified.  Kind of
  111.      * silly, but that's documented behaviour.
  112.      */
  113.     if (!S_ISDIR(sbuf.st_mode)) {
  114.         set_usage(ent_usage, sbuf.st_mtime, fs_numblocks(fsinfo, &sbuf));
  115.         if (Handle_output != PR_DIRS_ONLY)
  116.             print_usage(entry, ent_usage);
  117.         return;
  118.     }
  119.  
  120. #ifdef OBSOLETEOPT
  121.     /*
  122.      * If we aren't supposed to descend into directories then just get
  123.      * it's size.
  124.      */
  125.     if (!Do_descend_dirs) {
  126.         set_usage(ent_usage, sbuf.st_mtime, fs_numblocks(fsinfo, &sbuf));
  127.         print_usage(entry, ent_usage);
  128.         return;
  129.     }
  130. #endif
  131.  
  132.     /*
  133.      * We are about to descend down this directory entry.  We need to
  134.      * know where we started out so that we can return when done.
  135.      */
  136.     if (curr_dir == NULL) {
  137.         int len;
  138.         extern char *getcwd();
  139.         len = max_path_len(".") + 2; /* getcwd() wants two extra bytes */
  140.         curr_dir = (char *) xmalloc((unsigned)len);
  141.         if (errno = 0, getcwd(curr_dir, len) == NULL)
  142.             errmssg(ERR_ABORT, errno,
  143.                 "could not get current working directory");
  144.     }
  145.  
  146.     /*
  147.      * We need to initiate a recursive search through "du_dir()".  Normally
  148.      * "du_dir()" displays the disk usage of the directory scanned, except
  149.      * when "PR_TOTALS_ONLY" (-s) mode is set.  In that case it won't
  150.      * print anything at all, so we need to do it here.
  151.      */
  152.     zero_usage(ent_usage);
  153.     if (chdir(entry) != 0) {
  154.         errmssg(ERR_WARN, errno, "could not chdir to \"%s\"", entry);
  155.         return;
  156.     }
  157.     if (du_dir(entry, &sbuf, fsinfo, ent_usage) == 0) {
  158.         if (Handle_output == PR_TOTALS_ONLY)
  159.             print_usage(entry, ent_usage);
  160.     }
  161.     if (chdir(curr_dir) != 0)
  162.         errmssg(ERR_ABORT, errno, "could not chdir back to \"%s\"", curr_dir);
  163.  
  164. }
  165.  
  166.  
  167. /*
  168.  * du_dir() - Scan a specified directory and accumulate its disk usage.
  169.  *
  170.  * This routine recursively descends a directory tree accumulating and
  171.  * printing the disk usage.  The current working directory must be set
  172.  * to the directory to be scanned before this routine is called.  If the
  173.  * scan is successful, 0 will be returned and the accumlated usage
  174.  * will be stored in "dir_usage".  If an error occurs which prevents
  175.  * further processing, a -1 result will be returned and the usage
  176.  * results should be ignored as bogus.
  177.  */
  178. int du_dir(dir_name, dir_statp, dir_fsinfo, dir_usage)
  179. char *dir_name;                    /* pathname to the directory to scan    */
  180. struct stat    *dir_statp;            /* inode info for this directory        */
  181. struct fsinfo *dir_fsinfo;        /* filesys info for this directory        */
  182. register struct dusage *dir_usage; /* space in which to accumulate usage*/
  183. {
  184.     register struct dirent *dp;    /* current dir entry being checked        */
  185.     struct stat ent_stat;        /* inode info for this entry            */
  186.     struct fsinfo *ent_fsinfo;    /* filesys info for this entry            */
  187.     long numblocks;                /* num disk blocks used by this entry    */
  188.     struct dusage ent_usage;    /* disk usage by this entry                */
  189.     char *ent_pathname;            /* full pathname of this entry            */
  190.     register char *ent_basename;/* pointer into "ent_pathname[]"        */
  191.     DIR *dirp;                    /* stream for directory being scanned    */
  192.  
  193.     /*
  194.      * Allocate space to hold the pathname.  If you've got a good malloc
  195.      * this isn't too painful.  I profiled malloc() at 0.4% overhead.
  196.      */
  197.     ent_pathname = (char *) xmalloc((unsigned)dir_fsinfo->path_max+1);
  198.  
  199.     /*
  200.      * Initialize the block count with the usage of the directory itself.
  201.      */
  202.     numblocks = fs_numblocks(dir_fsinfo, dir_statp);
  203.     set_usage(dir_usage, dir_statp->st_mtime, numblocks);
  204.  
  205.     /*
  206.      * Setup a buffer to hold the full pathname of the entry being examined.
  207.      * Unless this directory is the root directory "/", append a slash
  208.      * to the name.  When we need the full pathname, we just need to place
  209.      * the filename at the location pointed to by "ent_basename".
  210.      */
  211.     ent_basename = strcpy(ent_pathname, dir_name) + strlen(dir_name);
  212.     if (ent_pathname[0] != '/' || ent_pathname[1] != '\0')
  213.         *ent_basename++ = '/';
  214.  
  215.     /*
  216.      * Open up the directory so we can scan it.
  217.      */
  218.     if ((dirp = opendir(".")) == NULL) {
  219.         errmssg(ERR_WARN, errno, "could not open dir \"%s\"", dir_name);
  220.         (void) free((PTRTYPE *)ent_pathname);
  221.         return -1;
  222.     }
  223.  
  224.     /*
  225.      * Go through each entry in the directory.
  226.      */
  227.     while ((dp = readdir(dirp)) != NULL) {
  228.  
  229. #ifdef TIMEOUT
  230.         alarm(TIMEOUT);
  231. #endif
  232.  
  233.         /*
  234.          * Skip the "." and ".." entries.
  235.          */
  236.         if (
  237.             dp->d_name[0] == '.' && (
  238.                 dp->d_name[1] == '\0' ||
  239.                 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
  240.             )
  241.         ) {
  242.             continue;
  243.         }
  244.  
  245.         /*
  246.          * Get the information on this entry.
  247.          */
  248.         if (lstat(dp->d_name, &ent_stat) != 0) {
  249.             errmssg(ERR_WARN, errno,
  250.                 "could not stat \"%s/%s\"", dir_name, dp->d_name);
  251.             continue;
  252.         }
  253.  
  254.         /*
  255.          * If it's not a directory then just accumulate its disk usage.
  256.          */
  257.         if (!S_ISDIR(ent_stat.st_mode)) {
  258.  
  259.             /*
  260.              * See what to do with entries with multiple links.
  261.              */
  262.             if (ent_stat.st_nlink > 1) {
  263.                 switch (Handle_links) {
  264.                 case LK_COUNT_FIRST:        /* count only first link */
  265.                     if (fs_linkdone(dir_fsinfo, &ent_stat))
  266.                         continue;
  267.                     break;
  268.                 case LK_COUNT_ALL:            /* count all hard links */
  269.                     break;
  270.                 case LK_COUNT_NONE:            /* skip all hard links */
  271.                     continue;
  272.                 case LK_COUNT_AVERAGE:        /* average usage over links */
  273.                     if (S_ISREG(ent_stat.st_mode)) {
  274.                         long nb = fs_numblocks(dir_fsinfo, &ent_stat);
  275.                         numblocks = nb / ent_stat.st_nlink;
  276.                         if (!fs_linkdone(dir_fsinfo, &ent_stat))
  277.                             numblocks += (nb % ent_stat.st_nlink);
  278.                         goto got_numblocks;
  279.                     }
  280.                     break;
  281.                 default:
  282.                     errmssg(ERR_ABORT, 0,
  283.                         "internal error - bad \"Handle_links\" value \"%d\"",
  284.                         Handle_links);
  285.                 }
  286.             }
  287.  
  288.             /*
  289.              * Get the usage of this entry and accumulate into the dir usage.
  290.              */
  291.             numblocks = fs_numblocks(dir_fsinfo, &ent_stat);
  292. got_numblocks:
  293.             set_usage(&ent_usage, ent_stat.st_mtime, numblocks);
  294.             add_usage(dir_usage, &ent_usage);
  295.             if (Handle_output == PR_EVERYTHING) {
  296.                 (void) strcpy(ent_basename, dp->d_name);
  297.                 print_usage(ent_pathname, &ent_usage);
  298.             }
  299.  
  300.         } else {
  301.  
  302.             /*
  303.              * We are probably going to need the pathname, so fill it out now.
  304.              */
  305.             (void) strcpy(ent_basename, dp->d_name);
  306.  
  307.             /*
  308.              * See if we are crossing a mount point -- and if so check if we
  309.              * want to process it.  Furthermore, if we cross a mount point then
  310.              * we need to get the filesystem information on the subdirectory.
  311.              * If we aren't crossing a mount point then the current filesystem
  312.              * info applies.
  313.              */
  314.             if (dir_statp->st_dev != ent_stat.st_dev) {
  315.                 if ((ent_fsinfo = fs_getinfo(&ent_stat)) == NULL) {
  316.                     errmssg(ERR_WARN, errno,
  317.                         "could not get filesystem info for \"%s\"",
  318.                         ent_pathname);
  319.                     continue;
  320.                 }
  321.                 switch (Handle_filesys) {
  322.                 case FS_ALWAYS_CROSS:        /* cross mount points */
  323.                     break;
  324.                 case FS_NEVER_CROSS:        /* do not cross mount points */
  325.                     continue;
  326.                 case FS_LOCAL_ONLY:            /* do not cross rmt mount points */
  327.                     if (ent_fsinfo->remote)
  328.                         continue;
  329.                     break;
  330.                 default:
  331.                     errmssg(ERR_ABORT, 0,
  332.                         "internal error - bad \"Handle_filesys\" value \"%d\"",
  333.                         Handle_filesys);
  334.                 }
  335.             } else {
  336.                 ent_fsinfo = dir_fsinfo;
  337.             }
  338.  
  339.             /*
  340.              * Recursively get the usage on this directory.
  341.              */
  342.             if (chdir(ent_basename) != 0) {
  343.                 errmssg(ERR_WARN, errno,
  344.                     "could not chdir to \"%s\"", ent_pathname);
  345.                 continue;
  346.             }
  347.             if (du_dir(ent_pathname, &ent_stat, ent_fsinfo, &ent_usage) == 0) {
  348.                 if (Do_accum_subdirs)
  349.                     add_usage(dir_usage, &ent_usage);
  350.             }
  351.             if (chdir("..") != 0) {
  352.                 errmssg(ERR_ABORT, errno,
  353.                     "could not chdir back to \"%s\"", dir_name);
  354.             }
  355.  
  356.         }
  357.  
  358.     }
  359.  
  360.     /*
  361.      * The current directory is complete.
  362.      */
  363.     (void) closedir(dirp);
  364.     if (Handle_output != PR_TOTALS_ONLY)
  365.         print_usage(dir_name, dir_usage);
  366.     (void) free((PTRTYPE *)ent_pathname);
  367.     return 0;
  368. }
  369.  
  370.