/* * $Id: cf_gen.c,v 1.53 2007/09/17 20:22:36 hno Exp $ * * DEBUG: none Generate squid.conf.default and cf_parser.h * AUTHOR: Max Okumoto * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from * the Internet community; see the CONTRIBUTORS file for full * details. Many organizations have provided support for Squid's * development; see the SPONSORS file for full details. Squid is * Copyrighted (C) 2001 by the Regents of the University of * California; see the COPYRIGHT file for full details. Squid * incorporates software developed and/or copyrighted by other * sources; see the CREDITS file for full details. * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ /***************************************************************************** * Abstract: This program parses the input file and generates code and * files used to configure the variables in squid. * (ie it creates the squid.conf.default file from the cf.data file) * * The output files are as follows: * cf_parser.h - this file contains, default_all() which * initializes variables with the default * values, parse_line() that parses line from * squid.conf.default, dump_config that dumps the * current the values of the variables. * squid.conf.default - default configuration file given to the server * administrator. *****************************************************************************/ #include "squid.h" #include "cf_gen_defines.h" #define MAX_LINE 1024 /* longest configuration line */ #define _PATH_PARSER "cf_parser.h" #define _PATH_SQUID_CONF "squid.conf.default" #define _PATH_CF_DEPEND "cf.data.depend" enum State { sSTART, s1, sDOC, sNOCOMMENT, sEXIT }; typedef struct Line { char *data; struct Line *next; } Line; typedef struct EntryAlias { struct EntryAlias *next; char *name; } EntryAlias; typedef struct Entry { char *name; EntryAlias *alias; char *type; char *loc; char *default_value; Line *default_if_none; char *comment; char *ifdef; Line *doc; Line *nocomment; int array_flag; struct Entry *next; } Entry; typedef struct TypeDep { char *name; struct TypeDep *next; } TypeDep; typedef struct Type { char *name; TypeDep *depend; struct Type *next; } Type; static const char WS[] = " \t\n"; static int gen_default(Entry *, FILE *); static void gen_parse(Entry *, FILE *); static void gen_dump(Entry *, FILE *); static void gen_free(Entry *, FILE *); static void gen_conf(Entry *, FILE *); static void gen_default_if_none(Entry *, FILE *); static void lineAdd(Line ** L, const char *str) { while (*L) L = &(*L)->next; *L = xcalloc(1, sizeof(Line)); (*L)->data = xstrdup(str); } static void checkDepend(const char *directive, const char *name, const Type * types, const Entry * entries) { const Type *type; for (type = types; type; type = type->next) { const TypeDep *dep; if (strcmp(type->name, name) != 0) continue; for (dep = type->depend; dep; dep = dep->next) { const Entry *entry; for (entry = entries; entry; entry = entry->next) { if (strcmp(entry->name, dep->name) == 0) break; } if (!entry) { fprintf(stderr, "ERROR: '%s' (%s) depends on '%s'\n", directive, name, dep->name); exit(1); } } return; } fprintf(stderr, "ERROR: Dependencies for cf.data type '%s' used in '%s' not defined\n", name, directive); exit(1); } int main(int argc, char *argv[]) { FILE *fp; char *input_filename = argv[1]; const char *output_filename = _PATH_PARSER; const char *conf_filename = _PATH_SQUID_CONF; const char *type_depend = argv[2]; int linenum = 0; Entry *entries = NULL; Entry *curr = NULL; Type *types = NULL; enum State state; int rc = 0; char *ptr = NULL; #ifdef _SQUID_OS2_ const char *rmode = "rt"; #else const char *rmode = "r"; #endif char buff[MAX_LINE]; /*-------------------------------------------------------------------* * Parse type dependencies *-------------------------------------------------------------------*/ if ((fp = fopen(type_depend, rmode)) == NULL) { perror(input_filename); exit(1); } while ((NULL != fgets(buff, MAX_LINE, fp))) { const char *type = strtok(buff, WS); const char *dep; Type *t; if (!type || type[0] == '#') continue; t = (Type *) xcalloc(1, sizeof(*t)); t->name = xstrdup(type); while ((dep = strtok(NULL, WS)) != NULL) { TypeDep *d = (TypeDep *) xcalloc(1, sizeof(*d)); d->name = xstrdup(dep); d->next = t->depend; t->depend = d; } t->next = types; types = t; } fclose(fp); /*-------------------------------------------------------------------* * Parse input file *-------------------------------------------------------------------*/ /* Open input file */ if ((fp = fopen(input_filename, rmode)) == NULL) { perror(input_filename); exit(1); } #ifdef _SQUID_WIN32_ setmode(fileno(fp), O_TEXT); #endif state = sSTART; while (feof(fp) == 0 && state != sEXIT) { char *t; if (NULL == fgets(buff, MAX_LINE, fp)) break; linenum++; if ((t = strchr(buff, '\n'))) *t = '\0'; switch (state) { case sSTART: if ((strlen(buff) == 0) || (!strncmp(buff, "#", 1))) { /* ignore empty and comment lines */ (void) 0; } else if (!strncmp(buff, "NAME:", 5)) { char *name, *aliasname; if ((name = strtok(buff + 5, WS)) == NULL) { printf("Error in input file\n"); exit(1); } curr = xcalloc(1, sizeof(Entry)); curr->name = xstrdup(name); while ((aliasname = strtok(NULL, WS)) != NULL) { EntryAlias *alias = xcalloc(1, sizeof(EntryAlias)); alias->next = curr->alias; alias->name = xstrdup(aliasname); curr->alias = alias; } state = s1; } else if (!strcmp(buff, "EOF")) { state = sEXIT; } else if (!strcmp(buff, "COMMENT_START")) { curr = xcalloc(1, sizeof(Entry)); curr->name = xstrdup("comment"); curr->loc = xstrdup("none"); state = sDOC; } else { printf("Error on line %d\n", linenum); printf("--> %s\n", buff); exit(1); } break; case s1: if ((strlen(buff) == 0) || (!strncmp(buff, "#", 1))) { /* ignore empty and comment lines */ (void) 0; } else if (!strncmp(buff, "COMMENT:", 8)) { ptr = buff + 8; while (xisspace(*ptr)) ptr++; curr->comment = xstrdup(ptr); } else if (!strncmp(buff, "DEFAULT:", 8)) { ptr = buff + 8; while (xisspace(*ptr)) ptr++; curr->default_value = xstrdup(ptr); } else if (!strncmp(buff, "DEFAULT_IF_NONE:", 16)) { ptr = buff + 16; while (xisspace(*ptr)) ptr++; lineAdd(&curr->default_if_none, ptr); } else if (!strncmp(buff, "LOC:", 4)) { if ((ptr = strtok(buff + 4, WS)) == NULL) { printf("Error on line %d\n", linenum); exit(1); } curr->loc = xstrdup(ptr); } else if (!strncmp(buff, "TYPE:", 5)) { if ((ptr = strtok(buff + 5, WS)) == NULL) { printf("Error on line %d\n", linenum); exit(1); } /* hack to support arrays, rather than pointers */ if (0 == strcmp(ptr + strlen(ptr) - 2, "[]")) { curr->array_flag = 1; *(ptr + strlen(ptr) - 2) = '\0'; } checkDepend(curr->name, ptr, types, entries); curr->type = xstrdup(ptr); } else if (!strncmp(buff, "IFDEF:", 6)) { if ((ptr = strtok(buff + 6, WS)) == NULL) { printf("Error on line %d\n", linenum); exit(1); } curr->ifdef = xstrdup(ptr); } else if (!strcmp(buff, "DOC_START")) { state = sDOC; } else if (!strcmp(buff, "DOC_NONE")) { /* add to list of entries */ curr->next = entries; entries = curr; state = sSTART; } else { printf("Error on line %d\n", linenum); exit(1); } break; case sDOC: if (!strcmp(buff, "DOC_END") || !strcmp(buff, "COMMENT_END")) { Line *head = NULL; Line *line = curr->doc; /* reverse order of doc lines */ while (line != NULL) { Line *tmp; tmp = line->next; line->next = head; head = line; line = tmp; } curr->doc = head; /* add to list of entries */ curr->next = entries; entries = curr; state = sSTART; } else if (!strcmp(buff, "NOCOMMENT_START")) { state = sNOCOMMENT; } else { Line *line = xcalloc(1, sizeof(Line)); line->data = xstrdup(buff); line->next = curr->doc; curr->doc = line; } break; case sNOCOMMENT: if (!strcmp(buff, "NOCOMMENT_END")) { Line *head = NULL; Line *line = curr->nocomment; /* reverse order of lines */ while (line != NULL) { Line *tmp; tmp = line->next; line->next = head; head = line; line = tmp; } curr->nocomment = head; state = sDOC; } else { Line *line = xcalloc(1, sizeof(Line)); line->data = xstrdup(buff); line->next = curr->nocomment; curr->nocomment = line; } break; case sEXIT: assert(0); /* should never get here */ break; } } if (state != sEXIT) { printf("Error unexpected EOF\n"); exit(1); } else { /* reverse order of entries */ Entry *head = NULL; while (entries != NULL) { Entry *tmp; tmp = entries->next; entries->next = head; head = entries; entries = tmp; } entries = head; } fclose(fp); /*-------------------------------------------------------------------* * Generate default_all() * Generate parse_line() * Generate dump_config() * Generate free_all() * Generate example squid.conf.default file *-------------------------------------------------------------------*/ /* Open output x.c file */ if ((fp = fopen(output_filename, "w")) == NULL) { perror(output_filename); exit(1); } #ifdef _SQUID_WIN32_ setmode(fileno(fp), O_TEXT); #endif fprintf(fp, "/*\n" " * Generated automatically from %s by %s\n" " *\n" " * Abstract: This file contains routines used to configure the\n" " * variables in the squid server.\n" " */\n" "\n", input_filename, argv[0] ); rc = gen_default(entries, fp); gen_default_if_none(entries, fp); gen_parse(entries, fp); gen_dump(entries, fp); gen_free(entries, fp); fclose(fp); /* Open output x.conf file */ if ((fp = fopen(conf_filename, "w")) == NULL) { perror(conf_filename); exit(1); } #ifdef _SQUID_WIN32_ setmode(fileno(fp), O_TEXT); #endif gen_conf(entries, fp); fclose(fp); return (rc); } static int gen_default(Entry * head, FILE * fp) { Entry *entry; int rc = 0; fprintf(fp, "static void\n" "default_line(const char *s)\n" "{\n" "\tLOCAL_ARRAY(char, tmp_line, BUFSIZ);\n" "\txstrncpy(tmp_line, s, BUFSIZ);\n" "\txstrncpy(config_input_line, s, BUFSIZ);\n" "\tconfig_lineno++;\n" "\tparse_line(tmp_line);\n" "}\n" ); fprintf(fp, "static void\n" "default_all(void)\n" "{\n" "\tcfg_filename = \"Default Configuration\";\n" "\tconfig_lineno = 0;\n" ); for (entry = head; entry != NULL; entry = entry->next) { assert(entry->name != NULL); assert(entry != entry->next); if (!strcmp(entry->name, "comment")) continue; if (entry->loc == NULL) { fprintf(stderr, "NO LOCATION FOR %s\n", entry->name); rc |= 1; continue; } if (entry->default_value == NULL) { fprintf(stderr, "NO DEFAULT FOR %s\n", entry->name); rc |= 1; continue; } assert(entry->default_value != NULL); if (entry->ifdef) fprintf(fp, "#if %s\n", entry->ifdef); if (strcmp(entry->default_value, "none") == 0) { fprintf(fp, "\t/* No default for %s */\n", entry->name); } else { fprintf(fp, "\tdefault_line(\"%s %s\");\n", entry->name, entry->default_value); } if (entry->ifdef) fprintf(fp, "#endif\n"); } fprintf(fp, "\tcfg_filename = NULL;\n"); fprintf(fp, "}\n\n"); return rc; } static void gen_default_if_none(Entry * head, FILE * fp) { Entry *entry; Line *line; fprintf(fp, "static void\n" "defaults_if_none(void)\n" "{\n" ); for (entry = head; entry != NULL; entry = entry->next) { assert(entry->name != NULL); assert(entry->loc != NULL); if (entry->default_if_none == NULL) continue; if (entry->ifdef) fprintf(fp, "#if %s\n", entry->ifdef); if (entry->default_if_none) { fprintf(fp, "\tif (check_null_%s(%s)) {\n", entry->type, entry->loc); for (line = entry->default_if_none; line; line = line->next) fprintf(fp, "\t\tdefault_line(\"%s %s\");\n", entry->name, line->data); fprintf(fp, "\t}\n"); } if (entry->ifdef) fprintf(fp, "#endif\n"); } fprintf(fp, "}\n\n"); } static void gen_parse(Entry * head, FILE * fp) { Entry *entry; char *name; EntryAlias *alias; fprintf(fp, "static int\n" "parse_line(char *buff)\n" "{\n" "\tint\tresult = 1;\n" "\tchar\t*token;\n" "\tdebug(0,10)(\"parse_line: %%s\\n\", buff);\n" "\tif ((token = strtok(buff, w_space)) == NULL)\n" "\t\t(void) 0;\t/* ignore empty lines */\n" ); for (entry = head; entry != NULL; entry = entry->next) { if (strcmp(entry->name, "comment") == 0) continue; if (entry->ifdef) fprintf(fp, "#if %s\n", entry->ifdef); name = entry->name; alias = entry->alias; next_alias: fprintf(fp, "\telse if (!strcmp(token, \"%s\"))\n", name); assert(entry->loc != NULL); if (strcmp(entry->loc, "none") == 0) { fprintf(fp, "\t\tparse_%s();\n", entry->type ); } else { fprintf(fp, "\t\tparse_%s(&%s%s);\n", entry->type, entry->loc, entry->array_flag ? "[0]" : "" ); } if (alias) { name = alias->name; alias = alias->next; goto next_alias; } if (entry->ifdef) fprintf(fp, "#endif\n"); } fprintf(fp, "\telse\n" "\t\tresult = 0; /* failure */\n" "\treturn(result);\n" "}\n\n" ); } static void gen_dump(Entry * head, FILE * fp) { Entry *entry; fprintf(fp, "static void\n" "dump_config(StoreEntry *entry)\n" "{\n" ); for (entry = head; entry != NULL; entry = entry->next) { assert(entry->loc != NULL); if (strcmp(entry->loc, "none") == 0) continue; if (strcmp(entry->name, "comment") == 0) continue; if (entry->ifdef) fprintf(fp, "#if %s\n", entry->ifdef); fprintf(fp, "\tdump_%s(entry, \"%s\", %s);\n", entry->type, entry->name, entry->loc); if (entry->ifdef) fprintf(fp, "#endif\n"); } fprintf(fp, "}\n\n"); } static void gen_free(Entry * head, FILE * fp) { Entry *entry; fprintf(fp, "static void\n" "free_all(void)\n" "{\n" ); for (entry = head; entry != NULL; entry = entry->next) { assert(entry->loc != NULL); if (strcmp(entry->loc, "none") == 0) continue; if (strcmp(entry->name, "comment") == 0) continue; if (entry->ifdef) fprintf(fp, "#if %s\n", entry->ifdef); fprintf(fp, "\tfree_%s(&%s%s);\n", entry->type, entry->loc, entry->array_flag ? "[0]" : ""); if (entry->ifdef) fprintf(fp, "#endif\n"); } fprintf(fp, "}\n\n"); } static int defined(char *name) { int i = 0; if (!name) return 1; for (i = 0; strcmp(defines[i].name, name) != 0; i++) { assert(defines[i].name != NULL); } return defines[i].defined; } static const char * available_if(char *name) { int i = 0; assert(name != NULL); for (i = 0; strcmp(defines[i].name, name) != 0; i++) { assert(defines[i].name != NULL); } return defines[i].enable; } static void gen_conf(Entry * head, FILE * fp) { Entry *entry; char buf[8192]; Line *def = NULL; for (entry = head; entry != NULL; entry = entry->next) { Line *line; int blank = 1; if (!strcmp(entry->name, "comment")) (void) 0; else fprintf(fp, "# TAG: %s", entry->name); if (entry->comment) fprintf(fp, "\t%s", entry->comment); fprintf(fp, "\n"); if (!defined(entry->ifdef)) { fprintf(fp, "# Note: This option is only available if Squid is rebuilt with the\n"); fprintf(fp, "# %s option\n#\n", available_if(entry->ifdef)); } for (line = entry->doc; line != NULL; line = line->next) { fprintf(fp, "#%s\n", line->data); } if (entry->default_value && strcmp(entry->default_value, "none") != 0) { snprintf(buf, sizeof(buf), "%s %s", entry->name, entry->default_value); lineAdd(&def, buf); } if (entry->default_if_none) { for (line = entry->default_if_none; line; line = line->next) { snprintf(buf, sizeof(buf), "%s %s", entry->name, line->data); lineAdd(&def, buf); } } if (entry->nocomment) blank = 0; if (!def && entry->doc && !entry->nocomment && strcmp(entry->name, "comment") != 0) lineAdd(&def, "none"); if (def && (entry->doc || entry->nocomment)) { if (blank) fprintf(fp, "#\n"); fprintf(fp, "#Default:\n"); while (def != NULL) { line = def; def = line->next; fprintf(fp, "# %s\n", line->data); xfree(line->data); xfree(line); } blank = 1; } if (entry->nocomment && blank) fprintf(fp, "#\n"); for (line = entry->nocomment; line != NULL; line = line->next) { fprintf(fp, "%s\n", line->data); } if (entry->doc != NULL) { fprintf(fp, "\n"); } } }