chiark / gitweb /
do not die straight away on errors
[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 void fn_escaped(FILE *f, const char *fn) {
28   int c;
29   while ((c= *fn++)) {
30     if (c>=33 && c<=126 && c!='\\') putc(c,f);
31     else fprintf(f,"\\x%02x",(int)(unsigned char)c);
32   }
33 }
34
35 static void add_pr(int *pr, int printf_ret) {
36   if (printf_ret == EOF) return;
37   *pr += printf_ret;
38 }
39
40 static void vproblemx(int padto, int per, const char *fmt, va_list al) {
41   int e=errno, pr=0;
42   add_pr(&pr, printf("\\["));
43   add_pr(&pr, vprintf(fmt,al));
44   if (per) add_pr(&pr, printf(": %s",strerror(e)));
45   add_pr(&pr, printf("]"));
46   while (pr++ < padto)
47     putchar(' ');
48 }  
49
50 static void problem_e(int padto, const char *fmt, ...) {
51   va_list(al);
52   va_start(al,fmt);
53   vproblemx(padto,1,fmt,al);
54   va_end(al);
55 }
56
57 static void problem(int padto, const char *fmt, ...) {
58   va_list(al);
59   va_start(al,fmt);
60   vproblemx(padto,0,fmt,al);
61   va_end(al);
62 }
63
64 static void csum_file(const char *path) {
65   FILE *f;
66   struct MD5Context mc;
67   char db[65536];
68   unsigned char digest[16];
69   size_t r;
70   int i;
71
72   f= fopen(path,"rb");
73   if (!f) { problem_e(sizeof(digest)*2,"open"); return; }
74   
75   MD5Init(&mc);
76   for (;;) {
77     r= fread(db,1,sizeof(db),f);
78     if (ferror(f)) { problem_e(sizeof(digest)*2,"read"); fclose(f); return; }
79     if (!r) { assert(feof(f)); break; }
80     MD5Update(&mc,db,r);
81   }
82   MD5Final(digest,&mc);
83   if (fclose(f)) { problem_e(sizeof(digest)*2,"close"); return; }
84
85   for (i=0; i<sizeof(digest); i++)
86     printf("%02x", digest[i]);
87 }
88
89 static void csum_dev(int cb, const struct stat *stab) {
90   printf("%c 0x%08lx %3lu %3lu %3lu %3lu    ", cb,
91          (unsigned long)stab->st_rdev,
92          ((unsigned long)stab->st_rdev & 0x0ff000000U) >> 24,
93          ((unsigned long)stab->st_rdev & 0x000ff0000U) >> 16,
94          ((unsigned long)stab->st_rdev & 0x00000ff00U) >> 8,
95          ((unsigned long)stab->st_rdev & 0x0000000ffU) >> 0);
96 }
97
98 static void csum_str(const char *s) {
99   printf("%-*s", CSUMXL, s);
100 }
101
102 struct FTW;
103
104 static int item(const char *path, const struct stat *stab,
105                 int flag, struct FTW *ftws) {
106   char linktarg[MAXFN+1];
107
108   switch (flag) {
109   case FTW_D:
110   case FTW_F:
111   case FTW_SL:
112     if (S_ISREG(stab->st_mode)) csum_file(path);
113     else if (S_ISDIR(stab->st_mode)) csum_str("dir");
114     else if (S_ISCHR(stab->st_mode)) csum_dev('c',stab);
115     else if (S_ISBLK(stab->st_mode)) csum_dev('b',stab);
116     else if (S_ISFIFO(stab->st_mode)) csum_str("pipe");
117     else if (S_ISLNK(stab->st_mode)) csum_str("link");
118     else if (S_ISSOCK(stab->st_mode)) csum_str("sock");
119     else problem(CSUMXL,"badobj: 0x%lx", (unsigned long)stab->st_mode);
120     break;
121
122   case FTW_NS:
123   case FTW_DNR:
124     problem_e(CSUMXL,"inaccessible");
125     break;
126
127   default:
128     problem(CSUMXL,"ftw flag 0x%x: %s",flag);
129   }
130
131   if (S_ISLNK(stab->st_mode)) {
132     int r;
133
134     r= readlink(path, linktarg, sizeof(linktarg)-1);
135     if (r==sizeof(linktarg)) { problem(-1,"readlink too big"); r=-1; }
136     else if (r<0) { problem_e(-1,"readlink"); }
137     else assert(r<sizeof(linktarg));
138
139     if (r<0) strcpy(linktarg,"\\?");
140     else linktarg[r]= 0;
141   }
142
143   printf(" %10lu %4d %4o %10ld %10ld %10lu %10lu %10lu ",
144          (unsigned long)stab->st_size,
145          (int)stab->st_nlink,
146          (unsigned)stab->st_mode & 07777U,
147          (unsigned long)stab->st_uid,
148          (unsigned long)stab->st_gid,
149          (unsigned long)stab->st_atime,
150          (unsigned long)stab->st_mtime,
151          (unsigned long)stab->st_ctime);
152   fn_escaped(stdout, path);
153
154   if (S_ISLNK(stab->st_mode)) {
155     printf(" -> ");
156     fn_escaped(stdout, linktarg);
157   }
158   putchar('\n');
159
160   if (ferror(stdout)) { perror("summer: stdout"); exit(12); }
161   return 0;
162 }
163
164 static void process(const char *startpoint) {
165   int r;
166   fprintf(stderr,"summer: processing: %s\n",startpoint);
167   r= nftw(startpoint, item, MAXDEPTH, FTW_MOUNT|FTW_PHYS);
168   if (r) { fprintf(stderr, "summer: nftw failed: %s: %s\n",
169                    strerror(errno), startpoint); exit(4); }
170 }
171
172 static void from_stdin(void) {
173   char buf[MAXFN+2];
174   char *s;
175   int l;
176   
177   fprintf(stderr, "summer: processing stdin lines as startpoints\n");
178   for (;;) {
179     s= fgets(buf,sizeof(buf),stdin);
180     if (ferror(stdin)) { perror("summer: stdin"); exit(12); }
181     if (!s) { if (feof(stdin)) return; else abort(); }
182     l= strlen(buf);
183     assert(l>0);
184     if (buf[l-1]!='\n') { fprintf(stderr,"summer: line too long\n"); exit(8); }
185     buf[l-1]= 0;
186     process(buf);
187   }
188 }
189
190 int main(int argc, const char *const *argv) {
191   const char *arg;
192   
193   if (!argv[1]) {
194     from_stdin();
195   } else if (argv[1][0]=='h') {
196     fprintf(stderr,
197             "summer: usage: summer startpoint... >data.list\n"
198             "               cat startpoints.list | summer >data.list\n");
199     return 8;
200   } else {
201     fprintf(stderr, "summer: processing command line args as startpoints\n");
202     while ((arg=*++argv)) {
203       process(arg);
204     }
205   }
206   if (ferror(stdout) || fclose(stdout)) {
207     perror("summer: stdout (at end)"); exit(12);
208   }
209   fputs("summer: done.\n", stderr);
210   return 0;
211 }