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