mergeperms.c (3164B)
1 #define _POSIX_C_SOURCE 200809L 2 #include <errno.h> 3 #include <stdarg.h> 4 #include <stdint.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <stdnoreturn.h> 8 #include <string.h> 9 #include <sys/stat.h> 10 11 struct perm { 12 char *name; 13 mode_t mode; 14 }; 15 16 static struct perm *perms; 17 static size_t permslen, permscap; 18 19 static noreturn void 20 fatal(const char *fmt, ...) 21 { 22 va_list ap; 23 24 va_start(ap, fmt); 25 vfprintf(stderr, fmt, ap); 26 va_end(ap); 27 if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { 28 fputc(' ', stderr); 29 perror(NULL); 30 } else { 31 fputc('\n', stderr); 32 } 33 exit(1); 34 } 35 36 static void 37 addperm(struct perm *p) 38 { 39 if (permslen == permscap) { 40 permscap = permscap ? permscap * 2 : 64; 41 if (permscap > SIZE_MAX / sizeof(perms[0])) { 42 errno = ENOMEM; 43 fatal("realloc:"); 44 } 45 perms = realloc(perms, permscap * sizeof(perms[0])); 46 if (!perms) 47 fatal("realloc:"); 48 } 49 perms[permslen++] = *p; 50 } 51 52 static void 53 readperm(FILE *file, struct perm *perm) 54 { 55 static char *line; 56 static size_t size; 57 ssize_t n; 58 char *s, *mode; 59 60 n = getline(&line, &size, file); 61 if (n < 0) { 62 if (ferror(file)) 63 fatal("getline:"); 64 perm->name = NULL; 65 return; 66 } 67 if (n && line[n - 1] == '\n') 68 line[n - 1] = '\0'; 69 mode = s = line; 70 s = strchr(s, ' '); 71 if (!s || s == mode) 72 fatal("invalid permissions file"); 73 *s++ = '\0'; 74 perm->name = strdup(s); 75 if (!perm->name) 76 fatal("strdup:"); 77 perm->mode = strtoul(mode, &s, 8); 78 if (*s) 79 fatal("invalid mode '%s'", mode); 80 } 81 82 static int 83 permcmp(struct perm *a, struct perm *b) 84 { 85 return a->name ? b->name ? strcmp(a->name, b->name) : -1 : !!b->name; 86 } 87 88 static noreturn void 89 usage(void) 90 { 91 fprintf(stderr, "usage: mergeperms old cur new\n"); 92 exit(2); 93 } 94 95 int 96 main(int argc, char *argv[]) 97 { 98 FILE *oldf, *curf, *newf; 99 struct perm old, cur, new; 100 int ij, ik, jk; 101 int ret; 102 103 if (argc != 4) 104 usage(); 105 106 ret = 0; 107 oldf = fopen(argv[1], "r"); 108 if (!oldf) 109 fatal("open %s:", argv[1]); 110 curf = fopen(argv[2], "r"); 111 if (!curf) 112 fatal("open %s:", argv[2]); 113 newf = fopen(argv[3], "r"); 114 if (!newf) 115 fatal("open %s:", argv[3]); 116 117 readperm(oldf, &old); 118 readperm(curf, &cur); 119 readperm(newf, &new); 120 for (;;) { 121 ij = permcmp(&old, &cur); 122 ik = permcmp(&old, &new); 123 if (ij < 0 && ik < 0 && old.name) { 124 readperm(oldf, &old); 125 continue; 126 } 127 if (!old.name && !cur.name && !new.name) 128 break; 129 jk = permcmp(&cur, &new); 130 if ((jk < 0 && ij == 0 && old.mode == cur.mode) || (jk > 0 && ik == 0 && old.mode == new.mode)) { 131 /* deleted in cur or new and unchanged in the other */ 132 } else if (jk < 0) { 133 if (ij == 0) 134 ret = 3; 135 addperm(&cur); 136 } else if (jk > 0) { 137 if (ik == 0) 138 ret = 3; 139 addperm(&new); 140 } else if (ij == 0 && old.mode == cur.mode) { 141 addperm(&new); 142 } else { 143 if (cur.mode != new.mode && (ik != 0 || old.mode != new.mode)) 144 ret = 3; 145 addperm(&cur); 146 } 147 if (jk <= 0) 148 readperm(curf, &cur); 149 if (jk >= 0) 150 readperm(newf, &new); 151 } 152 153 fclose(curf); 154 curf = fopen(argv[2], "w"); 155 if (!curf) 156 fatal("open %s:", argv[1]); 157 for (; permslen > 0; --permslen, ++perms) 158 fprintf(curf, "%#o %s\n", perms->mode, perms->name); 159 fflush(curf); 160 if (ferror(curf)) 161 fatal("write error"); 162 163 return ret; 164 }