/* ckaliases - verify syntax of sendmail-style alias files Copyright (C) 2005 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #define obstack_chunk_alloc malloc #define obstack_chunk_free free #include #include "ckaliases.h" void * xmalloc(size_t size) { void *p = malloc(size); if (!p) { fprintf(stderr, "not enough memory\n"); exit(1); } return p; } #ifndef CHAR_BIT # define CHAR_BIT 8 #endif #define BITS_PER_WORD (sizeof(unsigned)*CHAR_BIT) #define MAXTABLE 32767 #define WORDSIZE(n) (((n) + BITS_PER_WORD - 1) / BITS_PER_WORD) #define SETBIT(x, i) ((x)[(i)/BITS_PER_WORD] |= (1<<((i) % BITS_PER_WORD))) #define RESETBIT(x, i) ((x)[(i)/BITS_PER_WORD] &= ~(1<<((i) % BITS_PER_WORD))) #define BITISSET(x, i) (((x)[(i)/BITS_PER_WORD] & (1<<((i) % BITS_PER_WORD))) != 0) /* given n by n matrix of bits R, modify its contents to be the transitive closure of what was given. */ void TC(unsigned *R, int n) { register int rowsize; register unsigned mask; register unsigned *rowj; register unsigned *rp; register unsigned *rend; register unsigned *ccol; unsigned *relend; unsigned *cword; unsigned *rowi; rowsize = WORDSIZE(n) * sizeof(unsigned); relend = (unsigned *) ((char *) R + (n * rowsize)); cword = R; mask = 1; rowi = R; while (rowi < relend) { ccol = cword; rowj = R; while (rowj < relend) { if (*ccol & mask) { rp = rowi; rend = (unsigned *) ((char *) rowj + rowsize); while (rowj < rend) *rowj++ |= *rp++; } else { rowj = (unsigned *) ((char *) rowj + rowsize); } ccol = (unsigned *) ((char *) ccol + rowsize); } mask <<= 1; if (mask == 0) { mask = 1; cword++; } rowi = (unsigned *) ((char *) rowi + rowsize); } } void slist_add(SLIST **plist, char *str) { struct string_list *p = xmalloc(sizeof(*p)); p->str = str; p->next = NULL; if (!*plist) { *plist = xmalloc(sizeof(**plist)); (*plist)->head = NULL; } if ((*plist)->head == NULL) { (*plist)->head = p; (*plist)->count = 0; } else { (*plist)->tail->next = p; (*plist)->count++; } (*plist)->tail = p; } void slist_append(SLIST **pdst, SLIST *src) { struct string_list *tail; if (!*pdst) { *pdst = xmalloc(sizeof(**pdst)); (*pdst)->head = NULL; (*pdst)->count = 0; } if ((*pdst)->head = NULL) (*pdst)->head = src->head; for (tail = src->tail; tail->next; tail = tail->next) ; (*pdst)->tail = tail; (*pdst)->count += src->count; } char * slist_member(SLIST *plist, char *name) { struct string_list *p; if (plist) for (p = plist->head; p; p = p->next) if (p->str && strcmp(p->str, name) == 0) return p->str; return NULL; } void slist_destroy(SLIST **plist) { struct string_list *p; if (!plist || !*plist) return; p = (*plist)->head; while (p) { struct string_list *next = p->next; free(p); p = next; } free(*plist); *plist = NULL; } typedef struct { char *name; SLIST *exp; } ALIAS; struct obstack alias_stk; unsigned alias_count; ALIAS *aliases; void regalias(char *name, SLIST *exp) { ALIAS a; a.name = name; a.exp = exp; obstack_grow(&alias_stk, &a, sizeof a); alias_count++; } void begin_aliases() { obstack_init(&alias_stk); } static int alias_cmp(const void *a, const void *b) { return strcmp(((ALIAS*)a)->name, ((ALIAS*)b)->name); } static int alias_cmp2(const void *a, const void *b) { char *aname = ((ALIAS*)a)->name; char *bname = ((ALIAS*)b)->name; int rc; int alen; int blen; char *p; if ((p = strchr(aname, '@')) && slist_member(cw_list, p+1)) alen = p - aname; else alen = strlen(aname); if ((p = strchr(bname, '@')) && slist_member(cw_list, p+1)) blen = p - bname; else blen = strlen(bname); if (alen == blen) return memcmp(aname, bname, alen); return strcmp(aname, bname); } void end_aliases() { int i; aliases = obstack_finish(&alias_stk); qsort(aliases, alias_count, sizeof aliases[0], alias_cmp); for (i = 1; i < alias_count; i++) if (alias_cmp(aliases + i - 1, aliases + i) == 0) error("alias `%s' multiply defined", aliases[i].name); } int find_alias(char *name) { ALIAS a, *p; if (!name) return -1; a.name = name; p = bsearch(&a, aliases, alias_count, sizeof aliases[0], alias_cmp2); return p ? p - aliases : -1; } static void alias_setbit(unsigned *r, unsigned rowsize, unsigned row, unsigned col) { SETBIT(r + rowsize * row, col); } static int alias_bitisset(unsigned *r, unsigned rowsize, unsigned row, unsigned col) { return BITISSET(r + rowsize * row, col); } void mark_connected(unsigned *r, unsigned size) { int i; for (i = 0; i < alias_count; i++) { if (aliases[i].exp) { struct string_list *p; for (p = aliases[i].exp->head; p; p = p->next) { int n = find_alias(p->str); if (n >= 0) alias_setbit(r, size, i, n); } } } } void check_circular_deps(unsigned *r, unsigned size) { int i; for (i = 0; i < alias_count; i++) { if (alias_bitisset(r, size, i, i)) error("%s: circular dependency", aliases[i].name); } } void check_aliases() { size_t size; unsigned *r; /* Allocate matrix */ size = (alias_count + BITS_PER_WORD - 1) / BITS_PER_WORD; r = xmalloc(alias_count*size*sizeof(*r)); memset(r, 0, alias_count*size*sizeof(*r)); /* First pass: mark directly connected entries */ mark_connected(r, size); /* Compute transitive closure of the matrix r */ TC(r, alias_count); /* Third pass: check for circular deps */ check_circular_deps(r, size); if (verbose) printf("%lu aliases\n", alias_count); }