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