master hovercats/oakiss / pkg / skeleton / mergeperms.c
  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}