boot_script.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. /* Boot script parser for Mach. */
  2. /* Written by Shantanu Goel (goel@cs.columbia.edu). */
  3. #include <mach/mach_types.h>
  4. #include <string.h>
  5. #include "boot_script.h"
  6. /* This structure describes a symbol. */
  7. struct sym
  8. {
  9. /* Symbol name. */
  10. const char *name;
  11. /* Type of value returned by function. */
  12. int type;
  13. /* Symbol value. */
  14. long val;
  15. /* For function symbols; type of value returned by function. */
  16. int ret_type;
  17. /* For function symbols; if set, execute function at the time
  18. of command execution, not during parsing. A function with
  19. this field set must also have `no_arg' set. Also, the function's
  20. `val' argument will always be NULL. */
  21. int run_on_exec;
  22. };
  23. /* Additional values symbols can take.
  24. These are only used internally. */
  25. #define VAL_SYM 10 /* symbol table entry */
  26. #define VAL_FUNC 11 /* function pointer */
  27. /* This structure describes an argument. */
  28. struct arg
  29. {
  30. /* Argument text copied verbatim. 0 if none. */
  31. char *text;
  32. /* Type of value assigned. 0 if none. */
  33. int type;
  34. /* Argument value. */
  35. long val;
  36. };
  37. /* List of commands. */
  38. static struct cmd **cmds = 0;
  39. /* Amount allocated for `cmds'. */
  40. static int cmds_alloc = 0;
  41. /* Next available slot in `cmds'. */
  42. static int cmds_index = 0;
  43. /* Symbol table. */
  44. static struct sym **symtab = 0;
  45. /* Amount allocated for `symtab'. */
  46. static int symtab_alloc = 0;
  47. /* Next available slot in `symtab'. */
  48. static int symtab_index = 0;
  49. /* Create a task and suspend it. */
  50. static int
  51. create_task (struct cmd *cmd, long *val)
  52. {
  53. int err = boot_script_task_create (cmd);
  54. *val = (long) cmd->task;
  55. return err;
  56. }
  57. /* Resume a task. */
  58. static int
  59. resume_task (struct cmd *cmd, const long *val)
  60. {
  61. return boot_script_task_resume (cmd);
  62. }
  63. /* Resume a task when the user hits return. */
  64. static int
  65. prompt_resume_task (struct cmd *cmd, const long *val)
  66. {
  67. return boot_script_prompt_task_resume (cmd);
  68. }
  69. /* List of builtin symbols. */
  70. static struct sym builtin_symbols[] =
  71. {
  72. { "task-create", VAL_FUNC, (long) create_task, VAL_TASK, 0 },
  73. { "task-resume", VAL_FUNC, (long) resume_task, VAL_NONE, 1 },
  74. { "prompt-task-resume", VAL_FUNC, (long) prompt_resume_task, VAL_NONE, 1 },
  75. };
  76. #define NUM_BUILTIN (sizeof (builtin_symbols) / sizeof (builtin_symbols[0]))
  77. /* Free CMD and all storage associated with it.
  78. If ABORTING is set, terminate the task associated with CMD,
  79. otherwise just deallocate the send right. */
  80. static void
  81. free_cmd (struct cmd *cmd, int aborting)
  82. {
  83. if (cmd->task)
  84. boot_script_free_task (cmd->task, aborting);
  85. if (cmd->args)
  86. {
  87. int i;
  88. for (i = 0; i < cmd->args_index; i++)
  89. boot_script_free (cmd->args[i], sizeof *cmd->args[i]);
  90. boot_script_free (cmd->args, sizeof cmd->args[0] * cmd->args_alloc);
  91. }
  92. if (cmd->exec_funcs)
  93. boot_script_free (cmd->exec_funcs,
  94. sizeof cmd->exec_funcs[0] * cmd->exec_funcs_alloc);
  95. boot_script_free (cmd, sizeof *cmd);
  96. }
  97. /* Free all storage allocated by the parser.
  98. If ABORTING is set, terminate all tasks. */
  99. static void
  100. cleanup (int aborting)
  101. {
  102. int i;
  103. for (i = 0; i < cmds_index; i++)
  104. free_cmd (cmds[i], aborting);
  105. boot_script_free (cmds, sizeof cmds[0] * cmds_alloc);
  106. cmds = 0;
  107. cmds_index = cmds_alloc = 0;
  108. for (i = 0; i < symtab_index; i++)
  109. boot_script_free (symtab[i], sizeof *symtab[i]);
  110. boot_script_free (symtab, sizeof symtab[0] * symtab_alloc);
  111. symtab = 0;
  112. symtab_index = symtab_alloc = 0;
  113. }
  114. /* Add PTR to the list of pointers PTR_LIST, which
  115. currently has ALLOC amount of space allocated to it, and
  116. whose next available slot is INDEX. If more space
  117. needs to to allocated, INCR is the amount by which
  118. to increase it. Return 0 on success, non-zero otherwise. */
  119. static int
  120. add_list (void *ptr, void ***ptr_list, int *alloc, int *index, int incr)
  121. {
  122. if (*index == *alloc)
  123. {
  124. void **p;
  125. *alloc += incr;
  126. p = boot_script_malloc (*alloc * sizeof (void *));
  127. if (! p)
  128. {
  129. *alloc -= incr;
  130. return 1;
  131. }
  132. if (*ptr_list)
  133. {
  134. memcpy (p, *ptr_list, *index * sizeof (void *));
  135. boot_script_free (*ptr_list, (*alloc - incr) * sizeof (void *));
  136. }
  137. *ptr_list = p;
  138. }
  139. *(*ptr_list + *index) = ptr;
  140. *index += 1;
  141. return 0;
  142. }
  143. /* Create an argument with TEXT, value type TYPE, and value VAL.
  144. Add the argument to the argument list of CMD. */
  145. static struct arg *
  146. add_arg (struct cmd *cmd, char *text, int type, int val)
  147. {
  148. struct arg *arg;
  149. arg = boot_script_malloc (sizeof (struct arg));
  150. if (arg)
  151. {
  152. arg->text = text;
  153. arg->type = type;
  154. arg->val = val;
  155. if (add_list (arg, (void ***) &cmd->args,
  156. &cmd->args_alloc, &cmd->args_index, 5))
  157. {
  158. boot_script_free (arg, sizeof *arg);
  159. return 0;
  160. }
  161. }
  162. return arg;
  163. }
  164. /* Search for the symbol NAME in the symbol table. */
  165. static struct sym *
  166. sym_lookup (const char *name)
  167. {
  168. int i;
  169. for (i = 0; i < symtab_index; i++)
  170. if (! strcmp (name, symtab[i]->name))
  171. return symtab[i];
  172. return 0;
  173. }
  174. /* Create an entry for symbol NAME in the symbol table. */
  175. static struct sym *
  176. sym_enter (const char *name)
  177. {
  178. struct sym *sym;
  179. sym = boot_script_malloc (sizeof (struct sym));
  180. if (sym)
  181. {
  182. memset (sym, 0, sizeof (struct sym));
  183. sym->name = name;
  184. if (add_list (sym, (void ***) &symtab, &symtab_alloc, &symtab_index, 20))
  185. {
  186. boot_script_free (sym, sizeof *sym);
  187. return 0;
  188. }
  189. }
  190. return sym;
  191. }
  192. /* Parse the command line CMDLINE. */
  193. int
  194. boot_script_parse_line (void *hook, char *cmdline)
  195. {
  196. char *p, *q;
  197. int error;
  198. struct cmd *cmd;
  199. struct arg *arg;
  200. /* Extract command name. Ignore line if it lacks a command. */
  201. for (p = cmdline; *p == ' ' || *p == '\t'; p++)
  202. ;
  203. if (*p == '#')
  204. /* Ignore comment line. */
  205. return 0;
  206. #if 0
  207. if (*p && *p != ' ' && *p != '\t' && *p != '\n')
  208. {
  209. printf ("(bootstrap): %s\n", cmdline);
  210. }
  211. #endif
  212. for (q = p; *q && *q != ' ' && *q != '\t' && *q != '\n'; q++)
  213. ;
  214. if (p == q)
  215. return 0;
  216. *q = '\0';
  217. /* Allocate a command structure. */
  218. cmd = boot_script_malloc (sizeof (struct cmd));
  219. if (! cmd)
  220. return BOOT_SCRIPT_NOMEM;
  221. memset (cmd, 0, sizeof (struct cmd));
  222. cmd->hook = hook;
  223. cmd->path = p;
  224. p = q + 1;
  225. for (arg = 0;;)
  226. {
  227. if (! arg)
  228. {
  229. /* Skip whitespace. */
  230. while (*p == ' ' || *p == '\t')
  231. p++;
  232. /* End of command line. */
  233. if (! *p || *p == '\n')
  234. {
  235. /* Add command to list. */
  236. if (add_list (cmd, (void ***) &cmds,
  237. &cmds_alloc, &cmds_index, 10))
  238. {
  239. error = BOOT_SCRIPT_NOMEM;
  240. goto bad;
  241. }
  242. return 0;
  243. }
  244. }
  245. /* Look for a symbol. */
  246. if (arg || (*p == '$' && (*(p + 1) == '{' || *(p + 1) == '(')))
  247. {
  248. char end_char = (*(p + 1) == '{') ? '}' : ')';
  249. struct sym *sym = 0;
  250. for (p += 2;;)
  251. {
  252. char c;
  253. unsigned i;
  254. int type;
  255. long val;
  256. struct sym *s;
  257. /* Parse symbol name. */
  258. for (q = p; *q && *q != '\n' && *q != end_char && *q != '='; q++)
  259. ;
  260. if (p == q || ! *q || *q == '\n'
  261. || (end_char == '}' && *q != '}'))
  262. {
  263. error = BOOT_SCRIPT_SYNTAX_ERROR;
  264. goto bad;
  265. }
  266. c = *q;
  267. *q = '\0';
  268. /* See if this is a builtin symbol. */
  269. for (i = 0; i < NUM_BUILTIN; i++)
  270. if (! strcmp (p, builtin_symbols[i].name))
  271. break;
  272. if (i < NUM_BUILTIN)
  273. s = &builtin_symbols[i];
  274. else
  275. {
  276. /* Look up symbol in symbol table.
  277. If no entry exists, create one. */
  278. s = sym_lookup (p);
  279. if (! s)
  280. {
  281. s = sym_enter (p);
  282. if (! s)
  283. {
  284. error = BOOT_SCRIPT_NOMEM;
  285. goto bad;
  286. }
  287. }
  288. }
  289. /* Only values are allowed in ${...} constructs. */
  290. if (end_char == '}' && s->type == VAL_FUNC)
  291. return BOOT_SCRIPT_INVALID_SYM;
  292. /* Check that assignment is valid. */
  293. if (c == '=' && s->type == VAL_FUNC)
  294. {
  295. error = BOOT_SCRIPT_INVALID_ASG;
  296. goto bad;
  297. }
  298. /* For function symbols, execute the function. */
  299. if (s->type == VAL_FUNC)
  300. {
  301. if (! s->run_on_exec)
  302. {
  303. (error
  304. = ((*((int (*) (struct cmd *, long *)) s->val))
  305. (cmd, &val)));
  306. if (error)
  307. goto bad;
  308. type = s->ret_type;
  309. }
  310. else
  311. {
  312. if (add_list (s, (void ***) &cmd->exec_funcs,
  313. &cmd->exec_funcs_alloc,
  314. &cmd->exec_funcs_index, 5))
  315. {
  316. error = BOOT_SCRIPT_NOMEM;
  317. goto bad;
  318. }
  319. type = VAL_NONE;
  320. goto out;
  321. }
  322. }
  323. else if (s->type == VAL_NONE)
  324. {
  325. type = VAL_SYM;
  326. val = (long) s;
  327. }
  328. else
  329. {
  330. type = s->type;
  331. val = s->val;
  332. }
  333. if (sym)
  334. {
  335. sym->type = type;
  336. sym->val = val;
  337. }
  338. else if (arg)
  339. {
  340. arg->type = type;
  341. arg->val = val;
  342. }
  343. out:
  344. p = q + 1;
  345. if (c == end_char)
  346. {
  347. /* Create an argument if necessary.
  348. We create an argument if the symbol appears
  349. in the expression by itself.
  350. NOTE: This is temporary till the boot filesystem
  351. servers support arguments. When that happens,
  352. symbol values will only be printed if they're
  353. associated with an argument. */
  354. if (! arg && end_char == '}')
  355. {
  356. if (! add_arg (cmd, 0, type, val))
  357. {
  358. error = BOOT_SCRIPT_NOMEM;
  359. goto bad;
  360. }
  361. }
  362. arg = 0;
  363. break;
  364. }
  365. if (s->type != VAL_FUNC)
  366. sym = s;
  367. }
  368. }
  369. else
  370. {
  371. char c;
  372. /* Command argument; just copy the text. */
  373. for (q = p;; q++)
  374. {
  375. if (! *q || *q == ' ' || *q == '\t' || *q == '\n')
  376. break;
  377. if (*q == '$' && *(q + 1) == '{')
  378. break;
  379. }
  380. c = *q;
  381. *q = '\0';
  382. /* Add argument to list. */
  383. arg = add_arg (cmd, p, VAL_NONE, 0);
  384. if (! arg)
  385. {
  386. error = BOOT_SCRIPT_NOMEM;
  387. goto bad;
  388. }
  389. if (c == '$')
  390. p = q;
  391. else
  392. {
  393. if (c)
  394. p = q + 1;
  395. else
  396. p = q;
  397. arg = 0;
  398. }
  399. }
  400. }
  401. bad:
  402. free_cmd (cmd, 1);
  403. cleanup (1);
  404. return error;
  405. }
  406. /* Ensure that the command line buffer can accommodate LEN bytes of space. */
  407. #define CHECK_CMDLINE_LEN(len) \
  408. { \
  409. if (cmdline_alloc - cmdline_index < len) \
  410. { \
  411. char *ptr; \
  412. int alloc, i; \
  413. alloc = cmdline_alloc + len - (cmdline_alloc - cmdline_index) + 100; \
  414. ptr = boot_script_malloc (alloc); \
  415. if (! ptr) \
  416. { \
  417. error = BOOT_SCRIPT_NOMEM; \
  418. goto done; \
  419. } \
  420. memcpy (ptr, cmdline, cmdline_index); \
  421. for (i = 0; i < argc; ++i) \
  422. argv[i] = ptr + (argv[i] - cmdline); \
  423. boot_script_free (cmdline, cmdline_alloc); \
  424. cmdline = ptr; \
  425. cmdline_alloc = alloc; \
  426. } \
  427. }
  428. /* Execute commands previously parsed. */
  429. int
  430. boot_script_exec (void)
  431. {
  432. int cmd_index;
  433. for (cmd_index = 0; cmd_index < cmds_index; cmd_index++)
  434. {
  435. char **argv, *cmdline;
  436. int i, argc, cmdline_alloc;
  437. int cmdline_index, error, arg_index;
  438. struct cmd *cmd = cmds[cmd_index];
  439. /* Skip command if it doesn't have an associated task. */
  440. if (cmd->task == 0)
  441. continue;
  442. /* Allocate a command line and copy command name. */
  443. cmdline_index = strlen (cmd->path) + 1;
  444. cmdline_alloc = cmdline_index + 100;
  445. cmdline = boot_script_malloc (cmdline_alloc);
  446. if (! cmdline)
  447. {
  448. cleanup (1);
  449. return BOOT_SCRIPT_NOMEM;
  450. }
  451. memcpy (cmdline, cmd->path, cmdline_index);
  452. /* Allocate argument vector. */
  453. argv = boot_script_malloc (sizeof (char *) * (cmd->args_index + 2));
  454. if (! argv)
  455. {
  456. boot_script_free (cmdline, cmdline_alloc);
  457. cleanup (1);
  458. return BOOT_SCRIPT_NOMEM;
  459. }
  460. argv[0] = cmdline;
  461. argc = 1;
  462. /* Build arguments. */
  463. for (arg_index = 0; arg_index < cmd->args_index; arg_index++)
  464. {
  465. struct arg *arg = cmd->args[arg_index];
  466. /* Copy argument text. */
  467. if (arg->text)
  468. {
  469. int len = strlen (arg->text);
  470. if (arg->type == VAL_NONE)
  471. len++;
  472. CHECK_CMDLINE_LEN (len);
  473. memcpy (cmdline + cmdline_index, arg->text, len);
  474. argv[argc++] = &cmdline[cmdline_index];
  475. cmdline_index += len;
  476. }
  477. /* Add value of any symbol associated with this argument. */
  478. if (arg->type != VAL_NONE)
  479. {
  480. char *p, buf[50];
  481. int len;
  482. mach_port_t name;
  483. if (arg->type == VAL_SYM)
  484. {
  485. struct sym *sym = (struct sym *) arg->val;
  486. /* Resolve symbol value. */
  487. while (sym->type == VAL_SYM)
  488. sym = (struct sym *) sym->val;
  489. if (sym->type == VAL_NONE)
  490. {
  491. error = BOOT_SCRIPT_UNDEF_SYM;
  492. goto done;
  493. }
  494. arg->type = sym->type;
  495. arg->val = sym->val;
  496. }
  497. /* Print argument value. */
  498. switch (arg->type)
  499. {
  500. case VAL_STR:
  501. p = (char *) arg->val;
  502. len = strlen (p);
  503. break;
  504. case VAL_TASK:
  505. case VAL_PORT:
  506. if (arg->type == VAL_TASK)
  507. /* Insert send right to task port. */
  508. error = boot_script_insert_task_port
  509. (cmd, (task_t) arg->val, &name);
  510. else
  511. /* Insert send right. */
  512. error = boot_script_insert_right (cmd,
  513. (mach_port_t) arg->val,
  514. &name);
  515. if (error)
  516. goto done;
  517. i = name;
  518. p = buf + sizeof (buf);
  519. len = 0;
  520. do
  521. {
  522. *--p = i % 10 + '0';
  523. len++;
  524. }
  525. while (i /= 10);
  526. break;
  527. default:
  528. error = BOOT_SCRIPT_BAD_TYPE;
  529. goto done;
  530. }
  531. len++;
  532. CHECK_CMDLINE_LEN (len);
  533. memcpy (cmdline + cmdline_index, p, len - 1);
  534. *(cmdline + cmdline_index + len - 1) = '\0';
  535. if (! arg->text)
  536. argv[argc++] = &cmdline[cmdline_index];
  537. cmdline_index += len;
  538. }
  539. }
  540. /* Terminate argument vector. */
  541. argv[argc] = 0;
  542. /* Execute the command. */
  543. if (boot_script_exec_cmd (cmd->hook, cmd->task, cmd->path,
  544. argc, argv, cmdline, cmdline_index))
  545. {
  546. error = BOOT_SCRIPT_EXEC_ERROR;
  547. goto done;
  548. }
  549. error = 0;
  550. done:
  551. boot_script_free (cmdline, cmdline_alloc);
  552. boot_script_free (argv, sizeof (char *) * (cmd->args_index + 2));
  553. if (error)
  554. {
  555. cleanup (1);
  556. return error;
  557. }
  558. }
  559. for (cmd_index = 0; cmd_index < cmds_index; cmd_index++)
  560. {
  561. int i;
  562. struct cmd *cmd = cmds[cmd_index];
  563. /* Execute functions that want to be run on exec. */
  564. for (i = 0; i < cmd->exec_funcs_index; i++)
  565. {
  566. struct sym *sym = cmd->exec_funcs[i];
  567. int error = ((*((int (*) (struct cmd *, int *)) sym->val))
  568. (cmd, 0));
  569. if (error)
  570. {
  571. cleanup (1);
  572. return error;
  573. }
  574. }
  575. }
  576. cleanup (0);
  577. return 0;
  578. }
  579. /* Create an entry for the variable NAME with TYPE and value VAL,
  580. in the symbol table. */
  581. int
  582. boot_script_set_variable (const char *name, int type, long val)
  583. {
  584. struct sym *sym = sym_enter (name);
  585. if (sym)
  586. {
  587. sym->type = type;
  588. sym->val = val;
  589. }
  590. return sym ? 0 : 1;
  591. }
  592. /* Define the function NAME, which will return type RET_TYPE. */
  593. int
  594. boot_script_define_function (const char *name, int ret_type,
  595. int (*func) (const struct cmd *cmd, int *val))
  596. {
  597. struct sym *sym = sym_enter (name);
  598. if (sym)
  599. {
  600. sym->type = VAL_FUNC;
  601. sym->val = (long) func;
  602. sym->ret_type = ret_type;
  603. sym->run_on_exec = ret_type == VAL_NONE;
  604. }
  605. return sym ? 0 : 1;
  606. }
  607. /* Return a string describing ERR. */
  608. char *
  609. boot_script_error_string (int err)
  610. {
  611. switch (err)
  612. {
  613. case BOOT_SCRIPT_NOMEM:
  614. return "no memory";
  615. case BOOT_SCRIPT_SYNTAX_ERROR:
  616. return "syntax error";
  617. case BOOT_SCRIPT_INVALID_ASG:
  618. return "invalid variable in assignment";
  619. case BOOT_SCRIPT_MACH_ERROR:
  620. return "mach error";
  621. case BOOT_SCRIPT_UNDEF_SYM:
  622. return "undefined symbol";
  623. case BOOT_SCRIPT_EXEC_ERROR:
  624. return "exec error";
  625. case BOOT_SCRIPT_INVALID_SYM:
  626. return "invalid variable in expression";
  627. case BOOT_SCRIPT_BAD_TYPE:
  628. return "invalid value type";
  629. }
  630. return 0;
  631. }
  632. #ifdef BOOT_SCRIPT_TEST
  633. #include <stdio.h>
  634. int
  635. boot_script_exec_cmd (void *hook,
  636. mach_port_t task, char *path, int argc,
  637. char **argv, char *strings, int stringlen)
  638. {
  639. int i;
  640. printf ("port = %d: ", (int) task);
  641. for (i = 0; i < argc; i++)
  642. printf ("%s ", argv[i]);
  643. printf ("\n");
  644. return 0;
  645. }
  646. void
  647. main (int argc, char **argv)
  648. {
  649. char buf[500], *p;
  650. int len;
  651. FILE *fp;
  652. mach_port_t host_port, device_port;
  653. if (argc < 2)
  654. {
  655. fprintf (stderr, "Usage: %s <script>\n", argv[0]);
  656. exit (1);
  657. }
  658. fp = fopen (argv[1], "r");
  659. if (! fp)
  660. {
  661. fprintf (stderr, "Can't open %s\n", argv[1]);
  662. exit (1);
  663. }
  664. host_port = 1;
  665. device_port = 2;
  666. boot_script_set_variable ("host-port", VAL_PORT, (int) host_port);
  667. boot_script_set_variable ("device-port", VAL_PORT, (int) device_port);
  668. boot_script_set_variable ("root-device", VAL_STR, (int) "hd0a");
  669. boot_script_set_variable ("boot-args", VAL_STR, (int) "-ad");
  670. p = buf;
  671. len = sizeof (buf);
  672. while (fgets (p, len, fp))
  673. {
  674. int i, err;
  675. i = strlen (p) + 1;
  676. err = boot_script_parse_line (0, p);
  677. if (err)
  678. {
  679. fprintf (stderr, "error %s\n", boot_script_error_string (err));
  680. exit (1);
  681. }
  682. p += i;
  683. len -= i;
  684. }
  685. boot_script_exec ();
  686. exit (0);
  687. }
  688. #endif /* BOOT_SCRIPT_TEST */