lexer.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /* lexer.c - The scripting lexer. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/parser.h>
  20. #include <grub/misc.h>
  21. #include <grub/mm.h>
  22. #include <grub/script_sh.h>
  23. #include "grub_script.tab.h"
  24. #include "grub_script.yy.h"
  25. void
  26. grub_script_lexer_ref (struct grub_lexer_param *state)
  27. {
  28. state->refs++;
  29. }
  30. void
  31. grub_script_lexer_deref (struct grub_lexer_param *state)
  32. {
  33. state->refs--;
  34. }
  35. /* Start recording all characters passing through the lexer. */
  36. void
  37. grub_script_lexer_record_start (struct grub_parser_param *parser)
  38. {
  39. struct grub_lexer_param *lexer = parser->lexerstate;
  40. lexer->record = 1;
  41. lexer->recordpos = 0;
  42. if (lexer->recording) /* reuse last record */
  43. return;
  44. lexer->recordlen = GRUB_LEXER_INITIAL_RECORD_SIZE;
  45. lexer->recording = grub_malloc (lexer->recordlen);
  46. if (!lexer->recording)
  47. {
  48. grub_script_yyerror (parser, 0);
  49. lexer->record = 0;
  50. lexer->recordlen = 0;
  51. }
  52. }
  53. static char *
  54. compact (char *start, char *end)
  55. {
  56. /* Delete '{' and '}' characters and whitespaces. */
  57. while (*start && grub_isspace (*start)) start++;
  58. if (*start == '{') start++;
  59. while (*start && grub_isspace (*start)) start++;
  60. while (*end && grub_isspace (*end)) end--;
  61. if (*end == '}') end--;
  62. while (*end && grub_isspace (*end)) end--;
  63. end[1] = '\0';
  64. return start;
  65. }
  66. char *
  67. grub_script_lexer_record_stop (struct grub_parser_param *parser)
  68. {
  69. char *ptr;
  70. char *result;
  71. struct grub_lexer_param *lexer = parser->lexerstate;
  72. if (!lexer->record || !lexer->recording)
  73. return 0;
  74. /* XXX This is not necessary in BASH. */
  75. ptr = compact (lexer->recording, lexer->recording + lexer->recordpos - 1);
  76. lexer->record = 0;
  77. lexer->recordpos = 0;
  78. /* This memory would be freed by, grub_script_free. */
  79. result = grub_script_malloc (parser, grub_strlen (ptr) + 1);
  80. if (result)
  81. grub_strcpy (result, ptr);
  82. return result;
  83. }
  84. #define MAX(a,b) ((a) < (b) ? (b) : (a))
  85. /* Record STR if input recording is enabled. */
  86. void
  87. grub_script_lexer_record (struct grub_parser_param *parser, char *str)
  88. {
  89. int len;
  90. char *old;
  91. struct grub_lexer_param *lexer = parser->lexerstate;
  92. if (!lexer->record)
  93. return;
  94. len = grub_strlen (str);
  95. if (lexer->recordpos + len + 1 > lexer->recordlen)
  96. {
  97. old = lexer->recording;
  98. lexer->recordlen = MAX (len, lexer->recordlen) * 2;
  99. lexer->recording = grub_realloc (lexer->recording, lexer->recordlen);
  100. if (!lexer->recording)
  101. {
  102. grub_free (old);
  103. lexer->record = 0;
  104. lexer->recordpos = 0;
  105. lexer->recordlen /= 2;
  106. grub_script_yyerror (parser, 0);
  107. return;
  108. }
  109. }
  110. grub_strcpy (lexer->recording + lexer->recordpos, str);
  111. lexer->recordpos += len;
  112. }
  113. /* Append '\n' to SRC, before '\0' */
  114. static char *
  115. append_newline (const char *src)
  116. {
  117. char *line;
  118. grub_size_t len;
  119. len = grub_strlen (src);
  120. line = grub_malloc (len + 2);
  121. if (!line)
  122. return 0;
  123. grub_strcpy (line, src);
  124. line[len] = '\n';
  125. line[len + 1] = '\0';
  126. return line;
  127. }
  128. /* Read next line of input if necessary, and set yyscanner buffers. */
  129. int
  130. grub_script_lexer_yywrap (struct grub_parser_param *parserstate)
  131. {
  132. int len;
  133. char *line;
  134. char *line2;
  135. YY_BUFFER_STATE buffer;
  136. struct grub_lexer_param *lexerstate = parserstate->lexerstate;
  137. if (!lexerstate->refs)
  138. return 0;
  139. if (!lexerstate->getline)
  140. {
  141. grub_script_yyerror (parserstate, "unexpected end of file");
  142. return 0;
  143. }
  144. line = 0;
  145. buffer = 0;
  146. lexerstate->getline (&line, 1, lexerstate->closure);
  147. if (!line)
  148. {
  149. grub_script_yyerror (parserstate, 0); /* XXX this could be for ^C case? */
  150. return 0;
  151. }
  152. len = grub_strlen (line);
  153. if (line[len - 1] == '\n')
  154. {
  155. buffer = yy_scan_string (line, lexerstate->yyscanner);
  156. }
  157. else
  158. {
  159. line2 = append_newline (line);
  160. if (line2)
  161. {
  162. buffer = yy_scan_string (line2, lexerstate->yyscanner);
  163. grub_free (line2);
  164. }
  165. }
  166. grub_free (line);
  167. if (!buffer)
  168. {
  169. grub_script_yyerror (parserstate, 0);
  170. return 0;
  171. }
  172. return 1;
  173. }
  174. struct grub_lexer_param *
  175. grub_script_lexer_init (struct grub_parser_param *parser, char *script,
  176. grub_reader_getline_t getline, void *closure)
  177. {
  178. int len;
  179. char *script2;
  180. YY_BUFFER_STATE buffer;
  181. struct grub_lexer_param *lexerstate;
  182. lexerstate = grub_zalloc (sizeof (*lexerstate));
  183. if (!lexerstate)
  184. return 0;
  185. lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE;
  186. lexerstate->text = grub_malloc (lexerstate->size);
  187. if (!lexerstate->text)
  188. {
  189. grub_free (lexerstate);
  190. return 0;
  191. }
  192. lexerstate->getline = getline; /* rest are all zeros already */
  193. lexerstate->closure = closure;
  194. if (yylex_init (&lexerstate->yyscanner))
  195. {
  196. grub_free (lexerstate->text);
  197. grub_free (lexerstate);
  198. return 0;
  199. }
  200. buffer = 0;
  201. script = script ? : "\n";
  202. len = grub_strlen (script);
  203. if (script[len - 1] == '\n')
  204. {
  205. buffer = yy_scan_string (script, lexerstate->yyscanner);
  206. }
  207. else
  208. {
  209. script2 = append_newline (script);
  210. if (script2)
  211. {
  212. buffer = yy_scan_string (script2, lexerstate->yyscanner);
  213. grub_free (script2);
  214. }
  215. }
  216. if (!buffer)
  217. {
  218. yylex_destroy (lexerstate->yyscanner);
  219. grub_free (lexerstate->yyscanner);
  220. grub_free (lexerstate->text);
  221. grub_free (lexerstate);
  222. return 0;
  223. }
  224. yyset_extra (parser, lexerstate->yyscanner);
  225. return lexerstate;
  226. }
  227. void
  228. grub_script_lexer_fini (struct grub_lexer_param *lexerstate)
  229. {
  230. if (!lexerstate)
  231. return;
  232. yylex_destroy (lexerstate->yyscanner);
  233. grub_free (lexerstate->recording);
  234. grub_free (lexerstate->text);
  235. grub_free (lexerstate);
  236. }
  237. int
  238. grub_script_yylex (union YYSTYPE *value,
  239. struct grub_parser_param *parserstate)
  240. {
  241. char *str;
  242. int token;
  243. grub_script_arg_type_t type;
  244. struct grub_lexer_param *lexerstate = parserstate->lexerstate;
  245. value->arg = 0;
  246. if (parserstate->err)
  247. return GRUB_PARSER_TOKEN_BAD;
  248. if (lexerstate->eof)
  249. return GRUB_PARSER_TOKEN_EOF;
  250. /*
  251. * Words with environment variables, like foo${bar}baz needs
  252. * multiple tokens to be merged into a single grub_script_arg. We
  253. * use two variables to achieve this: lexerstate->merge_start and
  254. * lexerstate->merge_end
  255. */
  256. lexerstate->merge_start = 0;
  257. lexerstate->merge_end = 0;
  258. do
  259. {
  260. /* Empty lexerstate->text. */
  261. lexerstate->used = 1;
  262. lexerstate->text[0] = '\0';
  263. token = yylex (value, lexerstate->yyscanner);
  264. if (token == GRUB_PARSER_TOKEN_BAD)
  265. break;
  266. /* Merging feature uses lexerstate->text instead of yytext. */
  267. if (lexerstate->merge_start)
  268. {
  269. str = lexerstate->text;
  270. type = lexerstate->type;
  271. }
  272. else
  273. {
  274. str = yyget_text (lexerstate->yyscanner);
  275. type = GRUB_SCRIPT_ARG_TYPE_TEXT;
  276. }
  277. grub_dprintf("lexer", "token %u text [%s]\n", token, str);
  278. value->arg = grub_script_arg_add (parserstate, value->arg, type, str);
  279. }
  280. while (lexerstate->merge_start && !lexerstate->merge_end);
  281. if (!value->arg || parserstate->err)
  282. return GRUB_PARSER_TOKEN_BAD;
  283. return token;
  284. }
  285. void
  286. grub_script_yyerror (struct grub_parser_param *state, char const *err)
  287. {
  288. if (err)
  289. grub_error (GRUB_ERR_INVALID_COMMAND, err);
  290. grub_print_error ();
  291. state->err++;
  292. }