chiark / gitweb /
actually reads /proc - compiles but untested
[chiark-utils.git] / cprogs / summer.c
1 /*
2  * usage:
3  *    cat startpoints.list | summer >data.list
4  *    summer startpoints... >data.list
5  *  prints md5sum of data-list to stderr
6  */
7
8 #define _GNU_SOURCE
9
10 #include <ftw.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <stdarg.h>
18 #include <limits.h>
19 #include <assert.h>
20
21 #include "md5.h"
22
23 #define MAXFN 2048
24 #define MAXDEPTH 1024
25 #define CSUMXL 32
26
27 static int quiet=0;
28 static FILE *errfile;
29
30 static void fn_escaped(FILE *f, const char *fn) {
31   int c;
32   while ((c= *fn++)) {
33     if (c>=33 && c<=126 && c!='\\') putc(c,f);
34     else fprintf(f,"\\x%02x",(int)(unsigned char)c);
35   }
36 }
37
38 static void add_pr(int *pr, int printf_ret) {
39   if (printf_ret == EOF) return;
40   *pr += printf_ret;
41 }
42
43 static void vproblemx(const char *path, int padto, int per,
44                       const char *fmt, va_list al) {
45   int e=errno, pr=0;
46   
47   if (errfile==stderr) fputs("summer: error: ",stderr);
48   else add_pr(&pr, fprintf(errfile,"\\["));
49   
50   add_pr(&pr, vfprintf(errfile,fmt,al));
51   if (per) add_pr(&pr, fprintf(errfile,": %s",strerror(e)));
52
53   if (errfile==stderr) {
54     fputs(": ",stderr);
55     fn_escaped(stderr,path);
56     fputc('\n',stderr);
57     exit(2);
58   }
59
60   add_pr(&pr, printf("]"));
61
62   while (pr++ < padto)
63     putchar(' ');
64 }  
65
66 static void problem_e(const char *path, int padto, const char *fmt, ...) {
67   va_list(al);
68   va_start(al,fmt);
69   vproblemx(path,padto,1,fmt,al);
70   va_end(al);
71 }
72
73 static void problem(const char *path, int padto, const char *fmt, ...) {
74   va_list(al);
75   va_start(al,fmt);
76   vproblemx(path,padto,0,fmt,al);
77   va_end(al);
78 }
79
80 static void csum_file(const char *path) {
81   FILE *f;
82   struct MD5Context mc;
83   char db[65536];
84   unsigned char digest[16];
85   size_t r;
86   int i;
87
88   f= fopen(path,"rb");
89   if (!f) { problem_e(path,sizeof(digest)*2,"open"); return; }
90   
91   MD5Init(&mc);
92   for (;;) {
93     r= fread(db,1,sizeof(db),f);
94     if (ferror(f)) {
95       problem_e(path,sizeof(digest)*2,"read");
96       fclose(f); return;
97     }
98     if (!r) { assert(feof(f)); break; }
99     MD5Update(&mc,db,r);
100   }
101   MD5Final(digest,&mc);
102   if (fclose(f)) { problem_e(path,sizeof(digest)*2,"close"); return; }
103
104   for (i=0; i<sizeof(digest); i++)
105     printf("%02x", digest[i]);
106 }
107
108 static void csum_dev(int cb, const struct stat *stab) {
109   printf("%c 0x%08lx %3lu %3lu %3lu %3lu    ", cb,
110          (unsigned long)stab->st_rdev,
111          ((unsigned long)stab->st_rdev & 0x0ff000000U) >> 24,
112          ((unsigned long)stab->st_rdev & 0x000ff0000U) >> 16,
113          ((unsigned long)stab->st_rdev & 0x00000ff00U) >> 8,
114          ((unsigned long)stab->st_rdev & 0x0000000ffU) >> 0);
115 }
116
117 static void csum_str(const char *s) {
118   printf("%-*s", CSUMXL, s);
119 }
120
121 struct FTW;
122
123 static int item(const char *path, const struct stat *stab,
124                 int flag, struct FTW *ftws) {
125   char linktarg[MAXFN+1];
126
127   switch (flag) {
128   case FTW_D:
129   case FTW_F:
130   case FTW_SL:
131     if (S_ISREG(stab->st_mode)) csum_file(path);
132     else if (S_ISDIR(stab->st_mode)) csum_str("dir");
133     else if (S_ISCHR(stab->st_mode)) csum_dev('c',stab);
134     else if (S_ISBLK(stab->st_mode)) csum_dev('b',stab);
135     else if (S_ISFIFO(stab->st_mode)) csum_str("pipe");
136     else if (S_ISLNK(stab->st_mode)) csum_str("link");
137     else if (S_ISSOCK(stab->st_mode)) csum_str("sock");
138     else problem(path,CSUMXL,"badobj: 0x%lx", (unsigned long)stab->st_mode);
139     break;
140
141   case FTW_NS:
142   case FTW_DNR:
143     problem_e(path,CSUMXL,"inaccessible");
144     break;
145
146   default:
147     problem(path,CSUMXL,"ftw flag 0x%x: %s",flag);
148   }
149
150   if (S_ISLNK(stab->st_mode)) {
151     int r;
152
153     r= readlink(path, linktarg, sizeof(linktarg)-1);
154     if (r==sizeof(linktarg)) { problem(path,-1,"readlink too big"); r=-1; }
155     else if (r<0) { problem_e(path,-1,"readlink"); }
156     else assert(r<sizeof(linktarg));
157
158     if (r<0) strcpy(linktarg,"\\?");
159     else linktarg[r]= 0;
160   }
161
162   printf(" %10lu %4d %4o %10ld %10ld %10lu %10lu %10lu ",
163          (unsigned long)stab->st_size,
164          (int)stab->st_nlink,
165          (unsigned)stab->st_mode & 07777U,
166          (unsigned long)stab->st_uid,
167          (unsigned long)stab->st_gid,
168          (unsigned long)stab->st_atime,
169          (unsigned long)stab->st_mtime,
170          (unsigned long)stab->st_ctime);
171   fn_escaped(stdout, path);
172
173   if (S_ISLNK(stab->st_mode)) {
174     printf(" -> ");
175     fn_escaped(stdout, linktarg);
176   }
177   putchar('\n');
178
179   if (ferror(stdout)) { perror("summer: stdout"); exit(12); }
180   return 0;
181 }
182
183 static void process(const char *startpoint) {
184   int r;
185   if (!quiet)
186     fprintf(stderr,"summer: processing: %s\n",startpoint);
187   r= nftw(startpoint, item, MAXDEPTH, FTW_MOUNT|FTW_PHYS);
188   if (r) { fprintf(stderr, "summer: nftw failed: %s: %s\n",
189                    strerror(errno), startpoint); exit(4); }
190 }
191
192 static void from_stdin(void) {
193   char buf[MAXFN+2];
194   char *s;
195   int l;
196
197   if (!quiet)
198     fprintf(stderr, "summer: processing stdin lines as startpoints\n");
199   for (;;) {
200     s= fgets(buf,sizeof(buf),stdin);
201     if (ferror(stdin)) { perror("summer: stdin"); exit(12); }
202     if (!s) { if (feof(stdin)) return; else abort(); }
203     l= strlen(buf);
204     assert(l>0);
205     if (buf[l-1]!='\n') { fprintf(stderr,"summer: line too long\n"); exit(8); }
206     buf[l-1]= 0;
207     process(buf);
208   }
209 }
210
211 int main(int argc, const char *const *argv) {
212   const char *arg;
213   int c;
214
215   errfile= stderr;
216   
217   if ((arg=argv[1]) && *arg++=='-') {
218     while ((c=*arg++)) {
219       switch (c) {
220       case 'h':
221         fprintf(stderr,
222                 "summer: usage: summer startpoint... >data.list\n"
223                 "               cat startpoints.list | summer >data.list\n");
224         exit(8);
225       case 'q':
226         quiet= 1;
227         break;
228       case 'f':
229         errfile= stdout;
230         break;
231       default:
232         fprintf(stderr,"summer: bad usage, try -h\n");
233         exit(8);
234       }
235     }
236     argv++;
237   }
238
239   if (!argv[1]) {
240     from_stdin();
241   } else {
242     if (!quiet)
243       fprintf(stderr, "summer: processing command line args as startpoints\n");
244     while ((arg=*++argv)) {
245       process(arg);
246     }
247   }
248   if (ferror(stdout) || fclose(stdout)) {
249     perror("summer: stdout (at end)"); exit(12);
250   }
251   if (!quiet)
252     fputs("summer: done.\n", stderr);
253   return 0;
254 }