#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/avl.h>
#include <sys/sysmacros.h>
#include <fcntl.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <locale.h>
#include <libcmdutils.h>
static int aflg = 0;
static int rflg = 0;
static int sflg = 0;
static int kflg = 0;
static int mflg = 0;
static int oflg = 0;
static int dflg = 0;
static int hflg = 0;
static int Aflg = 0;
static int Hflg = 0;
static int Lflg = 0;
static int cmdarg = 0;
static char *dot = ".";
static int level = 0;
static char *base;
static char *name;
static size_t base_len = PATH_MAX + 1;
static size_t name_len = PATH_MAX + 1;
#ifdef XPG4
#define FORMAT1 "%s %s\n"
#define FORMAT2 "%llu %s\n"
#else
#define FORMAT1 "%s\t%s\n"
#define FORMAT2 "%llu\t%s\n"
#endif
#define DEV_BSHIFT 9
#define DEV_KSHIFT 10
#define DEV_MSHIFT 20
static u_longlong_t descend(char *curname, int curfd, int *retcode,
dev_t device);
static void printsize(blkcnt_t blocks, char *path);
static void exitdu(int exitcode);
static avl_tree_t *tree = NULL;
int
main(int argc, char **argv)
{
blkcnt_t blocks = 0;
int c;
extern int optind;
char *np;
pid_t pid, wpid;
int status, retcode = 0;
setbuf(stderr, NULL);
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
#ifdef XPG4
rflg++;
#endif
while ((c = getopt(argc, argv, "aAdhHkLmorsx")) != EOF)
switch (c) {
case 'a':
aflg++;
continue;
case 'h':
hflg++;
kflg = 0;
mflg = 0;
continue;
case 'r':
rflg++;
continue;
case 's':
sflg++;
continue;
case 'k':
kflg++;
hflg = 0;
mflg = 0;
continue;
case 'm':
mflg++;
hflg = 0;
kflg = 0;
continue;
case 'o':
oflg++;
continue;
case 'd':
dflg++;
continue;
case 'x':
dflg++;
continue;
case 'A':
Aflg++;
continue;
case 'H':
Hflg++;
Lflg = 0;
cmdarg++;
continue;
case 'L':
Lflg++;
Hflg = 0;
cmdarg = 0;
continue;
case '?':
(void) fprintf(stderr, gettext(
"usage: du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] "
"[file...]\n"));
exit(2);
}
if (optind == argc) {
argv = ˙
argc = 1;
optind = 0;
}
if (oflg && sflg)
oflg = 0;
if ((base = (char *)calloc(base_len, sizeof (char))) == NULL) {
perror("du");
exit(1);
}
if ((name = (char *)calloc(name_len, sizeof (char))) == NULL) {
perror("du");
free(base);
exit(1);
}
do {
pid = (pid_t)-1;
if (optind < argc - 1) {
pid = fork();
if (pid == (pid_t)-1) {
perror(gettext("du: No more processes"));
exitdu(1);
}
if (pid != 0) {
while ((wpid = wait(&status)) != pid &&
wpid != (pid_t)-1)
;
if (pid != (pid_t)-1 && status != 0)
retcode = 1;
}
}
if (optind == argc - 1 || pid == 0) {
while (base_len < (strlen(argv[optind]) + 1)) {
base_len = base_len * 2;
if ((base = (char *)realloc(base, base_len *
sizeof (char))) == NULL) {
if (rflg) {
(void) fprintf(stderr, gettext(
"du: can't process %s"),
argv[optind]);
perror("");
}
exitdu(1);
}
}
if (base_len > name_len) {
name_len = base_len;
if ((name = (char *)realloc(name, name_len *
sizeof (char))) == NULL) {
if (rflg) {
(void) fprintf(stderr, gettext(
"du: can't process %s"),
argv[optind]);
perror("");
}
exitdu(1);
}
}
(void) strcpy(base, argv[optind]);
(void) strcpy(name, argv[optind]);
np = strrchr(name, '/');
if (np != NULL) {
*np++ = '\0';
if (chdir(*name ? name : "/") < 0) {
if (rflg) {
(void) fprintf(stderr, "du: ");
perror(*name ? name : "/");
exitdu(1);
}
exitdu(0);
}
} else
np = base;
blocks = descend(*np ? np : ".", 0, &retcode,
(dev_t)0);
if (sflg)
printsize(blocks, base);
if (optind < argc - 1)
exitdu(retcode);
}
optind++;
} while (optind < argc);
exitdu(retcode);
return (retcode);
}
static u_longlong_t
descend(char *curname, int curfd, int *retcode, dev_t device)
{
static DIR *dirp = NULL;
char *ebase0, *ebase;
struct stat stb, stb1;
int i, j, ret, fd, tmpflg;
int follow_symlinks;
blkcnt_t blocks = 0;
off_t curoff = 0;
ptrdiff_t offset;
ptrdiff_t offset0;
struct dirent *dp;
char dirbuf[PATH_MAX + 1];
u_longlong_t retval;
ebase0 = ebase = strchr(base, 0);
if (ebase > base && ebase[-1] == '/')
ebase--;
offset = ebase - base;
offset0 = ebase0 - base;
if (curname)
curfd = AT_FDCWD;
follow_symlinks = (Lflg || (Hflg && cmdarg));
if (follow_symlinks) {
i = fstatat(curfd, curname, &stb, 0);
j = fstatat(curfd, curname, &stb1, AT_SYMLINK_NOFOLLOW);
if (Hflg) {
cmdarg = 0;
}
} else {
i = fstatat(curfd, curname, &stb, AT_SYMLINK_NOFOLLOW);
j = 0;
}
if ((i < 0) || (j < 0)) {
if (rflg) {
(void) fprintf(stderr, "du: ");
perror(base);
}
*retcode = (rflg ? 1 : 0);
*ebase0 = 0;
return (0);
}
if (device) {
if (dflg && stb.st_dev != device) {
*ebase0 = 0;
return (0);
}
}
else
device = stb.st_dev;
if (Lflg || ((stb.st_mode & S_IFMT) == S_IFDIR) ||
(stb.st_nlink > 1)) {
int rc;
if ((rc = add_tnode(&tree, stb.st_dev, stb.st_ino)) != 1) {
if (rc == 0) {
return (0);
} else {
if (rflg) {
perror("du");
}
exitdu(1);
}
}
}
blocks = Aflg ? stb.st_size : stb.st_blocks;
if (curname && (follow_symlinks ||
((stb.st_mode & S_IFMT) != S_IFLNK)) &&
pathconf(curname, _PC_XATTR_EXISTS) == 1) {
if ((fd = attropen(curname, ".", O_RDONLY)) < 0) {
if (rflg)
perror(gettext(
"du: can't access extended attributes"));
}
else
{
tmpflg = sflg;
sflg = 1;
blocks += descend(NULL, fd, retcode, device);
sflg = tmpflg;
}
}
if ((stb.st_mode & S_IFMT) != S_IFDIR) {
if (sflg == 0 && (aflg || level == 0))
printsize(blocks, base);
return (blocks);
}
if (dirp != NULL)
(void) closedir(dirp);
if (curname == NULL)
dirp = fdopendir(curfd);
else
dirp = opendir(curname);
if (dirp == NULL) {
if (rflg) {
(void) fprintf(stderr, "du: ");
perror(base);
}
*retcode = 1;
*ebase0 = 0;
return (0);
}
level++;
if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode))) {
if (getcwd(dirbuf, PATH_MAX) == NULL) {
if (rflg) {
(void) fprintf(stderr, "du: ");
perror(base);
}
exitdu(1);
}
}
if ((curname ? (chdir(curname) < 0) : (fchdir(curfd) < 0))) {
if (rflg) {
(void) fprintf(stderr, "du: ");
perror(base);
}
*retcode = 1;
*ebase0 = 0;
(void) closedir(dirp);
dirp = NULL;
level--;
return (0);
}
while ((dp = readdir(dirp)) != NULL) {
if ((strcmp(dp->d_name, ".") == 0) ||
(strcmp(dp->d_name, "..") == 0))
continue;
while ((offset + strlen(dp->d_name) + 2) > base_len) {
base_len = base_len * 2;
if ((base = (char *)realloc(base,
base_len * sizeof (char))) == NULL) {
if (rflg) {
perror("du");
}
exitdu(1);
}
ebase = base + offset;
ebase0 = base + offset0;
}
(void) sprintf(ebase, "/%s", dp->d_name);
curoff = telldir(dirp);
retval = descend(ebase + 1, 0, retcode, device);
ebase = base + offset;
ebase0 = base + offset0;
*ebase = 0;
blocks += retval;
if (dirp == NULL) {
if ((dirp = opendir(".")) == NULL) {
if (rflg) {
(void) fprintf(stderr,
gettext("du: Can't reopen in "));
perror(base);
}
*retcode = 1;
level--;
return (0);
}
seekdir(dirp, curoff);
}
}
(void) closedir(dirp);
level--;
dirp = NULL;
if (sflg == 0)
printsize(blocks, base);
if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode)))
ret = chdir(dirbuf);
else
ret = chdir("..");
if (ret < 0) {
if (rflg) {
(void) sprintf(strchr(base, '\0'), "/..");
(void) fprintf(stderr,
gettext("du: Can't change dir to '..' in "));
perror(base);
}
exitdu(1);
}
*ebase0 = 0;
if (oflg)
return (0);
else
return (blocks);
}
static u_longlong_t
mkb(blkcnt_t n, size_t shift)
{
u_longlong_t v = (u_longlong_t)n;
if (!Aflg)
v <<= DEV_BSHIFT;
return (P2ROUNDUP(v, 1 << shift) >> shift);
}
static void
printsize(blkcnt_t blocks, char *path)
{
if (hflg) {
u_longlong_t bsize = Aflg ? 1 : (1 << DEV_BSHIFT);
char buf[NN_NUMBUF_SZ] = { 0 };
nicenum_scale(blocks, bsize, buf, sizeof (buf), 0);
(void) printf(FORMAT1, buf, path);
return;
}
if (kflg) {
(void) printf(FORMAT2, mkb(blocks, DEV_KSHIFT), path);
} else if (mflg) {
(void) printf(FORMAT2, mkb(blocks, DEV_MSHIFT), path);
} else {
(void) printf(FORMAT2, mkb(blocks, DEV_BSHIFT), path);
}
}
static void
exitdu(int exitcode)
{
free(base);
free(name);
exit(exitcode);
}