tkparse.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. /* parser config.in
  2. * $Id: tkparse.c 2281 2010-10-15 14:21:13Z smasson $
  3. *
  4. * This software is governed by the CeCILL license
  5. * See IOIPSL/IOIPSL_License_CeCILL.txt
  6. *
  7. * Version 1.0
  8. * Eric Youngdale
  9. * 10/95
  10. *
  11. * The general idea here is that we want to parse a config.in file and
  12. * from this, we generate a wish script which gives us effectively the
  13. * same functionality that the original config.in script provided.
  14. *
  15. * This task is split roughly into 3 parts. The first parse is the parse
  16. * of the input file itself. The second part is where we analyze the
  17. * #ifdef clauses, and attach a linked list of tokens to each of the
  18. * menu items. In this way, each menu item has a complete list of
  19. * dependencies that are used to enable/disable the options.
  20. * The third part is to take the configuration database we have build,
  21. * and build the actual wish script.
  22. *
  23. * This file contains the code to do the first parse of config.in.
  24. */
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include "tkparse.h"
  29. struct kconfig * config = NULL;
  30. struct kconfig * clast = NULL;
  31. struct kconfig * koption = NULL;
  32. static int lineno = 0;
  33. static int menus_seen = 0;
  34. static char * current_file = NULL;
  35. static int do_source(char * filename);
  36. static char * get_string(char *pnt, char ** labl);
  37. static int choose_number = 0;
  38. /*
  39. * Simple function just to skip over spaces and tabs in config.in.
  40. */
  41. static char * skip_whitespace(char * pnt)
  42. {
  43. while( *pnt && (*pnt == ' ' || *pnt == '\t')) pnt++;
  44. return pnt;
  45. }
  46. /*
  47. * This function parses a conditional from a config.in (i.e. from an ifdef)
  48. * and generates a linked list of tokens that describes the conditional.
  49. */
  50. static struct condition * parse_if(char * pnt)
  51. {
  52. char * opnt;
  53. struct condition *list;
  54. struct condition *last;
  55. struct condition *cpnt;
  56. char varname[64];
  57. char * pnt1;
  58. opnt = pnt;
  59. /*
  60. * We need to find the various tokens, and build the linked list.
  61. */
  62. pnt = skip_whitespace(pnt);
  63. if( *pnt != '[' ) return NULL;
  64. pnt++;
  65. pnt = skip_whitespace(pnt);
  66. list = last = NULL;
  67. while(*pnt && *pnt != ']') {
  68. pnt = skip_whitespace(pnt);
  69. if(*pnt== '\0' || *pnt == ']') break;
  70. /*
  71. * Allocate memory for the token we are about to parse, and insert
  72. * it in the linked list.
  73. */
  74. cpnt = (struct condition *) malloc(sizeof(struct condition));
  75. memset(cpnt, 0, sizeof(struct condition));
  76. if( last == NULL )
  77. {
  78. list = last = cpnt;
  79. }
  80. else
  81. {
  82. last->next = cpnt;
  83. last = cpnt;
  84. }
  85. /*
  86. * Determine what type of operation this token represents.
  87. */
  88. if( *pnt == '-' && pnt[1] == 'a' )
  89. {
  90. cpnt->op = op_and;
  91. pnt += 2;
  92. continue;
  93. }
  94. if( *pnt == '-' && pnt[1] == 'o' )
  95. {
  96. cpnt->op = op_or;
  97. pnt += 2;
  98. continue;
  99. }
  100. if( *pnt == '!' && pnt[1] == '=' )
  101. {
  102. cpnt->op = op_neq;
  103. pnt += 2;
  104. continue;
  105. }
  106. if( *pnt == '=')
  107. {
  108. cpnt->op = op_eq;
  109. pnt += 1;
  110. continue;
  111. }
  112. if( *pnt == '!')
  113. {
  114. cpnt->op = op_bang;
  115. pnt += 1;
  116. continue;
  117. }
  118. if( *pnt != '"' ) goto error; /* This cannot be right. */
  119. pnt++;
  120. if( *pnt == '`' )
  121. {
  122. cpnt->op = op_shellcmd;
  123. pnt1 = varname;
  124. pnt++;
  125. while(*pnt && *pnt != '`') *pnt1++ = *pnt++;
  126. *pnt1++ = '\0';
  127. cpnt->variable.str = strdup(varname);
  128. if( *pnt == '`' ) pnt++;
  129. if( *pnt == '"' ) pnt++;
  130. continue;
  131. }
  132. if( *pnt == '$' )
  133. {
  134. cpnt->op = op_variable;
  135. pnt1 = varname;
  136. pnt++;
  137. while(*pnt && *pnt != '"') *pnt1++ = *pnt++;
  138. *pnt1++ = '\0';
  139. cpnt->variable.str = strdup(varname);
  140. if( *pnt == '"' ) pnt++;
  141. continue;
  142. }
  143. cpnt->op = op_constant;
  144. pnt1 = varname;
  145. while(*pnt && *pnt != '"') *pnt1++ = *pnt++;
  146. *pnt1++ = '\0';
  147. cpnt->variable.str = strdup(varname);
  148. if( *pnt == '"' ) pnt++;
  149. continue;
  150. }
  151. return list;
  152. error:
  153. if(current_file != NULL)
  154. fprintf(stderr,
  155. "Bad if clause at line %d(%s):%s\n", lineno, current_file, opnt);
  156. else
  157. fprintf(stderr,
  158. "Bad if clause at line %d:%s\n", lineno, opnt);
  159. return NULL;
  160. }
  161. /*
  162. * This function looks for a quoted string, from the input buffer, and
  163. * returns a pointer to a copy of this string. Any characters in
  164. * the string that need to be "quoted" have a '\' character inserted
  165. * in front - this way we can directly write these strings into
  166. * wish scripts.
  167. */
  168. static char * get_qstring(char *pnt, char ** labl)
  169. {
  170. char quotechar;
  171. char newlabel[1024];
  172. char * pnt1;
  173. char * pnt2;
  174. while( *pnt && *pnt != '"' && *pnt != '\'') pnt++;
  175. if (*pnt == '\0') return pnt;
  176. quotechar = *pnt++;
  177. pnt1 = newlabel;
  178. while(*pnt && *pnt != quotechar && pnt[-1] != '\\')
  179. {
  180. /*
  181. * Quote the character if we need to.
  182. */
  183. if( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']')
  184. *pnt1++ = '\\';
  185. *pnt1++ = *pnt++;
  186. }
  187. *pnt1++ = '\0';
  188. pnt2 = (char *) malloc(strlen(newlabel) + 1);
  189. strcpy(pnt2, newlabel);
  190. *labl = pnt2;
  191. /*
  192. * Skip over last quote, and whitespace.
  193. */
  194. pnt++;
  195. pnt = skip_whitespace(pnt);
  196. return pnt;
  197. }
  198. static char * parse_choices(struct kconfig * choice_kcfg, char * pnt)
  199. {
  200. struct kconfig * kcfg;
  201. int index = 1;
  202. /*
  203. * Choices appear in pairs of strings. The parse is fairly trivial.
  204. */
  205. while(1)
  206. {
  207. pnt = skip_whitespace(pnt);
  208. if(*pnt == '\0') break;
  209. kcfg = (struct kconfig *) malloc(sizeof(struct kconfig));
  210. memset(kcfg, 0, sizeof(struct kconfig));
  211. kcfg->tok = tok_choice;
  212. if( clast != NULL )
  213. {
  214. clast->next = kcfg;
  215. clast = kcfg;
  216. }
  217. else
  218. {
  219. clast = config = kcfg;
  220. }
  221. pnt = get_string(pnt, &kcfg->label);
  222. pnt = skip_whitespace(pnt);
  223. pnt = get_string(pnt, &kcfg->optionname);
  224. kcfg->choice_label = choice_kcfg;
  225. kcfg->choice_value = index++;
  226. if( strcmp(kcfg->label, choice_kcfg->value) == 0 )
  227. choice_kcfg->choice_value = kcfg->choice_value;
  228. }
  229. return pnt;
  230. }
  231. /*
  232. * This function grabs one text token from the input buffer
  233. * and returns a pointer to a copy of just the identifier.
  234. * This can be either a variable name (i.e. CONFIG_NET),
  235. * or it could be the default value for the option.
  236. */
  237. static char * get_string(char *pnt, char ** labl)
  238. {
  239. char newlabel[1024];
  240. char * pnt1;
  241. char * pnt2;
  242. if (*pnt == '\0') return pnt;
  243. pnt1 = newlabel;
  244. while(*pnt && *pnt != ' ' && *pnt != '\t')
  245. {
  246. *pnt1++ = *pnt++;
  247. }
  248. *pnt1++ = '\0';
  249. pnt2 = (char *) malloc(strlen(newlabel) + 1);
  250. strcpy(pnt2, newlabel);
  251. *labl = pnt2;
  252. if( *pnt ) pnt++;
  253. return pnt;
  254. }
  255. /*
  256. * Top level parse function. Input pointer is one complete line from config.in
  257. * and the result is that we create a token that describes this line
  258. * and insert it into our linked list.
  259. */
  260. void parse(char * pnt) {
  261. enum token tok;
  262. struct kconfig * kcfg;
  263. char tmpbuf[24],fake_if[1024];
  264. /*
  265. * Ignore comments and leading whitespace.
  266. */
  267. pnt = skip_whitespace(pnt);
  268. while( *pnt && (*pnt == ' ' || *pnt == '\t')) pnt++;
  269. if(! *pnt ) return;
  270. if( *pnt == '#' ) return;
  271. /*
  272. * Now categorize the next token.
  273. */
  274. tok = tok_unknown;
  275. if (strncmp(pnt, "mainmenu_name", 13) == 0)
  276. {
  277. tok = tok_menuname;
  278. pnt += 13;
  279. }
  280. else if (strncmp(pnt, "source", 6) == 0)
  281. {
  282. pnt += 7;
  283. pnt = skip_whitespace(pnt);
  284. do_source(pnt);
  285. return;
  286. }
  287. else if (strncmp(pnt, "mainmenu_option", 15) == 0)
  288. {
  289. menus_seen++;
  290. tok = tok_menuoption;
  291. pnt += 15;
  292. }
  293. else if (strncmp(pnt, "$MAKE ", 6) == 0)
  294. {
  295. tok = tok_make;
  296. }
  297. else if (strncmp(pnt, "comment", 7) == 0)
  298. {
  299. tok = tok_comment;
  300. pnt += 7;
  301. }
  302. else if (strncmp(pnt, "choice", 6) == 0)
  303. {
  304. tok = tok_choose;
  305. pnt += 6;
  306. }
  307. else if (strncmp(pnt, "define_bool", 11) == 0)
  308. {
  309. tok = tok_define;
  310. pnt += 11;
  311. }
  312. else if (strncmp(pnt, "bool", 4) == 0)
  313. {
  314. tok = tok_bool;
  315. pnt += 4;
  316. }
  317. else if (strncmp(pnt, "tristate", 8) == 0)
  318. {
  319. tok = tok_tristate;
  320. pnt += 8;
  321. }
  322. else if (strncmp(pnt, "dep_tristate", 12) == 0)
  323. {
  324. tok = tok_dep_tristate;
  325. pnt += 12;
  326. }
  327. else if (strncmp(pnt, "int", 3) == 0)
  328. {
  329. tok = tok_int;
  330. pnt += 3;
  331. }
  332. else if (strncmp(pnt, "hex", 3) == 0)
  333. {
  334. tok = tok_hex;
  335. pnt += 3;
  336. }
  337. else if (strncmp(pnt, "if", 2) == 0)
  338. {
  339. tok = tok_if;
  340. pnt += 2;
  341. }
  342. else if (strncmp(pnt, "else", 4) == 0)
  343. {
  344. tok = tok_else;
  345. pnt += 4;
  346. }
  347. else if (strncmp(pnt, "fi", 2) == 0)
  348. {
  349. tok = tok_fi;
  350. pnt += 2;
  351. }
  352. else if (strncmp(pnt, "endmenu", 7) == 0)
  353. {
  354. tok = tok_endmenu;
  355. pnt += 7;
  356. }
  357. if( tok == tok_unknown)
  358. {
  359. if( clast != NULL && clast->tok == tok_if
  360. && strcmp(pnt,"then") == 0) return;
  361. if( current_file != NULL )
  362. fprintf(stderr, "unknown command=%s(%s %d)\n", pnt,
  363. current_file, lineno);
  364. else
  365. fprintf(stderr, "unknown command=%s(%d)\n", pnt,lineno);
  366. return;
  367. }
  368. /*
  369. * Allocate memory for this item, and attach it to the end of the linked
  370. * list.
  371. */
  372. kcfg = (struct kconfig *) malloc(sizeof(struct kconfig));
  373. memset(kcfg, 0, sizeof(struct kconfig));
  374. kcfg->tok = tok;
  375. if( clast != NULL )
  376. {
  377. clast->next = kcfg;
  378. clast = kcfg;
  379. }
  380. else
  381. {
  382. clast = config = kcfg;
  383. }
  384. pnt = skip_whitespace(pnt);
  385. /*
  386. * Now parse the remaining parts of the option, and attach the results
  387. * to the structure.
  388. */
  389. switch (tok)
  390. {
  391. case tok_choose:
  392. pnt = get_qstring(pnt, &kcfg->label);
  393. pnt = get_qstring(pnt, &kcfg->optionname);
  394. pnt = get_string(pnt, &kcfg->value);
  395. /*
  396. * Now we need to break apart the individual options into their
  397. * own configuration structures.
  398. */
  399. parse_choices(kcfg, kcfg->optionname);
  400. free(kcfg->optionname);
  401. sprintf(tmpbuf, "tmpvar_%d", choose_number++);
  402. kcfg->optionname = strdup(tmpbuf);
  403. break;
  404. case tok_define:
  405. pnt = get_string(pnt, &kcfg->optionname);
  406. if(*pnt == 'y' || *pnt == 'Y' ) kcfg->value = "1";
  407. if(*pnt == 'n' || *pnt == 'N' ) kcfg->value = "0";
  408. if(*pnt == 'm' || *pnt == 'M' ) kcfg->value = "2";
  409. break;
  410. case tok_menuname:
  411. pnt = get_qstring(pnt, &kcfg->label);
  412. break;
  413. case tok_bool:
  414. case tok_tristate:
  415. pnt = get_qstring(pnt, &kcfg->label);
  416. pnt = get_string(pnt, &kcfg->optionname);
  417. break;
  418. case tok_int:
  419. case tok_hex:
  420. pnt = get_qstring(pnt, &kcfg->label);
  421. pnt = get_string(pnt, &kcfg->optionname);
  422. pnt = get_string(pnt, &kcfg->value);
  423. break;
  424. case tok_dep_tristate:
  425. pnt = get_qstring(pnt, &kcfg->label);
  426. pnt = get_string(pnt, &kcfg->optionname);
  427. pnt = skip_whitespace(pnt);
  428. if( *pnt == '$') pnt++;
  429. pnt = get_string(pnt, &kcfg->depend.str);
  430. /*
  431. * Create a conditional for this object's dependency.
  432. *
  433. * We can't use "!= n" because this is internally converted to "!= 0"
  434. * and if UMSDOS depends on MSDOS which depends on FAT, then when FAT
  435. * is disabled MSDOS has 16 added to its value, making UMSDOS fully
  436. * available. Whew.
  437. *
  438. * This is more of a hack than a fix. Nested "if" conditionals are
  439. * probably affected too - that +/- 16 affects things in too many
  440. * places. But this should do for now.
  441. */
  442. sprintf(fake_if,"[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" ]; then",
  443. kcfg->depend.str,kcfg->depend.str);
  444. kcfg->cond = parse_if(fake_if);
  445. if(kcfg->cond == NULL )
  446. {
  447. exit(1);
  448. }
  449. break;
  450. case tok_comment:
  451. pnt = get_qstring(pnt, &kcfg->label);
  452. if( koption != NULL )
  453. {
  454. pnt = get_qstring(pnt, &kcfg->label);
  455. koption->label = kcfg->label;
  456. koption = NULL;
  457. }
  458. break;
  459. case tok_menuoption:
  460. if( strncmp(pnt, "next_comment", 12) == 0)
  461. {
  462. koption = kcfg;
  463. }
  464. else
  465. {
  466. pnt = get_qstring(pnt, &kcfg->label);
  467. }
  468. break;
  469. case tok_make:
  470. kcfg->value=strdup(pnt);
  471. break;
  472. case tok_else:
  473. case tok_fi:
  474. case tok_endmenu:
  475. break;
  476. case tok_if:
  477. /*
  478. * Conditionals are different. For the first level parse, only
  479. * tok_if and tok_dep_tristate items have a ->cond chain attached.
  480. */
  481. kcfg->cond = parse_if(pnt);
  482. if(kcfg->cond == NULL )
  483. {
  484. exit(1);
  485. }
  486. break;
  487. default:
  488. exit(0);
  489. }
  490. return;
  491. }
  492. /*
  493. * Simple function to dump to the screen what the condition chain looks like.
  494. */
  495. void dump_if(struct condition * cond)
  496. {
  497. printf(" ");
  498. while(cond != NULL )
  499. {
  500. switch(cond->op){
  501. case op_eq:
  502. printf(" = ");
  503. break;
  504. case op_bang:
  505. printf(" ! ");
  506. break;
  507. case op_neq:
  508. printf(" != ");
  509. break;
  510. case op_and:
  511. printf(" -a ");
  512. break;
  513. case op_lparen:
  514. printf("(");
  515. break;
  516. case op_rparen:
  517. printf(")");
  518. break;
  519. case op_variable:
  520. printf("$%s", cond->variable.str);
  521. break;
  522. case op_constant:
  523. printf("'%s'", cond->variable.str);
  524. break;
  525. default:
  526. break;
  527. }
  528. cond = cond->next;
  529. }
  530. printf("\n");
  531. }
  532. static int do_source(char * filename)
  533. {
  534. char buffer[1024];
  535. int offset;
  536. int old_lineno;
  537. char * old_file;
  538. char * pnt;
  539. FILE * infile;
  540. if( strcmp(filename, "-") == 0 )
  541. infile = stdin;
  542. else
  543. infile = fopen(filename,"r");
  544. /*
  545. * If our cwd was in the scripts directory, we might have to go up one
  546. * to find the sourced file.
  547. */
  548. if(!infile) {
  549. strcpy (buffer, "../");
  550. strcat (buffer, filename);
  551. infile = fopen(buffer,"r");
  552. }
  553. if(!infile) {
  554. fprintf(stderr,"Unable to open file %s\n", filename);
  555. return 1;
  556. }
  557. old_lineno = lineno;
  558. lineno = 0;
  559. if( infile != stdin ) {
  560. old_file = current_file;
  561. current_file = filename;
  562. }
  563. offset = 0;
  564. while(1)
  565. {
  566. fgets(&buffer[offset], sizeof(buffer) - offset, infile);
  567. if(feof(infile)) break;
  568. /*
  569. * Strip the trailing return character.
  570. */
  571. pnt = buffer + strlen(buffer) - 1;
  572. if( *pnt == '\n') *pnt-- = 0;
  573. lineno++;
  574. if( *pnt == '\\' )
  575. {
  576. offset = pnt - buffer;
  577. }
  578. else
  579. {
  580. parse(buffer);
  581. offset = 0;
  582. }
  583. }
  584. fclose(infile);
  585. if( infile != stdin ) {
  586. current_file = old_file;
  587. }
  588. lineno = old_lineno;
  589. return 0;
  590. }
  591. int main(int argc, char * argv[])
  592. {
  593. #if 0
  594. char buffer[1024];
  595. char * pnt;
  596. struct kconfig * cfg;
  597. int i;
  598. #endif
  599. /*
  600. * Read stdin to get the top level script.
  601. */
  602. do_source("-");
  603. if( menus_seen == 0 )
  604. {
  605. fprintf(stderr,"The config.in file for this platform does not support\n");
  606. fprintf(stderr,"menus.\n");
  607. exit(1);
  608. }
  609. /*
  610. * Input file is now parsed. Next we need to go through and attach
  611. * the correct conditions to each of the actual menu items and kill
  612. * the if/else/endif tokens from the list. We also flag the menu items
  613. * that have other things that depend upon its setting.
  614. */
  615. fix_conditionals(config);
  616. /*
  617. * Finally, we generate the wish script.
  618. */
  619. dump_tk_script(config);
  620. #if 0
  621. /*
  622. * Now dump what we have so far. This is only for debugging so that
  623. * we can display what we think we have in the list.
  624. */
  625. for(cfg = config; cfg; cfg = cfg->next)
  626. {
  627. if(cfg->cond != NULL && cfg->tok != tok_if)
  628. dump_if(cfg->cond);
  629. switch(cfg->tok)
  630. {
  631. case tok_menuname:
  632. printf("main_menuname ");
  633. break;
  634. case tok_bool:
  635. printf("bool ");
  636. break;
  637. case tok_tristate:
  638. printf("tristate ");
  639. break;
  640. case tok_dep_tristate:
  641. printf("dep_tristate ");
  642. break;
  643. case tok_int:
  644. printf("int ");
  645. break;
  646. case tok_hex:
  647. printf("hex ");
  648. break;
  649. case tok_comment:
  650. printf("comment ");
  651. break;
  652. case tok_menuoption:
  653. printf("menuoption ");
  654. break;
  655. case tok_else:
  656. printf("else");
  657. break;
  658. case tok_fi:
  659. printf("fi");
  660. break;
  661. case tok_if:
  662. printf("if");
  663. break;
  664. default:
  665. }
  666. switch(cfg->tok)
  667. {
  668. case tok_menuoption:
  669. case tok_comment:
  670. case tok_menuname:
  671. printf("%s\n", cfg->label);
  672. break;
  673. case tok_bool:
  674. case tok_tristate:
  675. case tok_dep_tristate:
  676. case tok_int:
  677. case tok_hex:
  678. printf("%s %s\n", cfg->label, cfg->optionname);
  679. break;
  680. case tok_if:
  681. dump_if(cfg->cond);
  682. break;
  683. case tok_nop:
  684. case tok_endmenu:
  685. break;
  686. default:
  687. printf("\n");
  688. }
  689. }
  690. #endif
  691. return 0;
  692. }