diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2005-04-28 14:40:22 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2005-04-28 14:40:22 +0000 |
commit | 0d8e04d0b65246cc86aabb4beb032a84be184bda (patch) | |
tree | b9be359d35d605c8c8ebe51f51349d19d572da65 | |
download | alck-0d8e04d0b65246cc86aabb4beb032a84be184bda.tar.gz alck-0d8e04d0b65246cc86aabb4beb032a84be184bda.tar.bz2 |
Added to the repository
git-svn-id: file:///svnroot/gsc/trunk@38 d2de0444-eb31-0410-8365-af798a554d48
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | ckaliases.c | 300 | ||||
-rw-r--r-- | ckaliases.h | 33 | ||||
-rw-r--r-- | gram.y | 252 | ||||
-rw-r--r-- | lex.l | 211 |
5 files changed, 809 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..06c97fb --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CFLAGS=-ggdb +ckaliases: lex.yy.o y.tab.o ckaliases.o + cc -ggdb -o $@ $^ + +y.tab.c y.tab.h: gram.y + yacc -vtd gram.y + +lex.yy.c: lex.l + lex -d lex.l + +lex.yy.o: lex.yy.c y.tab.h + +clean:; rm -rf *.o ckaliases
\ No newline at end of file diff --git a/ckaliases.c b/ckaliases.c new file mode 100644 index 0000000..d128c0d --- /dev/null +++ b/ckaliases.c @@ -0,0 +1,300 @@ +#include <stdio.h> +#include <stdlib.h> +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free +#include <obstack.h> +#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++) { + 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); +} diff --git a/ckaliases.h b/ckaliases.h new file mode 100644 index 0000000..6fdeded --- /dev/null +++ b/ckaliases.h @@ -0,0 +1,33 @@ +extern char *file_name; +extern int line_num; + +void init_lex(); +void lex_debug(int n); +void openaliases(char *name); +void openaliases_prefix(char *prefix, char *name); + +struct string_list { + struct string_list *next; + char *str; +}; + +typedef struct slist { + struct string_list *head, *tail; + int count; +} SLIST; + +void slist_add(SLIST **plist, char *str); +void slist_append(SLIST **pdst, SLIST *src); +char *slist_member(SLIST *plist, char *name); +void slist_destroy(SLIST **plist); + +void read_include(SLIST **plist, char *name); + +void regalias(char *name, SLIST *exp); +void begin_aliases(void); +void end_aliases(void); + +void error(char *fmt, ...); + +extern SLIST *cw_list; +extern int verbose; @@ -0,0 +1,252 @@ +%{ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <getopt.h> +#include <errno.h> +#include "ckaliases.h" + +SLIST *cw_list; /* List of domain names pertaining to Sendmail 'w' class */ +static int restricted; /* prohibit use of `special' aliases (pipes, file redirections + and includes */ +int verbose; /* Verbose mode */ +static int error_count; /* Number of errors detected so far */ +%} + +%union { + char *string; + SLIST *slist; +}; + +%token <string> IDENT EMAIL STRING LHS +%token CONT +%token EOL +%token INCLUDE + +%type <string> string lhs +%type <slist> rhs emails email + +%% + +input : list + ; + +list : alias + | list EOL alias + | list error EOL + { + yyclearin; + yyerrok; + } + ; + +alias : /* empty */ + | lhs rhs + { + regalias($1, $2); + } + ; + +lhs : LHS ':' + ; + +rhs : emails + | rhs CONT emails + { + slist_append(&$1, $3); + $$ = $1; + } + ; + +emails: email + | emails ',' email + { + slist_append(&$1, $3); + $$ = $1; + } + ; + +email : string + { + if (restricted && ($1[0] == '|' || $1[0] == '/')) { + yyerror("Construct not allowed"); + YYERROR; + } + $$ = NULL; + slist_add(&$$, $1); + } + | EMAIL + { + $$ = NULL; + slist_add(&$$, $1); + } + | INCLUDE string + { + if (restricted) { + yyerror("Include statement is not allowed"); + YYERROR; + } + $$ = NULL; + read_include(&$$, $2); + } + ; + +string: IDENT + | STRING + ; + +%% + +void +error(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + error_count++; +} + +yyerror(char *s) +{ + error("%s:%d: %s", file_name, line_num, s); +} + + + +void +usage() +{ + printf("usage: ckaliases [OPTIONS] [FILES...]\n"); + printf("OPTIONS and FILES may be interspered.\n"); + printf("Valid options are:\n"); + printf(" -d SPEC Set debug level. SPEC consists of the following\n"); + printf(" letters:\n"); + printf(" y enable parser debugging\n"); + printf(" l enable lexical analizer debugging\n"); + printf(" Upper-case variants are also accepted. Prepending\n"); + printf(" a letter with '-' reverts its sense\n"); + printf(" -h Display this help list\n"); + printf(" -r Restrict alias file syntax to aliases only (i.e.\n"); + printf(" prohibit use of pipes and file redirections\n"); + printf(" -u Revert the effect of the previous -r option\n"); + printf(" -v Verbose mode\n"); + printf(" -w FILE Read contents of Sendmail `w' class from the given\n"); + printf(" file.\n"); +} + + +int +main(int argc, char **argv) +{ + char *p; + int c; + int file_count = 0; + int true = 1; + char *cwfile = "/etc/mail/sendmail.cw"; + SLIST *file_list; /* List of files to be read */ + struct string_list *s; + + begin_aliases(); + init_lex(); + while ((c = getopt(argc, argv, "-d:f:hp:ruvw:")) != EOF) { + switch (c) { + case 1: + if (!cw_list) + read_include(&cw_list, cwfile); + openaliases(optarg); + yyparse(); + file_count++; + break; + + case 'd': + for (p = optarg; *p; p++) { + switch (*p) { + case '-': + true = 0; + break; + case 'y': + case 'Y': + yydebug = true; + true = 1; + break; + case 'l': + case 'L': + lex_debug(true); + true = 1; + break; + default: + fprintf(stderr, "%s: unknown debug option %c\n", + argv[0], *p); + exit(1); + } + } + break; + + case 'f': + if (!cw_list) + read_include(&cw_list, cwfile); + file_list = NULL; + read_include(&file_list, optarg); + for (s = file_list->head; s; s = s->next) { + openaliases_prefix(optarg, s->str); + yyparse(); + file_count++; + } + slist_destroy(&file_list); + break; + + case 'h': + usage(); + exit(0); + + case 'r': + restricted = 1; + break; + + case 'u': + restricted = 0; + break; + + case 'v': + verbose++; + break; + + case 'w': + if (file_count) { + error("-w must be used before first non-option argument"); + exit(1); + } + cwfile = optarg; + break; + + default: + exit(1); + } + } + + argc -= optind; + argv += optind; + + if (!cw_list) + read_include(&cw_list, cwfile); + while (argc--) { + openaliases(*argv++); + yyparse(); + file_count++; + } + + if (!file_count) { + error("no files specified"); + exit(1); + } + + if (verbose) + printf("%d files\n", file_count); + end_aliases(); + check_aliases(); + if (verbose) + printf("%lu errors\n", error_count); + exit(error_count!=0); +} @@ -0,0 +1,211 @@ +%{ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free +#include <obstack.h> +#include "ckaliases.h" +#include "y.tab.h" + +static void line_begin(void); +static void line_add(char *text, size_t len); +static void line_add_unescape(char *text, size_t len); +static void line_finish(void); + +struct obstack string_stk; + +char *file_name; +int line_num; + +%} + + +%x STR +IDENT [a-zA-Z0-9_\-+\./]+ +WS [ \t] +SPEC [:@\\] +%% + /* Comments */ +#.*\n { line_num++; return EOL; } + /* White space */ +^{WS}+\n { line_num++; return EOL; } +{WS}+ ; + /* Names and emails */ +:include: return INCLUDE; +^{IDENT} { + line_begin(); + line_add(yytext, yyleng); + line_finish(); + return LHS; } +{IDENT}@{IDENT} { + line_begin(); + line_add(yytext, yyleng); + line_finish(); + return EMAIL; } +{IDENT} { line_begin(); + line_add(yytext, yyleng); + line_finish(); + return IDENT; } + /* Quoted strings */ +\"[^\\"\n]*\" { line_begin(); + line_add(yytext, yyleng); + line_finish(); + return STRING; } +\"[^\\"\n]*\\. { BEGIN(STR); + line_begin(); + line_add_unescape(yytext + 1, yyleng - 1); } +<STR>[^\\"\n]*\\. { line_add_unescape(yytext, yyleng); } +<STR>[^\\"\n]*\" { BEGIN(INITIAL); + if (yyleng > 1) + line_add(yytext, yyleng - 1); + line_finish(); + return STRING; } + /* Other characters */ +{SPEC} return yytext[0]; +\\\n { line_num++; } +\n{WS}+/[^ \t\n] { line_num++; return CONT; } +\n { line_num++; return EOL; } +. { char *p; + asprintf(&p, + "Stray character %03o in alias file", yytext[0]); + yyerror(p); + free (p); } +%% + +int +yywrap() +{ + fclose(yyin); + return 1; +} + +static char escape_transtab[] = "\\\\a\ab\bf\fn\nr\rt\t"; + +int +unescape_char (int c) +{ + char *p; + + for (p = escape_transtab; *p; p += 2) + { + if (*p == c) + return p[1]; + } + return c; +} + +void +line_add(char *text, size_t len) +{ + obstack_grow(&string_stk, text, len); +} + +void +line_add_unescape(char *text, size_t len) +{ + char c; + obstack_grow(&string_stk, text, len - 2); + c = unescape_char(text[len - 1]); + obstack_1grow(&string_stk, c); +} + +void +line_begin() +{ +} + +void +line_finish() +{ + obstack_1grow(&string_stk, 0); + yylval.string = obstack_finish(&string_stk); +} + +void +openaliases(char *name) +{ + yyin = fopen(name, "r"); + if (!yyin) { + fprintf(stderr, "cannot open file `%s': %s\n", + name, strerror(errno)); + exit(1); + } + file_name = name; + line_num = 0; +} + +void +openaliases_prefix(char *prefix, char *name) +{ + char *fullname = NULL; + struct stat st; + + if (stat(prefix, &st)) { + fprintf(stderr, "cannot stat `%s': %s\n", + prefix, strerror(errno)); + exit(1); + } + + if (!S_ISDIR(st.st_mode)) { + char *p = strrchr(prefix, '/'); + if (p) + *p = 0; + else + prefix = "."; + } + asprintf(&fullname, "%s/%s", prefix, name); + openaliases(fullname); + free(fullname); +} + +void +init_lex() +{ + obstack_init(&string_stk); + yy_flex_debug = 0; +} + +void +lex_debug(int debug) +{ + yy_flex_debug = debug; +} + +void +read_include(SLIST **plist, char *name) +{ + char *p; + char buffer[256]; + FILE *fp = fopen(name, "r"); + + if (!fp) { + char *p; + asprintf(&p, + "cannot open include file `%s': %s", + name, strerror(errno)); + yyerror(p); + free(p); + return; + } + + while (p = fgets(buffer, sizeof buffer, fp)) { + char *q; + + while (*p && isspace(*p)) + p++; + if (*p == '#') + continue; + for (q = p + strlen(p) - 1; q > p && isspace(*q); q--) + ; + q[1] = 0; + if (*p) + slist_add(plist, strdup(p)); + } + fclose(fp); +} + |