du.c - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       du.c (2902B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <sys/stat.h>
            3 #include <sys/types.h>
            4 
            5 #include <errno.h>
            6 #include <fcntl.h>
            7 #include <limits.h>
            8 #include <search.h>
            9 #include <stdint.h>
           10 #include <stdlib.h>
           11 #include <stdio.h>
           12 #include <unistd.h>
           13 
           14 #include "fs.h"
           15 #include "util.h"
           16 
           17 static size_t maxdepth = SIZE_MAX;
           18 static size_t blksize  = 512;
           19 
           20 static int aflag = 0;
           21 static int sflag = 0;
           22 static int hflag = 0;
           23 
           24 struct file {
           25         dev_t devno;
           26         ino_t inode;
           27 };
           28 
           29 static void
           30 printpath(off_t n, const char *path)
           31 {
           32         if (hflag)
           33                 printf("%s\t%s\n", humansize(n * blksize), path);
           34         else
           35                 printf("%jd\t%s\n", (intmax_t)n, path);
           36 }
           37 
           38 static off_t
           39 nblks(blkcnt_t blocks)
           40 {
           41         return (512 * blocks + blksize - 1) / blksize;
           42 }
           43 
           44 static int
           45 cmp(const void *p1, const void *p2)
           46 {
           47         const struct file *f1 = p1, *f2 = p2;
           48 
           49         if (f1->devno > f2->devno)
           50                 return -1;
           51         if (f1->devno < f2->devno)
           52                 return 1;
           53 
           54         /* f1->devno == f2->devno */
           55         if (f1->inode < f2->inode)
           56                 return -1;
           57         if (f1->inode > f2->inode)
           58                 return 1;
           59 
           60         return 0;
           61 }
           62 
           63 static int
           64 duplicated(dev_t dev, ino_t ino)
           65 {
           66         static void *tree;
           67         struct file **fpp, *fp, file = {dev, ino};
           68 
           69         if ((fpp = tsearch(&file, &tree, cmp)) == NULL)
           70                 eprintf("%s:", argv0);
           71 
           72         if (*fpp != &file)
           73                 return 1;
           74 
           75         /* new file added */
           76         fp = emalloc(sizeof(*fp));
           77         *fp = file;
           78         *fpp = fp;
           79 
           80         return 0;
           81 }
           82 
           83 static void
           84 du(int dirfd, const char *path, struct stat *st, void *data, struct recursor *r)
           85 {
           86         off_t *total = data, subtotal;
           87 
           88         subtotal = nblks(st->st_blocks);
           89         if (S_ISDIR(st->st_mode)) {
           90                 recurse(dirfd, path, &subtotal, r);
           91         } else if (r->follow != 'P' || st->st_nlink > 1) {
           92                 if (duplicated(st->st_dev, st->st_ino))
           93                         goto print;
           94         }
           95 
           96         *total += subtotal;
           97 
           98 print:
           99         if (!r->depth)
          100                 printpath(*total, r->path);
          101         else if (!sflag && r->depth <= maxdepth && (S_ISDIR(st->st_mode) || aflag))
          102                 printpath(subtotal, r->path);
          103 }
          104 
          105 static void
          106 usage(void)
          107 {
          108         eprintf("usage: %s [-a | -s] [-d depth] [-h] [-k] [-H | -L | -P] [-x] [file ...]\n", argv0);
          109 }
          110 
          111 int
          112 main(int argc, char *argv[])
          113 {
          114         struct recursor r = { .fn = du, .follow = 'P' };
          115         off_t n = 0;
          116         int kflag = 0, dflag = 0;
          117         char *bsize;
          118 
          119         ARGBEGIN {
          120         case 'a':
          121                 aflag = 1;
          122                 break;
          123         case 'd':
          124                 dflag = 1;
          125                 maxdepth = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
          126                 break;
          127         case 'h':
          128                 hflag = 1;
          129                 break;
          130         case 'k':
          131                 kflag = 1;
          132                 break;
          133         case 's':
          134                 sflag = 1;
          135                 break;
          136         case 'x':
          137                 r.flags |= SAMEDEV;
          138                 break;
          139         case 'H':
          140         case 'L':
          141         case 'P':
          142                 r.follow = ARGC();
          143                 break;
          144         default:
          145                 usage();
          146         } ARGEND
          147 
          148         if ((aflag && sflag) || (dflag && sflag))
          149                 usage();
          150 
          151         bsize = getenv("BLOCKSIZE");
          152         if (bsize)
          153                 blksize = estrtonum(bsize, 1, MIN(LLONG_MAX, SIZE_MAX));
          154         if (kflag)
          155                 blksize = 1024;
          156 
          157         if (!argc) {
          158                 recurse(AT_FDCWD, ".", &n, &r);
          159         } else {
          160                 for (; *argv; argc--, argv++) {
          161                         n = 0;
          162                         recurse(AT_FDCWD, *argv, &n, &r);
          163                 }
          164         }
          165 
          166         return fshut(stdout, "<stdout>") || recurse_status;
          167 }