home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char SCCSID[] = "@(#) duentry.c 1.18 93/08/18 00:09:03";
- #endif
-
- /*
- * "du" enhanced disk usage summary - version 2.
- *
- * Copyright 1990-1993, Unicom Systems Development. All rights reserved.
- * See accompanying README file for terms of distribution and use.
- *
- * Edit at tabstops=4.
- */
-
- #include "config.h"
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #ifdef USE_UNISTD
- # include <unistd.h>
- #endif
- #include <sys/types.h>
- #include <sys/stat.h>
- #include "du.h"
-
- #ifdef USE_DIR_DIRENT
- # include <dirent.h>
- #endif
- #ifdef USE_DIR_SYSNDIR
- # include <sys/ndir.h>
- # define dirent direct
- #endif
- #ifdef USE_DIR_SYSDIR
- # include <sys/dir.h>
- # define dirent direct
- #endif
-
- #ifndef USE_SYMLINKS
- # define lstat stat
- #endif
-
-
- /*
- * Determine maximum pathlength of an entry on the filesystem containing "file".
- */
- int max_path_len(fname)
- register char *fname;
- {
- #ifdef USE_PATHCONF
- register int len;
- if ((len = pathconf(fname, _PC_PATH_MAX)) > 0)
- return len;
- #ifdef notdef
- errmssg(ERR_WARN, errno, "could not get max pathname for \"%s\"", fname);
- #endif
- #endif
-
- /*
- * If pathconf() is not available or fails, we need to take a stab. If
- * the POSIX PATH_MAX parameter is available we will use that. If not,
- * we will try to use the MAXNAMLEN that should be in whatever header
- * file USE_DIR_XXXX pulled in. If all that fails, we'll just make a
- * guess of 1024. Note that we do NOT want to use _POSIX_PATH_MAX. That
- * specifies the minimum value that a conformant system must support.
- */
- #ifndef PATH_MAX
- # ifdef MAXNAMLEN
- # define PATH_MAX MAXNAMLEN
- # else
- # define PATH_MAX 1024
- # endif
- #endif
- return PATH_MAX;
- }
-
-
- /*
- * du_entry - Initiate a disk usage report for a specified filesystem entry.
- *
- * This routine is called once per command line argument to setup a disk
- * usage scan. This routine prepares to call "du_dir()" recursively to
- * scan through a directory to accumulate its usage.
- */
- void du_entry(entry, ent_usage)
- char *entry; /* pathname of the entry to check */
- struct dusage *ent_usage; /* space in which to return disk usage */
- {
- struct stat sbuf;
- struct fsinfo *fsinfo;
- static char *curr_dir = NULL;
-
- #ifdef TIMEOUT
- alarm(TIMEOUT);
- #endif
-
- /*
- * Get the information on this entry.
- */
- if (lstat(entry, &sbuf) != 0) {
- errmssg(ERR_WARN, errno, "could not stat \"%s\"", entry);
- return;
- }
- if ((fsinfo = fs_getinfo(&sbuf)) == NULL) {
- errmssg(ERR_WARN, errno,
- "could not get filesystem info for \"%s\"", entry);
- return;
- }
-
- /*
- * If this isn't a directory then simply get its size. Note that we
- * won't say what size is unless "-s" or "-a" was specified. Kind of
- * silly, but that's documented behaviour.
- */
- if (!S_ISDIR(sbuf.st_mode)) {
- set_usage(ent_usage, sbuf.st_mtime, fs_numblocks(fsinfo, &sbuf));
- if (Handle_output != PR_DIRS_ONLY)
- print_usage(entry, ent_usage);
- return;
- }
-
- #ifdef OBSOLETEOPT
- /*
- * If we aren't supposed to descend into directories then just get
- * it's size.
- */
- if (!Do_descend_dirs) {
- set_usage(ent_usage, sbuf.st_mtime, fs_numblocks(fsinfo, &sbuf));
- print_usage(entry, ent_usage);
- return;
- }
- #endif
-
- /*
- * We are about to descend down this directory entry. We need to
- * know where we started out so that we can return when done.
- */
- if (curr_dir == NULL) {
- int len;
- extern char *getcwd();
- len = max_path_len(".") + 2; /* getcwd() wants two extra bytes */
- curr_dir = (char *) xmalloc((unsigned)len);
- if (errno = 0, getcwd(curr_dir, len) == NULL)
- errmssg(ERR_ABORT, errno,
- "could not get current working directory");
- }
-
- /*
- * We need to initiate a recursive search through "du_dir()". Normally
- * "du_dir()" displays the disk usage of the directory scanned, except
- * when "PR_TOTALS_ONLY" (-s) mode is set. In that case it won't
- * print anything at all, so we need to do it here.
- */
- zero_usage(ent_usage);
- if (chdir(entry) != 0) {
- errmssg(ERR_WARN, errno, "could not chdir to \"%s\"", entry);
- return;
- }
- if (du_dir(entry, &sbuf, fsinfo, ent_usage) == 0) {
- if (Handle_output == PR_TOTALS_ONLY)
- print_usage(entry, ent_usage);
- }
- if (chdir(curr_dir) != 0)
- errmssg(ERR_ABORT, errno, "could not chdir back to \"%s\"", curr_dir);
-
- }
-
-
- /*
- * du_dir() - Scan a specified directory and accumulate its disk usage.
- *
- * This routine recursively descends a directory tree accumulating and
- * printing the disk usage. The current working directory must be set
- * to the directory to be scanned before this routine is called. If the
- * scan is successful, 0 will be returned and the accumlated usage
- * will be stored in "dir_usage". If an error occurs which prevents
- * further processing, a -1 result will be returned and the usage
- * results should be ignored as bogus.
- */
- int du_dir(dir_name, dir_statp, dir_fsinfo, dir_usage)
- char *dir_name; /* pathname to the directory to scan */
- struct stat *dir_statp; /* inode info for this directory */
- struct fsinfo *dir_fsinfo; /* filesys info for this directory */
- register struct dusage *dir_usage; /* space in which to accumulate usage*/
- {
- register struct dirent *dp; /* current dir entry being checked */
- struct stat ent_stat; /* inode info for this entry */
- struct fsinfo *ent_fsinfo; /* filesys info for this entry */
- long numblocks; /* num disk blocks used by this entry */
- struct dusage ent_usage; /* disk usage by this entry */
- char *ent_pathname; /* full pathname of this entry */
- register char *ent_basename;/* pointer into "ent_pathname[]" */
- DIR *dirp; /* stream for directory being scanned */
-
- /*
- * Allocate space to hold the pathname. If you've got a good malloc
- * this isn't too painful. I profiled malloc() at 0.4% overhead.
- */
- ent_pathname = (char *) xmalloc((unsigned)dir_fsinfo->path_max+1);
-
- /*
- * Initialize the block count with the usage of the directory itself.
- */
- numblocks = fs_numblocks(dir_fsinfo, dir_statp);
- set_usage(dir_usage, dir_statp->st_mtime, numblocks);
-
- /*
- * Setup a buffer to hold the full pathname of the entry being examined.
- * Unless this directory is the root directory "/", append a slash
- * to the name. When we need the full pathname, we just need to place
- * the filename at the location pointed to by "ent_basename".
- */
- ent_basename = strcpy(ent_pathname, dir_name) + strlen(dir_name);
- if (ent_pathname[0] != '/' || ent_pathname[1] != '\0')
- *ent_basename++ = '/';
-
- /*
- * Open up the directory so we can scan it.
- */
- if ((dirp = opendir(".")) == NULL) {
- errmssg(ERR_WARN, errno, "could not open dir \"%s\"", dir_name);
- (void) free((PTRTYPE *)ent_pathname);
- return -1;
- }
-
- /*
- * Go through each entry in the directory.
- */
- while ((dp = readdir(dirp)) != NULL) {
-
- #ifdef TIMEOUT
- alarm(TIMEOUT);
- #endif
-
- /*
- * Skip the "." and ".." entries.
- */
- if (
- dp->d_name[0] == '.' && (
- dp->d_name[1] == '\0' ||
- (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
- )
- ) {
- continue;
- }
-
- /*
- * Get the information on this entry.
- */
- if (lstat(dp->d_name, &ent_stat) != 0) {
- errmssg(ERR_WARN, errno,
- "could not stat \"%s/%s\"", dir_name, dp->d_name);
- continue;
- }
-
- /*
- * If it's not a directory then just accumulate its disk usage.
- */
- if (!S_ISDIR(ent_stat.st_mode)) {
-
- /*
- * See what to do with entries with multiple links.
- */
- if (ent_stat.st_nlink > 1) {
- switch (Handle_links) {
- case LK_COUNT_FIRST: /* count only first link */
- if (fs_linkdone(dir_fsinfo, &ent_stat))
- continue;
- break;
- case LK_COUNT_ALL: /* count all hard links */
- break;
- case LK_COUNT_NONE: /* skip all hard links */
- continue;
- case LK_COUNT_AVERAGE: /* average usage over links */
- if (S_ISREG(ent_stat.st_mode)) {
- long nb = fs_numblocks(dir_fsinfo, &ent_stat);
- numblocks = nb / ent_stat.st_nlink;
- if (!fs_linkdone(dir_fsinfo, &ent_stat))
- numblocks += (nb % ent_stat.st_nlink);
- goto got_numblocks;
- }
- break;
- default:
- errmssg(ERR_ABORT, 0,
- "internal error - bad \"Handle_links\" value \"%d\"",
- Handle_links);
- }
- }
-
- /*
- * Get the usage of this entry and accumulate into the dir usage.
- */
- numblocks = fs_numblocks(dir_fsinfo, &ent_stat);
- got_numblocks:
- set_usage(&ent_usage, ent_stat.st_mtime, numblocks);
- add_usage(dir_usage, &ent_usage);
- if (Handle_output == PR_EVERYTHING) {
- (void) strcpy(ent_basename, dp->d_name);
- print_usage(ent_pathname, &ent_usage);
- }
-
- } else {
-
- /*
- * We are probably going to need the pathname, so fill it out now.
- */
- (void) strcpy(ent_basename, dp->d_name);
-
- /*
- * See if we are crossing a mount point -- and if so check if we
- * want to process it. Furthermore, if we cross a mount point then
- * we need to get the filesystem information on the subdirectory.
- * If we aren't crossing a mount point then the current filesystem
- * info applies.
- */
- if (dir_statp->st_dev != ent_stat.st_dev) {
- if ((ent_fsinfo = fs_getinfo(&ent_stat)) == NULL) {
- errmssg(ERR_WARN, errno,
- "could not get filesystem info for \"%s\"",
- ent_pathname);
- continue;
- }
- switch (Handle_filesys) {
- case FS_ALWAYS_CROSS: /* cross mount points */
- break;
- case FS_NEVER_CROSS: /* do not cross mount points */
- continue;
- case FS_LOCAL_ONLY: /* do not cross rmt mount points */
- if (ent_fsinfo->remote)
- continue;
- break;
- default:
- errmssg(ERR_ABORT, 0,
- "internal error - bad \"Handle_filesys\" value \"%d\"",
- Handle_filesys);
- }
- } else {
- ent_fsinfo = dir_fsinfo;
- }
-
- /*
- * Recursively get the usage on this directory.
- */
- if (chdir(ent_basename) != 0) {
- errmssg(ERR_WARN, errno,
- "could not chdir to \"%s\"", ent_pathname);
- continue;
- }
- if (du_dir(ent_pathname, &ent_stat, ent_fsinfo, &ent_usage) == 0) {
- if (Do_accum_subdirs)
- add_usage(dir_usage, &ent_usage);
- }
- if (chdir("..") != 0) {
- errmssg(ERR_ABORT, errno,
- "could not chdir back to \"%s\"", dir_name);
- }
-
- }
-
- }
-
- /*
- * The current directory is complete.
- */
- (void) closedir(dirp);
- if (Handle_output != PR_TOTALS_ONLY)
- print_usage(dir_name, dir_usage);
- (void) free((PTRTYPE *)ent_pathname);
- return 0;
- }
-
-