/*========================================================================*\ Copyright (c) 2000 Paul Vojta Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following condition: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL PAUL VOJTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \*========================================================================*/ /* * Not done yet: * nchoice * text */ #define VERSION "0.92" #include #include #include #include #include #include #include #ifndef ARCH # if __alpha || __alpha__ # define ARCH "alpha" # elif __arm || __arm__ # define ARCH "arm" # elif __i386 || __i386__ # define ARCH "i386" # elif __ia64 || __ia64__ # define ARCH "ia64" # elif __m68k || __m68k__ # define ARCH "m68k" # elif __mips64 || __mips64__ # define ARCH "mips64" # elif __mips || __mips__ # define ARCH "mips" # elif __ppc || __ppc__ # define ARCH "ppc" # elif __s390 || __s390__ # define ARCH "s390" # elif __sh || __sh__ # define ARCH "sh" # elif __sparc64 || __sparc64__ # define ARCH "sparc64" # elif __sparc || __sparc__ # define ARCH "sparc" # else # error Which architecture? # endif #endif #define NUMBER(x) (sizeof (x) / sizeof *(x)) #define NORETURN volatile typedef int Boolean; #define True 1 #define False 0 /* * Command-line arguments */ const char *path_qc_in = "qconfig.in"; const char *path_qc_out = "qconfig.out"; const char *path_dot_cfg = ".config"; const char *path_autoconf = "include/linux/autoconf.h"; char *path_defaults = NULL; char *path_config_in = NULL; const char *arch = NULL; const char *trace_var = NULL; int trace_var_len = 0; struct option { const char *longname; short shortname; void *addr; }; static const struct option options[] = { {"input", 'i', &path_qc_in}, {"output", 'o', &path_qc_out}, {"config", 'c', &path_dot_cfg}, {"headers", 'h', &path_autoconf}, {"defaults", 'd', &path_defaults}, {"script", 's', &path_config_in}, {"arch", 'a', &arch}, {"trace", 't', &trace_var}, {"nominal", 'n', NULL}, {"version", 'v', NULL}, {"help", '-', NULL}}; const char *path_tmp_out = NULL; const char *path_tmp_config= NULL; const char *path_tmp_header= NULL; #define INCR 80 #define ARGC_INCR 10 /* * Global information */ /* Current file. */ FILE *f; int lineno; int lineno_begin; const char *filename; /* Other files. */ FILE *out_file; /* qconfig.out */ FILE *config_file; /* .config */ FILE *header_file; /* config.h */ /* * My usual utilities. */ NORETURN void clean_exit(void) { if (out_file != NULL) { fclose(out_file); if (path_tmp_out != NULL) unlink(path_tmp_out); } if (config_file != NULL) { fclose(config_file); if (path_tmp_config != NULL) unlink(path_tmp_config); } if (header_file != NULL) { fclose(header_file); if (path_tmp_header != NULL) unlink(path_tmp_header); } exit(1); } NORETURN void oops(const char *message, ...) { va_list args; va_start(args, message); fputs("qconfig: ", stderr); vfprintf(stderr, message, args); va_end(args); putc('\n', stderr); clean_exit(); } NORETURN void opt_oops(const char *message, ...) { va_list args; va_start(args, message); fputs("qconfig: ", stderr); vfprintf(stderr, message, args); va_end(args); fputs("\nTry `qconfig --help' for more information.\n", stderr); clean_exit(); } NORETURN void line_oops(const char *message, ...) { va_list args; va_start(args, message); if (lineno == lineno_begin) fprintf(stderr, "qconfig: %s line %d: ", filename, lineno); else fprintf(stderr, "qconfig: %s lines %d-%d: ", filename, lineno_begin, lineno); vfprintf(stderr, message, args); va_end(args); putc('\n', stderr); clean_exit(); } static void * xmalloc(unsigned size) { void *mem = (void *) malloc(size); if (mem == NULL) oops("Cannot allocate %u bytes.\n", size); return mem; } static void * xrealloc(void *oldp, unsigned size) { void *mem; mem = oldp == NULL ? (void *) malloc(size) : (void *) realloc(oldp, size); if (mem == NULL) oops("Cannot reallocate %u bytes.\n", size); return mem; } static char * xstrdup(const char *p) { unsigned len = strlen(p) + 1; return memcpy(xmalloc(len), p, len); } /* * Storing the shell variables. * The algorithm is the AVL algorithm from Knuth Vol. 3. */ struct var { /* data structure for storing "shell" variables */ const char *var_name; /* key */ int len; /* length of key */ const char *value; /* value of variable */ Boolean accessed; /* if it's been accessed yet */ int bal; /* AVL balancing information */ struct var *left; struct var *right; }; struct var *var_head; struct var *var_config_modules; #define config_modules() (var_config_modules->accessed = True, \ *var_config_modules->value == 'y') static struct var * find_var(const char *var_name, int len) { struct var *tp; struct var **tpp; struct var *sp; /* place where rebalancing may be necessary */ struct var **spp; /* points to sp */ int i; /* Search */ spp = tpp = &var_head; for (;;) { tp = *tpp; if (tp == NULL) /* bottom of tree */ break; if (tp->bal != 0) spp = tpp; i = len - tp->len; if (i == 0) i = memcmp(var_name, tp->var_name, len); if (i == 0) /* found record already */ return tp; if (i < 0) /* move left */ tpp = &tp->left; else tpp = &tp->right; } /* Insert */ tp = xmalloc(sizeof *tp); tp->var_name = memcpy(xmalloc(len), var_name, len); tp->len = len; tp->value = NULL; tp->accessed = False; tp->bal = 0; tp->left = tp->right = NULL; *tpp = tp; /* Adjust balance factors */ sp = *spp; if (sp == tp) return tp; i = len - sp->len; if (i == 0) i = memcmp(var_name, sp->var_name, len); sp = (i < 0 ? sp->left : sp->right); while (sp != tp) { i = len - sp->len; if (i == 0) i = memcmp(var_name, sp->var_name, len); if (i < 0) { sp->bal = -1; sp = sp->left; } else { sp->bal = 1; sp = sp->right; } } /* Balancing act */ sp = *spp; i = len - sp->len; if (i == 0) i = memcmp(var_name, sp->var_name, len); if (i < 0) { if (sp->bal >= 0) --sp->bal; else { /* need to rebalance */ struct var *left; left = sp->left; if (left->bal < 0) { /* single rotation */ sp->left = left->right; left->right = sp; sp->bal = left->bal = 0; *spp = left; } else { /* double rotation */ struct var *newtop; newtop = left->right; sp->left = newtop->right; newtop->right = sp; left->right = newtop->left; newtop->left = left; sp->bal = left->bal = 0; if (newtop->bal < 0) ++sp->bal; else if (newtop->bal > 0) --left->bal; newtop->bal = 0; *spp = newtop; } } } else { if (sp->bal <= 0) ++sp->bal; else { /* need to rebalance */ struct var *right; right = sp->right; if (right->bal > 0) { /* single rotation */ sp->right = right->left; right->left = sp; sp->bal = right->bal = 0; *spp = right; } else { /* double rotation */ struct var *newtop; newtop = right->left; sp->right = newtop->left; newtop->left = sp; right->left = newtop->right; newtop->right = right; sp->bal = right->bal = 0; if (newtop->bal > 0) --sp->bal; else if (newtop->bal < 0) ++right->bal; newtop->bal = 0; *spp = newtop; } } } return tp; } static void define_var(const char *str, int len, const char *value) { struct var *vp; vp = find_var(str, len); if (vp->value != NULL) free((char *) vp->value); vp->value = xstrdup(value); if (len == trace_var_len && memcmp(str, trace_var, len) == 0) printf("Variable %s modified at %s:%d\n", trace_var, filename, lineno); if (vp->accessed) { fputs("Warning: variable ", stdout); fwrite(str, 1, len, stdout); puts(" modified after being used."); } } static const char * lookup_var(const char *str, int len) { return find_var(str, len)->value; } /* * Reading from the file. */ char *line; unsigned line_max; const char *lp; static Boolean getline(void) { unsigned len; ++lineno; lp = line; for (len = 0;;) { if (fgets(line + len, line_max - len, f) == NULL) { line[len] = '\0'; return (len > 0); } len += strlen(line + len); if (len > 0 && line[len - 1] == '\n') { line[--len] = '\0'; break; } if (len == line_max - 1) lp = line = xrealloc(line, line_max += INCR); } return True; } int argc; /* The usual */ char **argv; char *args; /* Area for storing args */ unsigned args_max; /* size of that area */ char *args_end; /* args + args_max */ char *args_pos; /* where we are now */ unsigned argc_max; /* maximum value of argc */ int *arg_lengths; /* lengths of arguments */ Boolean argv_needs_more;/* if argv is temporarily shorter than argc */ /* * Note: this is used separately by read_defaults_file. */ static void arg_put(const char *p, unsigned len) { unsigned args_pos_int; if (args_pos + len >= args_end) { args_pos_int = args_pos - args; do args_max += INCR; while (args_pos_int + len >= args_max); args = xrealloc(args, args_max); args_pos = args + args_pos_int; args_end = args + args_max; } memcpy(args_pos, p, len); args_pos += len; } static void arg_close(int arg_begin_len) { arg_put("", 1); if (argc >= argc_max) { arg_lengths = xrealloc(arg_lengths, (argc_max += ARGC_INCR) * sizeof(int)); argv_needs_more = True; } arg_lengths[argc] = (args_pos - args) - arg_begin_len; ++argc; } const char * arg_lookup_var(Boolean skipping) { int len_save; char *str_end; struct var *vp; ++lp; len_save = args_pos - args; for (;;) { if ((*lp >= 'A' && *lp <= 'Z') || (*lp >= 'a' && *lp <= 'z') || (*lp >= '0' && *lp <= '9') || *lp == '_') { const char *p0 = lp; do ++lp; while ((*lp >= 'A' && *lp <= 'Z') || (*lp >= 'a' && *lp <= 'z') || (*lp >= '0' && *lp <= '9') || *lp == '_'); arg_put(p0, lp - p0); } else if (*lp == '\\' && lp[1] == '\0') getline(); else break; } str_end = args_pos; args_pos = args + len_save; vp = find_var(args_pos, str_end - args_pos); if (!skipping) { if (str_end - args_pos == trace_var_len && memcmp(args_pos, trace_var, trace_var_len) == 0) printf("Variable %s used at %s:%d\n", trace_var, filename, lineno); vp->accessed = True; } return vp->value; } static Boolean pre_tokenize(Boolean skipping) { int arg_begin_len; /* position of start of current arg */ Boolean force_arg; /* force us to start a new arg */ int i; char *q; do { if (*lp == ';') ++lp; else if (!getline()) return False; argc = 0; args_pos = args; argv_needs_more = False; arg_begin_len = 0; force_arg = False; lineno_begin = lineno; for (;;) { if (*lp == '\0' || *lp == ';' || *lp == '#' || *lp == ' ' || *lp == '\t') { if (force_arg || args_pos - args > arg_begin_len) { /* Close off current argument */ arg_close(arg_begin_len); arg_begin_len = args_pos - args; force_arg = False; } while (*lp == ' ' || *lp == '\t') ++lp; if (*lp == '\0' || *lp == '#' || *lp == ';') break; /* end of command */ } else if (*lp == '|' || *lp == '&' || *lp == '(' || *lp == ')' || *lp == '<' || *lp == '>' || *lp == '`') line_oops("Special character %c not supported.", *lp); else if (*lp == '\'') { const char *p0; p0 = ++lp; for (;;) { if (*lp == '\0') line_oops("Unmatched ' at end of line"); if (*lp == '\'') break; ++lp; } arg_put(p0, lp - p0); ++lp; force_arg = True; } else if (*lp == '"') { ++lp; for (;;) { if (*lp == '\0') line_oops("Unmatched \" at end of line"); if (*lp == '`') line_oops("Special character ` not supported."); else if (*lp == '"') break; else if (*lp == '\\') { if (lp[1] == '\0') getline(); else { if (lp[1] == '`' || lp[1] == '"' || lp[1] == '\\' || lp[1] == '$') arg_put(lp + 1, 1); else arg_put(lp, 2); lp += 2; } } else if (*lp == '$') { const char *q; q = arg_lookup_var(skipping); if (q != NULL) arg_put(q, strlen(q)); } else { const char *p0; p0 = lp; do ++lp; while (*lp != '\0' && *lp != '`' && *lp != '"' && *lp != '\\' && *lp != '$'); arg_put(p0, lp - p0); } } ++lp; force_arg = True; } else if (*lp == '\\') { ++lp; if (*lp == '\0') getline(); else { arg_put(lp, 1); ++lp; } } else if (*lp == '$') { const char *q; q = arg_lookup_var(skipping); if (q != NULL) { while (*q != '\0') { if (*q == ' ' || *q == '\t') { if (args_pos - args > arg_begin_len) { /* Close off current argument */ arg_close(arg_begin_len); arg_begin_len = args_pos - args; } force_arg = False; ++q; } else { const char *q0 = q; do ++q; while (*q != '\0' && *q != ' ' && *q != '\t'); arg_put(q0, q - q0); } } } } else { const char *p0; p0 = lp; do ++lp; while (*lp != '\0' && *lp != ';' && *lp != ' ' && *lp != '\t' && *lp != '|' && *lp != '&' && *lp != '(' && *lp != ')' && *lp != '<' && *lp != '>' && *lp != '`' && *lp != '\'' && *lp != '"' && *lp != '\\' && *lp != '$'); arg_put(p0, lp - p0); } } } while (argc == 0); /* repeat if null command */ if (argv_needs_more) { free(argv); argv = xmalloc(argc_max * sizeof(char *)); } q = args; for (i = 0; i < argc; ++i) { argv[i] = q; q += arg_lengths[i]; } return True; } /* * Store the default values from arch/$ARCH/defconfig */ static void read_defaults_file(const char *name) { char *p; f = fopen(name, "r"); if (f == NULL) { perror(name); exit(1); } filename = name; lineno = 0; while (getline()) { p = line; while (*p == ' ' || *p == '\t') ++p; if (*p == '\0') continue; if (*p == '#') { int len; ++p; if (*p != ' ') continue; ++p; len = strlen(p) - 11; if (len <= 0 || memcmp(p + len, " is not set", 12) != 0) continue; define_var(p, len, "n"); } else { char *p1; char *p2; int len; int len2; if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_')) line_oops("syntax error"); p1 = p; do ++p1; while ((*p1 >= 'A' && *p1 <= 'Z') || (*p1 >= 'a' && *p1 <= 'z') || (*p1 >= '0' && *p1 <= '9') || *p1 == '_'); len = p1 - p; if (len == 0 || *p1 != '=') line_oops("syntax error"); ++p1; if (*p1 == '"') { ++p1; p2 = p1; for (;;) { if (*p2 == '\0') line_oops("missing '\"'"); if (*p2 == '"') break; ++p2; } len2 = p2 - p1; ++p2; } else { p2 = p1; while (*p2 != '\0' && *p2 != '#' && *p2 != ' ' && *p2 != '\t') ++p2; len2 = p2 - p1; } while (*p2 == ' ' || *p2 == '\t') ++p2; if (*p2 != '\0' && *p2 != '#') line_oops("syntax error"); p1[len2] = '\0'; define_var(p, len, p1); } } fclose(f); } /* * Store the information from the qconfig file. * Read it into a linked list, merge-sort it, and then store it into * an array for binary searching. */ struct qcf { const char *name; int len; const char *value; Boolean used; struct qcf *next; }; struct qcf **qcf_array; unsigned qcf_count = 0; /* number of variables defined */ struct qcf * sort_list(struct qcf *head, unsigned count) { struct qcf *p1, *p2; struct qcf *newhead; struct qcf **newheadp; unsigned half; int i; half = count / 2; if (half == 0) return head; p1 = head; for (i = half - 1; i > 0; --i) p1 = p1->next; p2 = sort_list(p1->next, count - half); p1->next = NULL; p1 = sort_list(head, half); /* Merge p1 and p2. */ newheadp = &newhead; for (;;) { i = p1->len - p2->len; if (i == 0) i = memcmp(p1->name, p2->name, p1->len); if (i < 0) { /* if p1 comes first */ *newheadp = p1; newheadp = &p1->next; p1 = p1->next; if (p1 == NULL) { *newheadp = p2; break; } } else { *newheadp = p2; newheadp = &p2->next; p2 = p2->next; if (p2 == NULL) { *newheadp = p1; break; } } } return newhead; } static void read_qconfig_file(const char *name) { struct qcf *head = NULL; struct qcf *q; struct qcf **qp; const char *p; unsigned len; f = fopen(name, "r"); if (f == NULL) { perror(name); exit(1); } filename = name; lineno = 0; lp = line; *line = '\0'; if (!pre_tokenize(False)) return; do { if (argc != 1) line_oops("syntax error"); p = *argv; while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') ++p; len = p - *argv; if (*p != '=' || len == 0) line_oops("syntax error"); q = xmalloc(sizeof *q); q->name = memcpy(xmalloc(len), *argv, len); q->len = len; q->value = xstrdup(p + 1); q->used = False; q->next = head; head = q; ++qcf_count; } while (pre_tokenize(False)); fclose(f); head = sort_list(head, qcf_count); q = head; while (q != NULL) { /* Check for duplicates */ struct qcf *q1; q1 = q->next; if (q1 == NULL) break; if (q1->len == q->len && memcmp(q1->name, q->name, q->len) == 0) { fputs("Warning: variable ", stdout); fwrite(q->name, 1, q->len, stdout); printf(" in %s was defined multiple times\n", path_qc_in); do { q1 = q1->next; --qcf_count; } while (q1 != NULL && q1->len == q->len && memcmp(q1->name, q->name, q->len) == 0); q->next = q1; } q = q1; } qcf_array = qp = xmalloc(qcf_count * sizeof *qcf_array); for (q = head; q != NULL; q = q->next) *qp++ = q; } static const char * qcf_search(const char *name) { int len; int n1, n2, n3; struct qcf *q; int i; if (qcf_count == 0) return NULL; n1 = 0; /* binary search qcf list */ n2 = qcf_count; len = strlen(name); for (;;) { n3 = (n1 + n2) / 2; q = qcf_array[n3]; i = q->len - len; if (i == 0) i = memcmp(q->name, name, len); if (i == 0) { q->used = True; return q->value; } if (n2 - n1 <= 1) return NULL; if (i < 0) n1 = n3; else n2 = n3; } } /* * Command execution. */ typedef void (*cmd_proc)(void); struct cmd { int len; const char *str; int min_args; int max_args; cmd_proc proc; }; static void cmd_bool(void); static void cmd_choice(void); static void cmd_comment(void); static void cmd_define_hex(void); static void cmd_define_int(void); static void cmd_define_string(void); static void cmd_define_tristate(void); static void cmd_dep_bool(void); static void cmd_dep_mbool(void); static void cmd_dep_tristate(void); static void cmd_echo(void); static void cmd_else(void); static void cmd_fi(void); static void cmd_hex(void); static void cmd_if(void); static void cmd_int(void); static void cmd_ignore(void); static void cmd_source(void); static void cmd_string(void); static void cmd_then(void); static void cmd_tristate(void); static void cmd_unset(void); struct cmd cmdtab[] = { /* sorted by length (first), then alpha */ {2, "fi", 0, 0, cmd_fi}, {2, "if", 3, -1, cmd_if}, {3, "hex", 3, 3, cmd_hex}, {3, "int", 3, 5, cmd_int}, {4, "bool", 2, 3, cmd_bool}, {4, "echo", 0, -1, cmd_echo}, {4, "else", 0, 0, cmd_else}, {4, "then", 0, 0, cmd_then}, {5, "unset", 1, -1, cmd_unset}, {6, "choice", 3, 3, cmd_choice}, {6, "source", 1, 1, cmd_source}, {6, "string", 3, 3, cmd_string}, {7, "comment", 1, 1, cmd_comment}, {7, "endmenu", 0, 0, cmd_ignore}, {8, "dep_bool", 2, -1, cmd_dep_bool}, {8, "tristate", 2, 3, cmd_tristate}, {9, "dep_mbool", 2, -1, cmd_dep_mbool}, {10, "define_hex", 2, 2, cmd_define_hex}, {10, "define_int", 2, 2, cmd_define_int}, {11, "define_bool", 2, 2, cmd_define_tristate}, {12, "dep_tristate", 2, -1, cmd_dep_tristate}, {13, "define_string", 2, 2, cmd_define_string}, {13, "mainmenu_name", 1, 1, cmd_ignore}, {15, "define_tristate", 2, 2, cmd_define_tristate}, {15, "mainmenu_option", 1, 1, cmd_ignore}, }; static struct cmd * tokenize(Boolean skipping) { struct cmd *cmdp; int n1, n2; int len; if (!pre_tokenize(skipping)) /* if EOF */ return NULL; n1 = 0; /* binary search command table */ n2 = sizeof(cmdtab) / sizeof(*cmdtab); len = strlen(argv[0]); for (;;) { int n3; int i; n3 = (n1 + n2) / 2; cmdp = cmdtab + n3; i = cmdp->len - len; if (i == 0) i = strcmp(cmdp->str, argv[0]); if (i == 0) break; if (n2 - n1 <= 1) line_oops("invalid command \"%s\".", argv[0]); if (i < 0) n1 = n3; else n2 = n3; } if (!skipping) { if (argc <= cmdp->min_args) line_oops("too few arguments"); if (argc - 1 > cmdp->max_args && cmdp->max_args >= 0) line_oops("too many arguments"); } return cmdp; } static void do_source(const char *name) { struct cmd *cmdp; f = fopen(name, "r"); if (f == NULL) { perror(name); clean_exit(); } filename = name; lineno = 0; lp = line; *line = '\0'; for (;;) { cmdp = tokenize(False); if (cmdp == NULL) break; cmdp->proc(); } fclose(f); } /* * Commands */ void cmd_comment(void) { fprintf(out_file, "*\n* %s\n*\n", argv[1]); fprintf(config_file, "\n#\n# %s\n#\n", argv[1]); fprintf(header_file, "\n/*\n * %s\n */\n", argv[1]); } void cmd_echo(void) { int i; if (argc <= 1) return; for (i = 1;;) { fputs(argv[i], stdout); if (++i >= argc) break; putchar(' '); } putchar('\n'); } /* * unset {variable ...} */ void cmd_unset(void) { int i; for (i = 1; i < argc; ++i) define_var(argv[i], strlen(argv[i]), ""); } static void define_tristate(const char *var, const char *value) { switch (*value) { case 'y': fprintf(config_file, "%s=y\n", var); fprintf(header_file, "#define %s 1\n", var); break; case 'm': fprintf(config_file, "%s=m\n", var); fprintf(header_file, "#undef %s\n", var); fprintf(header_file, "#define %s_MODULE 1\n", var); break; case 'n': fprintf(config_file, "# %s is not set\n", var); fprintf(header_file, "#undef %s\n", var); break; } define_var(var, strlen(var), value); } void cmd_define_tristate(void) { define_tristate(argv[1], argv[2]); } /* bool question define */ void cmd_bool(void) { const char *new = ""; const char *def; const char *ans, *ans2; const char *defprompt = ""; def = lookup_var(argv[2], strlen(argv[2])); if (def == NULL) { def = "n"; new = "(NEW) "; } switch (*def) { case 'y': case 'm': defprompt = "Y/n/?"; break; case 'n': defprompt = "N/y/?"; break; } ans = ans2 = qcf_search(argv[2]); if (ans == NULL) { ans = ""; ans2 = def; } fprintf(out_file, "%s (%s) [%s] %s%s\n", argv[1], argv[2], defprompt, new, ans); switch (*ans2) { case 'y': case 'Y': case 'm': case 'M': define_tristate(argv[2], "y"); break; case 'n': case 'N': define_tristate(argv[2], "n"); break; default: line_oops("Invalid answer \"%s\" given for %s", ans2, argv[2]); } } /* tristate question define */ void cmd_tristate(void) { const char *new = ""; const char *def; const char *ans, *ans2; const char *defprompt = ""; if (!config_modules()) { cmd_bool(); return; } def = lookup_var(argv[2], strlen(argv[2])); if (def == NULL) { def = "n"; new = "(NEW) "; } switch (*def) { case 'y': defprompt = "Y/m/n/?"; break; case 'm': defprompt = "M/n/y/?"; break; case 'n': defprompt = "N/y/m/?"; break; } ans = ans2 = qcf_search(argv[2]); if (ans == NULL) { ans = ""; ans2 = def; } fprintf(out_file, "%s (%s) [%s] %s%s\n", argv[1], argv[2], defprompt, new, ans); switch (*ans2) { case 'y': case 'Y': define_tristate(argv[2], "y"); break; case 'm': case 'M': define_tristate(argv[2], "m"); break; case 'n': case 'N': define_tristate(argv[2], "n"); break; default: line_oops("Invalid answer \"%s\" given for %s", ans2, argv[2]); } } /* dep_tristate question define {answer_dependencies} */ void cmd_dep_tristate(void) { Boolean need_module = False; int i; const char *new = ""; const char *def; const char *ans, *ans2; const char *defprompt = ""; for (i = 3; i < argc; ++i) switch (*argv[i]) { case 'n': define_tristate(argv[2], "n"); return; case 'm': need_module = True; break; } if (!need_module) { cmd_tristate(); return; } if (!config_modules()) { cmd_bool(); return; } def = lookup_var(argv[2], strlen(argv[2])); if (def == NULL) { def = "n"; new = "(NEW) "; } switch (*def) { case 'y': case 'm': defprompt = "M/n/?"; break; case 'n': defprompt = "N/m/?"; break; } ans = ans2 = qcf_search(argv[2]); if (ans == NULL) { ans = ""; ans2 = def; } fprintf(out_file, "%s (%s) [%s] %s%s\n", argv[1], argv[2], defprompt, new, ans); switch (*ans2) { case 'n': case 'N': define_tristate(argv[2], "n"); break; case 'y': case 'Y': case 'm': case 'M': define_tristate(argv[2], "m"); break; default: line_oops("Invalid answer \"%s\" given for %s", ans2, argv[2]); } } /* dep_bool question define {answer_dependencies} */ void cmd_dep_bool(void) { int i; for (i = 3; i < argc; ++i) if (*argv[i] == 'n' || *argv[i] == 'm') { define_tristate(argv[2], "n"); return; } cmd_bool(); } /* dep_mbool question define {answer_dependencies} */ void cmd_dep_mbool(void) { int i; for (i = 3; i < argc; ++i) if (*argv[i] == 'n') { define_tristate(argv[2], "n"); return; } else if (*argv[i] == 'm') { /* change the default */ define_var(argv[2], strlen(argv[2]), "y"); } cmd_bool(); } static void define_int(const char *var, const char *value) { const char *p; p = value; if (*p == '-') ++p; if (*p == '0' && (p[1] == 'x' || p[1] == 'X')) { p+=2; do { if ((*p < '0' || *p > '9') && (*p < 'a' || *p > 'f') && (*p < 'A' && *p > 'F')) line_oops("Value for %s is not an integer", var); ++p; } while (*p != '\0'); } else { do { if (*p < '0' || *p > '9') line_oops("Value for %s is not an integer", var); ++p; } while (*p != '\0'); } fprintf(config_file, "%s=%s\n", var, value); fprintf(header_file, "#define %s (%s)\n", var, value); define_var(var, strlen(var), value); } /* define_int var value */ void cmd_define_int(void) { define_int(argv[1], argv[2]); } /* int question define default [min [max]] */ void cmd_int(void) { const char *new = ""; const char *def; const char *ans, *ans2; int min, max; int i; def = lookup_var(argv[2], strlen(argv[2])); if (def == NULL) { def = argv[3]; new = "(NEW) "; } min = -10000000; max = 10000000; if (argc > 4) { min = atoi(argv[4]); if (argc > 5) max = atoi(argv[5]); } ans = ans2 = qcf_search(argv[2]); if (ans == NULL) { ans = ""; ans2 = def; } fprintf(out_file, "%s (%s) [%s] %s%s\n", argv[1], argv[2], def, new, ans); i = atoi(ans2); if (i < min || i > max) line_oops("Value for %s is out of range", argv[2]); define_int(argv[2], ans2); } static void define_hex(const char *var, const char *value) { const char *p; p = value; if (*p == '0') ++p; if (*p == 'x' || *p == 'X') value = p; p = value; do { if (!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F'))) line_oops("Value for %s is not a hexadecimal integer", var); ++p; } while (*p != '\0'); fprintf(config_file, "%s=%s\n", var, value); fprintf(header_file, "#define %s 0x%s\n", var, value); define_var(var, strlen(var), value); } /* define_hex var value */ void cmd_define_hex(void) { define_hex(argv[1], argv[2]); } /* hex question define default */ void cmd_hex(void) { const char *new = ""; const char *def; const char *ans, *ans2; const char *p; def = lookup_var(argv[2], strlen(argv[2])); if (def == NULL) { def = argv[3]; new = "(NEW) "; } ans = ans2 = qcf_search(argv[2]); if (ans == NULL) { ans = ""; ans2 = def; } p = def; if (*p == '0') ++p; if (*p == 'x' || *p == 'X') def = p; fprintf(out_file, "%s (%s) [%s] %s%s\n", argv[1], argv[2], def, new, ans); define_hex(argv[2], ans2); } static void define_string(const char *var, const char *value) { fprintf(config_file, "%s=\"%s\"\n", var, value); fprintf(header_file, "#define %s \"%s\"\n", var, value); define_var(var, strlen(var), value); } /* define_string var value */ void cmd_define_string(void) { define_string(argv[1], argv[2]); } /* string question define default */ void cmd_string(void) { const char *new = ""; const char *def; const char *ans, *ans2; def = lookup_var(argv[2], strlen(argv[2])); if (def == NULL) { def = argv[3]; new = "(NEW) "; } ans = ans2 = qcf_search(argv[2]); if (ans == NULL) { ans = ""; ans2 = def; } fprintf(out_file, "%s (%s) [%s] %s%s\n", argv[1], argv[2], def, new, ans); define_string(argv[2], ans2); } /* choice question choice-list default */ void cmd_choice(void) { struct choices { struct choices *next; const char *keys; const char *var; }; struct choices *ch_head; struct choices **ch_end; struct choices *chp; const char *new = ""; const char *def; struct choices *def_rec; const char *ans; struct choices *ans_rec; char *p; /* Parse choice list */ ch_end = &ch_head; p = argv[2]; for (;;) { while (*p == ' ' || *p == '\t') ++p; if (*p == '\0') break; chp = xmalloc(sizeof *chp); *ch_end = chp; ch_end = &chp->next; chp->keys = p; while (*p != '\0' && *p != ' ' && *p != '\t') ++p; if (*p == '\0') line_oops("Odd number of entries in choice-list"); *p = '\0'; do ++p; while (*p == ' ' || *p == '\t'); if (*p == '\0') line_oops("Odd number of entries in choice-list"); chp->var = p; while (*p != '\0' && *p != ' ' && *p != '\t') ++p; if (*p == '\0') break; *p++ = '\0'; } *ch_end = NULL; if (ch_head == NULL) line_oops("Empty choice list"); /* Determine default and answer (if any) */ def_rec = ans_rec = NULL; for (chp = ch_head; chp != NULL; chp = chp->next) { def = lookup_var(chp->var, strlen(chp->var)); if (def != NULL && *def == 'y') def_rec = chp; ans = qcf_search(chp->var); if (ans != NULL && *ans == 'y') ans_rec = chp; } if (def_rec != NULL) def = def_rec->keys; else { def = argv[3]; new = "(NEW) "; } fprintf(out_file, "%s (", argv[1]); for (chp = ch_head;;) { fputs(chp->keys, out_file); chp = chp->next; if (chp == NULL) break; fputs(", ", out_file); } fprintf(out_file, ") [%s] %s%s\n", def, new, ans_rec != NULL ? ans_rec->keys : ""); if (ans_rec == NULL) { ans_rec = def_rec; if (ans_rec == NULL) { int ans_len = strlen(def); for (chp = ch_head; chp != NULL; chp = chp->next) { const char *p1; for (p1 = chp->keys;;) { const char *p2 = strchr(p1, '/'); if (p2 == NULL) p2 = p1 + strlen(p1); if (p2 - p1 >= ans_len && memcmp(def, p1, ans_len) == 0) { if (ans_rec != NULL) line_oops("Default answer given for choice is ambiguous"); ans_rec = chp; } p1 = p2; if (*p1 == '\0') break; ++p1; } } if (ans_rec == NULL) line_oops("Default answer given for choice does not match anything"); } } for (chp = ch_head; chp != NULL; chp = chp->next) if (chp == ans_rec) { define_tristate(chp->var, "y"); fprintf(out_file, " defined %s\n", chp->var); } else define_tristate(chp->var, "n"); } /* source path */ void cmd_source(void) { FILE *save_f; const char *save_filename; int save_lineno; if (*lp == ';') line_oops("source command cannot end with ';'"); save_f = f; save_filename = filename; save_lineno = lineno; do_source(filename = xstrdup(argv[1])); free((char *) filename); f = save_f; filename = save_filename; lineno = save_lineno; lp = line; *line = '\0'; } void cmd_then(void) { line_oops("misplaced \"then\""); } void cmd_else(void) { line_oops("misplaced \"else\""); } void cmd_fi(void) { line_oops("misplaced \"fi\""); } Boolean sub_condition(char ***argpp, char **arg_end) { char **argp = *argpp; if (strcmp(*argp, "!") == 0) { if ((*argpp = argp + 1) <= arg_end) return !sub_condition(argpp, arg_end); } else if ((*argpp = argp + 3) <= arg_end) { if (strcmp(argp[1], "=") == 0) return strcmp(argp[0], argp[2]) == 0; else if (strcmp(argp[1], "!=") == 0) return strcmp(argp[0], argp[2]) != 0; } line_oops("conditional syntax error"); } Boolean and_list(char ***argpp, char **arg_end) { char **argp = *argpp; Boolean value; value = sub_condition(&argp, arg_end); while (argp < arg_end && strcmp(*argp, "-a") == 0) { ++argp; value &= sub_condition(&argp, arg_end); } *argpp = argp; return value; } static struct cmd * if_skip(void) { struct cmd *cmdp; int level = 0; for (;;) { cmdp = tokenize(True); if (cmdp == NULL) return cmdp; if (cmdp->proc == cmd_if) ++level; else if (cmdp->proc == cmd_else) { if (level == 0) return cmdp; } else if (cmdp->proc == cmd_fi) { if (level == 0) return cmdp; else --level; } } } void cmd_if(void) { Boolean value; char **argp; char **arg_end; struct cmd *cmdp; argp = argv + 1; if (strcmp(*argp, "[") != 0) line_oops("conditional expression must begin with \"[\""); arg_end = argv + (argc - 1); if (strcmp(*arg_end, "]") != 0) line_oops("conditional expression must end with \"]\""); ++argp; value = and_list(&argp, arg_end); while (argp < arg_end) { if (strcmp(*argp, "-o") != 0) line_oops("conditional syntax error"); ++argp; value |= and_list(&argp, arg_end); } cmdp = tokenize(False); if (cmdp == NULL || cmdp->proc != cmd_then) line_oops("\"then\" expected"); if (value) { for (;;) { cmdp = tokenize(False); if (cmdp == NULL || cmdp->proc == cmd_fi) break; if (cmdp->proc == cmd_else) { cmdp = if_skip(); if (cmdp != NULL && cmdp->proc == cmd_else) cmd_else(); /* misplaced */ break; } cmdp->proc(); } } else { cmdp = if_skip(); if (cmdp != NULL && cmdp->proc == cmd_else) { for (;;) { cmdp = tokenize(False); if (cmdp == NULL || cmdp->proc == cmd_fi) break; cmdp->proc(); } } } if (cmdp == NULL) line_oops("File ended before \"if\" was complete"); } void cmd_ignore(void) { } /* * File commands. */ static void open_writable_file(const char *path, const char *tmp_repl, const char **tmp_path_out, FILE **file_out) { FILE *file; struct stat statbuf; if (path[0] == '-' && path[1] == '\0') file = stdout; else { const char *path2; /* * If path is /dev/null, we don't want to write a temporary regular * file and then move it into place! */ if (stat(path, &statbuf) == 0 && !S_ISREG(statbuf.st_mode)) path2 = path; else { const char *p = strrchr(path, '/'); if (p == NULL) *tmp_path_out = path2 = tmp_repl; else { char *tmp_path; int len1 = p - path + 1; int len2 = strlen(tmp_repl) + 1; *tmp_path_out = path2 = tmp_path = xmalloc(len1 + len2); memcpy(tmp_path, path, len1); memcpy(tmp_path + len1, tmp_repl, len2); } } file = fopen(path2, "w"); if (file == NULL) { perror(path2); clean_exit(); } } *file_out = file; } static const char * oldname(const char *path) { const char *p; int len; char *q; p = strrchr(path, '/'); if (p == NULL) p = path; p = strrchr(p, '.'); if (p == NULL) len = strlen(path); else len = p - path; q = xmalloc(len + 5); memcpy(q, path, len); memcpy(q + len, ".old", 5); return q; } static Boolean file_exists(const char *path) { struct stat buf; return stat(path, &buf) == 0; } static void move(const char *path1, const char *path2) { if (link(path1, path2) != 0 || unlink(path1) != 0) { fprintf(stderr, "%s --> ", path1); perror(path2); } } /* * Main program. */ int main(int main_argc, char **main_argv) { char **argp; struct qcf *q; struct var *var_config_modversions; argp = main_argv; while (++argp < main_argv + main_argc && (*argp)[0] == '-') { const struct option *opt_ptr; const struct option *opt; char *arg = *argp + 1; if (*arg == '\0') --arg; /* this will flag an error later */ if (*arg != '-') { /* if short argument */ opt = options; for (;;) { if (*arg == opt->shortname) break; if (++opt >= options + NUMBER(options)) opt_oops("invalid option -- %c", *arg); } if (opt->addr != NULL) { ++arg; if (*arg == '\0') { if (++argp >= main_argv + main_argc) opt_oops("option requires an argument -- %c", arg[-1]); arg = *argp; } } else { if (arg[1] != '\0') opt_oops("invalid number of bytes in option `%s'", arg - 1); } } else { /* long argument */ int len; char *arg1; ++arg; len = strlen(arg); arg1 = memchr(arg, '=', len); if (arg1 != NULL) { len = arg1 - arg; ++arg1; } opt = NULL; for (opt_ptr = options; opt_ptr < options + NUMBER(options); ++opt_ptr) if (memcmp(arg, opt_ptr->longname, len) == 0) { if (opt != NULL) opt_oops("option `%s' is ambiguous.", arg - 2); opt = opt_ptr; } if (opt == NULL) opt_oops("unrecognized option `%s'", arg - 2); if (opt->addr != NULL) { if (arg1 == NULL) { if (++argp >= main_argv + main_argc) opt_oops("option `--%s' requires an argument.", opt->longname); arg1 = *argp; } } else { if (arg1 != NULL) opt_oops("option `--%s' doesn't allow an argument.", opt->longname); } arg = arg1; } /* end long argument */ if (opt->addr != NULL) *((char **) opt->addr) = arg; switch (opt->shortname) { case 'v': puts("qconfig " VERSION); return 0; case '-': /* --help */ puts("\ Usage: qconfig [options]\n\ Create configuration files for Linux kernel.\n\ \n\ -i PATH, --input=PATH Use PATH as the input file instead of qconfig.in\n\ -o PATH, --output=PATH Send pseudo-output file to PATH instead of qconfig.out\n\ -c PATH, --config=PATH Create a file PATH instead of .config\n\ -h PATH, --headers=PATH Use PATH instead of include/linux/autoconf.h\n\ -d PATH, --defaults=PATH Get default values from PATH instead of\n\ arch/$ARCH/defconfig\n\ -s PATH, --script=PATH Use PATH as the initial configure script instead of\n\ arch/$ARCH/config.in\n\ -a ARCH, --arch=ARCH Use ARCH as the architecture\n\ -t VAR, --trace=VAR Trace variable VAR\n\ -n, --nominal Same as -c /dev/null -h /dev/null -o /dev/null\n\ -v, --version Print qconfig version number and exit\n\ --help Print this help message and exit\n"); return 0; case 'n': /* --nominal */ path_dot_cfg = path_autoconf = path_qc_out = "/dev/null"; break; } } if (argp != main_argv + main_argc) opt_oops("Invalid option `%s'", *argp); if (arch == NULL) { arch = getenv("ARCH"); if (arch == NULL) arch = ARCH; } if (trace_var != NULL) trace_var_len = strlen(trace_var); if (path_defaults == NULL) { path_defaults = xmalloc(16 + strlen(arch)); sprintf(path_defaults, "arch/%s/defconfig", arch); } if (path_config_in == NULL) { path_config_in = xmalloc(16 + strlen(arch)); sprintf(path_config_in, "arch/%s/config.in", arch); } line = xmalloc(line_max = INCR); args = xmalloc(args_max = INCR); args_end = args + args_max; arg_lengths = xmalloc((argc_max = ARGC_INCR) * sizeof (int)); argv = xmalloc(argc_max * sizeof(char *)); var_config_modules = find_var("CONFIG_MODULES", 14); var_config_modules->value = xstrdup(""); define_var("ARCH", 4, arch); read_defaults_file(path_defaults); read_qconfig_file(path_qc_in); open_writable_file(path_qc_out, ".tmpout", &path_tmp_out, &out_file); fprintf(out_file, "#\n# Using defaults found in %s\n#\n", path_defaults); open_writable_file(path_dot_cfg, ".tmpconfig", &path_tmp_config, &config_file); fputs("#\n# Automatically generated make config: don't edit\n#\n", config_file); open_writable_file(path_autoconf, ".tmpconfig.h", &path_tmp_header, &header_file); fputs("/*\n * Automatically generated C config: don't edit\n */\n", header_file); fputs("#define AUTOCONF_INCLUDED 1\n", header_file); do_source(path_config_in); if (path_tmp_out != NULL) { fclose(out_file); if (file_exists(path_qc_out)) { const char *oldpath = oldname(path_qc_out); unlink(oldpath); move(path_qc_out, oldpath); } move(path_tmp_out, path_qc_out); } if (path_tmp_config != NULL) { fclose(config_file); if (file_exists(path_dot_cfg)) { const char *oldpath = oldname(path_dot_cfg); unlink(oldpath); move(path_dot_cfg, oldpath); } move(path_tmp_config, path_dot_cfg); } if (path_tmp_header != NULL) { fclose(header_file); unlink(path_autoconf); move(path_tmp_header, path_autoconf); } if (qcf_array != NULL) for (q = *qcf_array; q != NULL; q = q->next) if (!q->used) { fputs("Note: variable ", stdout); fwrite(q->name, 1, q->len, stdout); printf(" in %s was not used.\n", path_qc_in); } puts( "\n*** Check the top-level Makefile for additional configuration."); var_config_modversions = find_var("CONFIG_MODVERSIONS", 18); if (!file_exists(".hdepend") || (var_config_modversions != NULL && *var_config_modversions->value == 'y')) puts("*** Next, you must run 'make dep'.\n"); else puts("*** Next, you may run 'make zImage', 'make zdisk', or 'make zlilo'.\n"); return 0; }