use library versions of err/warn
[unix-history] / usr / src / usr.bin / du / du.c
index df6cbbb..2cece57 100644 (file)
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Newcomb.
+ *
+ * %sccs.include.redist.c%
+ */
+
 #ifndef lint
 #ifndef lint
-static char *sccsid = "@(#)du.c        4.7 (Berkeley) %G%";
-#endif
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)du.c       5.20 (Berkeley) %G%";
+#endif /* not lint */
 
 
-#include <stdio.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/param.h>
 #include <sys/stat.h>
-#include <dir.h>
 
 
-char   path[BUFSIZ], name[BUFSIZ];
-int    aflg;
-int    sflg;
-char   *dot = ".";
-
-#define ML     1000
-struct {
-       int     dev;
-       ino_t   ino;
-} ml[ML];
-int    mlx;
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 
-long   descend();
-char   *index(), *rindex(), *strcpy(), *sprintf();
+char   *getbsize __P((char *, int *, long *));
+int     linkchk __P((FTSENT *));
+void    usage __P((void));
 
 
+int
 main(argc, argv)
        int argc;
 main(argc, argv)
        int argc;
-       char **argv;
+       char *argv[];
 {
 {
-       long kbytes = 0;
-       register char *np;
-       int pid;
-
-       argc--, argv++;
-again:
-       if (argc && !strcmp(*argv, "-s")) {
-               sflg++;
-               argc--, argv++;
-               goto again;
-       }
-       if (argc && !strcmp(*argv, "-a")) {
-               aflg++;
-               argc--, argv++;
-               goto again;
+       register FTS *fts;
+       register FTSENT *p;
+       register int listdirs, listfiles;
+       long blocksize;
+       int aflag, ch, ftsoptions, notused, sflag;
+       char **save;
+
+       ftsoptions = FTS_PHYSICAL;
+       save = argv;
+       aflag = sflag = 0;
+       while ((ch = getopt(argc, argv, "Hahsx")) != EOF)
+               switch(ch) {
+               case 'H':
+                       ftsoptions |= FTS_COMFOLLOW;
+                       break;
+               case 'a':
+                       aflag = 1;
+                       break;
+               case 'h':
+                       ftsoptions &= ~FTS_PHYSICAL;
+                       ftsoptions |= FTS_LOGICAL;
+                       break;
+               case 's':
+                       sflag = 1;
+                       break;
+               case 'x':
+                       ftsoptions |= FTS_XDEV;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argv += optind;
+
+       if (aflag) {
+               if (sflag)
+                       usage();
+               listdirs = listfiles = 1;
+       } else if (sflag)
+               listdirs = listfiles = 0;
+       else {
+               listfiles = 0;
+               listdirs = 1;
        }
        }
-       if (argc == 0) {
-               argv = &dot;
-               argc = 1;
+
+       if (!*argv) {
+               argv = save;
+               argv[0] = ".";
+               argv[1] = NULL;
        }
        }
-       do {
-               if (argc > 1) {
-                       pid = fork();
-                       if (pid == -1) {
-                               fprintf(stderr, "No more processes.\n");
-                               exit(1);
-                       }
-                       if (pid != 0)
-                               wait((int *)0);
-               }
-               if (argc == 1 || pid == 0) {
-                       (void) strcpy(path, *argv);
-                       (void) strcpy(name, *argv);
-                       if (np = rindex(name, '/')) {
-                               *np++ = '\0';
-                               if (chdir(*name ? name : "/") < 0) {
-                                       perror(*name ? name : "/");
-                                       exit(1);
-                               }
-                       } else
-                               np = path;
-                       kbytes = descend(path, *np ? np : ".");
-                       if (sflg)
-                               printf("%ld\t%s\n", kbytes, path);
-                       if (argc > 1)
-                               exit(1);
+
+       (void)getbsize("du", &notused, &blocksize);
+       blocksize /= 512;
+
+       if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
+               err(1, "");
+
+       while (p = fts_read(fts))
+               switch(p->fts_info) {
+               case FTS_D:
+                       break;
+               case FTS_DP:
+                       p->fts_parent->fts_number += 
+                           p->fts_number += p->fts_statp->st_blocks;
+                       /*
+                        * If listing each directory, or not listing files
+                        * or directories and this is post-order of the
+                        * root of a traversal, display the total.
+                        */
+                       if (listdirs || !listfiles && !p->fts_level)
+                               (void)printf("%ld\t%s\n",
+                                   howmany(p->fts_number, blocksize),
+                                   p->fts_path);
+                       break;
+               case FTS_DNR:
+               case FTS_ERR:
+               case FTS_NS:
+                       warn("%s", p->fts_path);
+                       break;
+               default:
+                       if (p->fts_statp->st_nlink > 1 && linkchk(p))
+                               break;
+                       /*
+                        * If listing each file, or a non-directory file was
+                        * the root of a traversal, display the total.
+                        */
+                       if (listfiles || !p->fts_level)
+                               (void)printf("%qd\t%s\n",
+                                   howmany(p->fts_statp->st_blocks, blocksize),
+                                   p->fts_path);
+                       p->fts_parent->fts_number += p->fts_statp->st_blocks;
                }
                }
-               argc--, argv++;
-       } while (argc > 0);
+       if (errno)
+               err(1, "");
        exit(0);
 }
 
        exit(0);
 }
 
-DIR    *dirp = NULL;
+typedef struct _ID {
+       dev_t   dev;
+       ino_t   inode;
+} ID;
 
 
-long
-descend(base, name)
-       char *base, *name;
+int
+linkchk(p)
+       register FTSENT *p;
 {
 {
-       char *ebase0, *ebase;
-       struct stat stb;
-       int i;
-       long kbytes = 0;
-       long curoff = NULL;
-       register struct direct *dp;
-
-       ebase0 = ebase = index(base, 0);
-       if (ebase > base && ebase[-1] == '/')
-               ebase--;
-       if (lstat(name, &stb) < 0) {
-               perror(base);
-               *ebase0 = 0;
-               return (0);
-       }
-       if (stb.st_nlink > 1 && (stb.st_mode&S_IFMT) != S_IFDIR) {
-               for (i = 0; i <= mlx; i++)
-                       if (ml[i].ino == stb.st_ino && ml[i].dev == stb.st_dev)
-                               return (0);
-               if (mlx < ML) {
-                       ml[mlx].dev = stb.st_dev;
-                       ml[mlx].ino = stb.st_ino;
-                       mlx++;
-               }
-       }
-       kbytes = howmany(stb.st_size, 1024);
-       if ((stb.st_mode&S_IFMT) != S_IFDIR) {
-               if (aflg)
-                       printf("%ld\t%s\n", kbytes, base);
-               return (kbytes);
-       }
-       if (dirp != NULL)
-               closedir(dirp);
-       dirp = opendir(name);
-       if (dirp == NULL) {
-               perror(base);
-               *ebase0 = 0;
-               return (0);
-       }
-       if (chdir(name) < 0) {
-               perror(base);
-               *ebase0 = 0;
-               return (0);
-       }
-       while (dp = readdir(dirp)) {
-               if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
-                       continue;
-               (void) sprintf(ebase, "/%s", dp->d_name);
-               curoff = telldir(dirp);
-               kbytes += descend(base, ebase+1);
-               *ebase = 0;
-               if (dirp == NULL) {
-                       dirp = opendir(".");
-                       if (dirp == NULL) {
-                               perror(".");
-                               return (0);
-                       }
-                       seekdir(dirp, curoff);
-               }
-       }
-       closedir(dirp);
-       dirp = NULL;
-       if (sflg == 0)
-               printf("%ld\t%s\n", kbytes, base);
-       if (chdir("..") < 0) {
-               (void) sprintf(index(base, 0), "/..");
-               perror(base);
-               exit(1);
-       }
-       *ebase0 = 0;
-       return (kbytes);
+       static ID *files;
+       static int maxfiles, nfiles;
+       register ID *fp, *start;
+       register ino_t ino;
+       register dev_t dev;
+
+       ino = p->fts_statp->st_ino;
+       dev = p->fts_statp->st_dev;
+       if (start = files)
+               for (fp = start + nfiles - 1; fp >= start; --fp)
+                       if (ino == fp->inode && dev == fp->dev)
+                               return(1);
+
+       if (nfiles == maxfiles && (files = realloc((char *)files,
+           (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
+               err(1, "");
+       files[nfiles].inode = ino;
+       files[nfiles].dev = dev;
+       ++nfiles;
+       return(0);
+}
+
+void
+usage()
+{
+       (void)fprintf(stderr, "usage: du [-a | -s] [-Hhx] [file ...]\n");
+       exit(1);
 }
 }