anndl: Fixed a couple of bugs.
[utils.git] / pdu.c
... / ...
CommitLineData
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
13struct subdir {
14 struct subdir *parent;
15 char *path;
16 off_t sum;
17 int rv, subjobs, summary;
18};
19
20static int summary = 0;
21static int retval = 0;
22
23static 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
38static void printres(struct subdir *job)
39{
40 printf("%ji\t%s\n", (intmax_t)(job->sum / 1024), job->path);
41}
42
43static 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
57static 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
105static 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
116static void usage(FILE *out)
117{
118 fprintf(out, "usage: pdu [-sh] [DIRECTORY...]\n");
119}
120
121int 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 */