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