/* * Proof of concept CML1 constraint generator * (c) 2001, Red Hat Inc * * TODO: * For each CHOICE set insert a constraint set of * entry NOR { other options } * Allow -o foo -a bar (put the or/and entry in the condition) * * * 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. * * 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-1307 US * */ #include #include #include #include /* * We chain these together to list conditions applied to a rule block * for an if or a subset rule */ struct condition { struct condition *next; char *name1; int op; #define OP_EQ 1 #define OP_NE 2 char *name2; }; /* * Each actual ruleset we must apply is a constraint. They are linked * by ->next for each constraint in this ruleset and chain to walk to * the next ruleset. */ struct constraint { int type; struct condition *link; struct constraint *next; /* For stacks */ struct constraint *chain; /* For gluing multiple */ }; #define CONSTRAINT_GROUP_OR 1 #define CONSTRAINT_GROUP_AND 2 /* * For these two cases the chain holds a list of conditions that * are applied either logical and or logical or. The conditions may * contain unparsed strings at this point */ #define CONSTRAINT_GROUP_NOTOR 3 #define CONSTRAINT_GROUP_NOTAND 4 /* * As above but generated by inversions. This allows us to use the same * condition chain and just copy the constraint. */ #define CONSTRAINT_SUBSET 5 /* * The condition group is a list of entries whose value bounds the * range of the target. */ /* * Each CML1 variable we discover we add to this. The constraints * is a chain of discovered constraints for this variable. Ctail holds * the last constraint so that we can apply more nodes to it correctly * when building an AND. */ struct variable { char *tag; int type; struct variable *next; struct constraint *constraints; struct constraint *ctail; }; /* * Used to remember where in the constraint stack we are as we walk * through layers of if conditionals. */ struct ifstack { struct constraint *top; /* Stack top below our entry */ struct constraint *head, *tail; struct ifstack *next; }; static struct variable *varlist; static struct constraint *constraint_stack; static struct ifstack *if_stack; /* * Add a constraint to the if stack. The code is designed to allow us * to add a group of constraints here in case we need it in future */ static void push_ifconstraint(struct constraint *c) { struct ifstack *i = (struct ifstack *)malloc(sizeof(struct ifstack)); if(i==NULL) { fprintf(stderr, "Out of memory.\n"); exit(1); } i->head = c; /* Push us on the ifstack, add our constraint set */ i->top = constraint_stack; c->next = constraint_stack; i->next = if_stack; constraint_stack = i->head; if_stack = i; } /* * Rip the head off the constraint stack, used for fi */ static int pop_ifconstraint(void) { if(if_stack) { constraint_stack = if_stack->top; if_stack = if_stack->next; return 0; } return -1; } /* * Change the sense of the top of the if stack. Used for else */ static void invert_top_ifconstraint(void) { if(if_stack) { if(if_stack->head->type <= 2) if_stack->head->type += 2; else if_stack->head->type -= 2; } } /* * We have discovered a new declaration of a variable. We must attach * the current if stack to it, and invert the order of the stack * as we go for neatness. We have to copy here but we don't have to * copy the condition block */ static struct constraint *attach_constraint_stack(struct variable *v) { struct constraint *c, *n, *p, *r; /* * We want most global rule first. We also have to copy here * as we will attach new subtrees */ c = constraint_stack; p = NULL; r = NULL; while(c) { n = malloc(sizeof(struct constraint)); if(n==NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } *n = *c; /* * Link copy into our tree, and invert the order as we go */ n->next = p; n->chain = NULL; p = n; if(r==NULL) r = n; c=c->next; } /* * p is now the top of the chain (n is not always defined.. ) */ if(p==NULL) { v->ctail = NULL; v->constraints = NULL; return NULL; } /* * Add the ruleset to the variable */ p->chain = v->constraints; v->constraints = p; v->ctail = r; return p; } /* * Given a name find the variable */ static struct variable *find_variable(char *tag) { struct variable *v = varlist; while(v != NULL) { if(strcmp(v->tag,tag)==0) return v; v = v->next; } return NULL; } /* * Given a name add a variable, or if it exists add another rule chain * to it */ static void add_variable(char *tag, int type) { struct variable *v; v = find_variable(tag); if(v==NULL) { v = malloc(sizeof(*v)+strlen(tag)+1); v->tag = (char *)(v+1); strcpy(v->tag, tag); v->type = type; v->next = varlist; v->constraints = NULL; v->ctail = NULL; attach_constraint_stack(v); varlist = v; } else { /* * A second way to set the variable exists, both must * be considered */ attach_constraint_stack(v); } } /* * Add a condition to a constraint rule */ static struct condition *add_condition(struct constraint *c, char *name1, int op, char *name2) { struct condition *cn = malloc(sizeof(*cn)+strlen(name1)+strlen(name2)+2); if(cn==NULL) { fprintf(stderr, "Out of memory.\n"); exit(1); } cn->name1 = (char *)(cn+1); cn->name2 = cn->name1+strlen(name1)+1; strcpy(cn->name1, name1); strcpy(cn->name2, name2); cn->op = op; cn->next = c->link; c->link = cn; } /* * Add a constraint to a variable. It is added to the tail of the * current constraint set. */ static struct constraint *add_constraint(char *vname, int type) { struct variable *v; struct constraint *c = malloc(sizeof(*c)); v = find_variable(vname); if(v==NULL) { fprintf(stderr, "Undeclared variable '%s' used.\n", vname); return NULL; } c->type = type; c->link = NULL; c->next = NULL; c->chain = NULL; /* * Add the rule to the end of the current rule stack */ if(v->ctail) v->ctail->next = c; else v->constraints = c; v->ctail = c; } /* * Parser (this is taken from Milliways BBS). Either Alan Cox or * Justin Mitchell wrote this bit but I don't remember whom..) */ #define MAX_ARGC 256 static void shuffle_up(char *bp) { /* The strlen is right its the length of bp, which is the length of bp+1 including the NUL */ memmove(bp,bp+1,strlen(bp)); } static int keywords(char *line, char **argvp[]) { char *ptr=line; int n=0; int quoted=0; int slashed=0; int squoted=0; static char *argv[MAX_ARGC]; while(n0) { if(*argv[0]==']') break; if(argc<3) { parse_error("Expected condition"); goto bad; } if(parse_triple(name, errbuf, errline, c, &argc, &argv)==-1) break; if(*argv[0]==']') break; if(argc==0) { parse_error("']' expected"); return NULL; } if(strcmp(argv[0], "-o")==0) { if(and==1) { parse_error("Mixing -o and -a is not permitted"); goto bad; } and=0; } else if(strcmp(argv[0], "-a")==0) { if(and==0) { parse_error("Mixing -o and -a is not permitted"); goto bad; } and=1; } else { parse_error("'];' '-o' or '-a' expected"); goto bad; } argc--; argv++; } done: if(and==1 || and==-1) c->type = CONSTRAINT_GROUP_AND; else c->type = CONSTRAINT_GROUP_OR; if(invert) c->type += 2; return c; bad: c->link = NULL; goto done; } static void parse_file(char *name) { char buf[2048]; char errbuf[2048]; FILE *fp = fopen(name, "r"); char *p; int errline = 0; struct constraint *c; if(fp==NULL) { perror(name); return; } while(multigets(buf, 2048, fp)) { char **argv; int argc; p = buf; errline++; strcpy(errbuf,buf); argc = keywords(buf, &argv); if(argc==0) continue; if(*argv[0]=='#') continue; if(strcmp(argv[0], "comment")==0) continue; if(strcmp(argv[0], "mainmenu_option")==0) continue; if(strcmp(argv[0], "mainmenu_name")==0) continue; if(strcmp(argv[0], "endmenu")==0) continue; if(strcmp(argv[0], "source")==0) { if(argc!=2) { parse_error("source requires file name"); continue; } parse_file(argv[1]); continue; } if(strcmp(argv[0], "bool")==0 || strcmp(argv[0], "tristate")==0) { int v = vartype(argv[0]); if(argc!=3) parse_error("prompt and name expected"); else add_variable(argv[2], v); continue; } if(vartype(argv[0])!=-1) { int v = vartype(argv[0]); if(argc!=4) parse_error("prompt, name and default expected"); else add_variable(argv[2], v); continue; } if(strncmp(argv[0], "dep_", 4)==0) { int v=vartype(argv[0] + 4); int i; if(argc<4) parse_error("prompt, name and symbols expected"); else { add_variable(argv[2], v); c = add_constraint(argv[2], CONSTRAINT_SUBSET); for(i=3;argv[i];i++) add_condition(c, argv[i], 0, ""); } continue; } if(strncmp(argv[0], "define_", 7)==0) { int v=vartype(argv[0] + 7); if(argc!=3) parse_error("name, value and symbol expected"); else { add_variable(argv[1], v); c = add_constraint(argv[1], CONSTRAINT_SUBSET); add_condition(c, argv[2], 0, ""); } continue; } if(strcmp(argv[0], "if")==0) { struct constraint *c = parse_if_constraint(name, errbuf, errline, argc, argv); if(c) push_ifconstraint(c); continue; } if(strcmp(argv[0], "else")==0) { invert_top_ifconstraint(); continue; } if(strcmp(argv[0], "fi")==0) { if(pop_ifconstraint()<0) parse_error("unbalanced 'fi'"); continue; } if(strcmp(argv[0], "then")==0) { /* We hit a then on a new line, thats fine.. skip */ continue; } if(strcmp(argv[0], "unset")==0) { /* This isnt an official CML op but its abused by the Alpha port */ if(argc<2) parse_error("unset expects one variable name"); continue; } if(strcmp(argv[0], "choice")==0) { // add_variable(argv[1], 5); continue; } parse_error("unknown command"); } fclose(fp); } static void dump_condition(int type, struct condition *cn) { int doall = 1; if(type == CONSTRAINT_SUBSET) doall = 0; printf(" %s", cn->name1); if(doall) { char *opstr="???"; if(cn->op==OP_EQ) opstr="="; if(cn->op==OP_NE) opstr="!="; printf("%s %s", opstr, cn->name2); } printf("\n"); } static void dump_constraint(struct constraint *c) { struct condition *cp; printf("{\n"); while(c!=NULL) { cp=c->link; if(cp!=NULL) { printf(" {\n"); switch(c->type) { case CONSTRAINT_GROUP_OR: printf(" One of:\n"); break; case CONSTRAINT_GROUP_AND: printf(" All of:\n"); break; case CONSTRAINT_GROUP_NOTOR: printf(" None of:\n"); break; case CONSTRAINT_GROUP_NOTAND: printf(" Not all of:\n"); break; case CONSTRAINT_SUBSET: printf(" Subset of:\n"); break; default: printf("BAD RULE!!\n"); } while(cp!=NULL) { dump_condition(c->type, cp); cp = cp->next; } printf(" }\n"); } c=c->next; if(c) printf(" AND\n"); } printf("}\n"); } static void dump(void) { struct variable *v = varlist; while(v!=NULL) { struct constraint *c; printf("Variable %s: \n", v->tag); c=v->constraints; while(c!=NULL) { dump_constraint(c); c=c->chain; if(c) printf("OR\n"); } v=v->next; } } int main(int argc, char *argv[]) { if(chdir("/usr/src/LINUS/LINUX2.4/linux.ac")==-1) perror("chdir"); if(argv[1]) { parse_file(argv[1]); } else { fprintf(stderr, "%s [file]\n", argv[0]); exit(1); } dump(); return 0; }