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