aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2005-04-28 14:40:22 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2005-04-28 14:40:22 +0000
commit0d8e04d0b65246cc86aabb4beb032a84be184bda (patch)
treeb9be359d35d605c8c8ebe51f51349d19d572da65
downloadalck-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--Makefile13
-rw-r--r--ckaliases.c300
-rw-r--r--ckaliases.h33
-rw-r--r--gram.y252
-rw-r--r--lex.l211
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;
diff --git a/gram.y b/gram.y
new file mode 100644
index 0000000..86578cf
--- /dev/null
+++ b/gram.y
@@ -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);
+}
diff --git a/lex.l b/lex.l
new file mode 100644
index 0000000..c2ab37c
--- /dev/null
+++ b/lex.l
@@ -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);
+}
+

Return to:

Send suggestions and report system problems to the System administrator.