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
11struct perm {
12 char *name;
13 mode_t mode;
14};
15
16static struct perm *perms;
17static size_t permslen, permscap;
18
19static noreturn void
20fatal(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
36static void
37addperm(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
52static void
53readperm(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
82static int
83permcmp(struct perm *a, struct perm *b)
84{
85 return a->name ? b->name ? strcmp(a->name, b->name) : -1 : !!b->name;
86}
87
88static noreturn void
89usage(void)
90{
91 fprintf(stderr, "usage: mergeperms old cur new\n");
92 exit(2);
93}
94
95int
96main(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}