Added a working pdu.
[utils.git] / pdu.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <dirent.h>
7 #include <stdarg.h>
8 #include <stdint.h>
9 #include <sys/stat.h>
10 #include <dpar.h>
11 #include <stdatomic.h>
12
13 struct subdir {
14     struct subdir *parent;
15     char *path;
16     off_t sum;
17     int rv, subjobs, summary;
18 };
19
20 static int summary = 0;
21 static int retval = 0;
22
23 static char *sprintf2(char *fmt, ...)
24 {
25     char *ret;
26     size_t sz;
27     FILE *fp;
28     va_list args;
29     
30     fp = open_memstream(&ret, &sz);
31     va_start(args, fmt);
32     vfprintf(fp, fmt, args);
33     va_end(args);
34     fclose(fp);
35     return(ret);
36 }
37
38 static void printres(struct subdir *job)
39 {
40     printf("%ji\t%s\n", (intmax_t)(job->sum / 1024), job->path);
41 }
42
43 static void finish(struct subdir *job)
44 {
45     if(atomic_fetch_sub(&job->subjobs, 1) == 1) {
46         if(job->summary)
47             printres(job);
48         if(job->parent) {
49             atomic_fetch_add(&job->parent->sum, job->sum);
50             finish(job->parent);
51         }
52         free(job->path);
53         free(job);
54     }
55 }
56
57 static void dodir(void *jobp)
58 {
59     struct subdir *job = jobp;
60     DIR *dp;
61     struct dirent *dent, dbuf;
62     struct stat sb;
63     struct subdir *nj;
64     char *ep;
65     
66     if((dp = opendir(job->path)) == NULL) {
67         fprintf(stderr, "pdu: %s: %s\n", job->path, strerror(errno));
68         job->rv = 1;
69         return;
70     }
71     job->subjobs = 1;
72     while(1) {
73         if(readdir_r(dp, &dbuf, &dent)) {
74             fprintf(stderr, "pdu: reading %s: %s\n", job->path, strerror(errno));
75             job->rv = 1;
76             break;
77         }
78         if(!dent)
79             break;
80         if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
81             continue;
82         ep = sprintf2("%s/%s", job->path, dent->d_name);
83         if(stat(ep, &sb)) {
84             fprintf(stderr, "pdu: stat %s: %s\n", ep, strerror(errno));
85         } else {
86             if(S_ISDIR(sb.st_mode)) {
87                 *(nj = malloc(sizeof(*nj))) = (struct subdir) {
88                     .path = strdup(ep),
89                     .summary = !summary,
90                     .parent = job,
91                 };
92                 atomic_fetch_add(&job->subjobs, 1);
93                 atomic_fetch_add(&job->sum, (off_t)sb.st_blocks * 512);
94                 submitiowork(dodir, nj);
95             } else if(S_ISREG(sb.st_mode)) {
96                 atomic_fetch_add(&job->sum, (off_t)sb.st_blocks * 512);
97             }
98         }
99         free(ep);
100     }
101     closedir(dp);
102     finish(job);
103 }
104
105 static void dodu(char *path)
106 {
107     struct subdir *job;
108     
109     *(job = malloc(sizeof(*job))) = (struct subdir) {
110         .path = strdup(path),
111         .summary = 1,
112     };
113     submitiowork(dodir, job);
114 }
115
116 static void usage(FILE *out)
117 {
118     fprintf(out, "usage: pdu [-sh] [DIRECTORY...]\n");
119 }
120
121 int main(int argc, char **argv)
122 {
123     int i, c;
124     
125     while((c = getopt(argc, argv, "hs")) != -1) {
126         switch(c) {
127         case 's':
128             summary = 1;
129             break;
130         case 'h':
131             usage(stdout);
132             exit(0);
133         default:
134             usage(stderr);
135             exit(1);
136         }
137     }
138     if(optind < argc) {
139         for(i = optind; i < argc; i++)
140             dodu(argv[i]);
141     } else {
142         dodu(".");
143     }
144     joinwork();
145     return(retval);
146 }
147
148 /*
149  * Local Variables:
150  * compile-command: "gcc -Wall -g -o pdu pdu.c -ldpar"
151  * End:
152  */