lexer.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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 <config.h>
  20. #include <grub/parser.h>
  21. #include <grub/misc.h>
  22. #include <grub/mm.h>
  23. #include <grub/script_sh.h>
  24. #include <grub/i18n.h>
  25. #include <grub/safemath.h>
  26. #define yytext_ptr char *
  27. #include "grub_script.tab.h"
  28. #include "grub_script.yy.h"
  29. void
  30. grub_script_lexer_ref (struct grub_lexer_param *state)
  31. {
  32. state->refs++;
  33. }
  34. void
  35. grub_script_lexer_deref (struct grub_lexer_param *state)
  36. {
  37. state->refs--;
  38. }
  39. /* Start recording all characters passing through the lexer. */
  40. unsigned
  41. grub_script_lexer_record_start (struct grub_parser_param *parser)
  42. {
  43. struct grub_lexer_param *lexer = parser->lexerstate;
  44. lexer->record++;
  45. if (lexer->recording)
  46. return lexer->recordpos;
  47. lexer->recordpos = 0;
  48. lexer->recordlen = GRUB_LEXER_INITIAL_RECORD_SIZE;
  49. lexer->recording = grub_malloc (lexer->recordlen);
  50. if (!lexer->recording)
  51. {
  52. grub_script_yyerror (parser, 0);
  53. lexer->recordlen = 0;
  54. }
  55. return lexer->recordpos;
  56. }
  57. char *
  58. grub_script_lexer_record_stop (struct grub_parser_param *parser, unsigned offset)
  59. {
  60. int count;
  61. char *result;
  62. struct grub_lexer_param *lexer = parser->lexerstate;
  63. if (!lexer->record)
  64. return 0;
  65. lexer->record--;
  66. if (!lexer->recording)
  67. return 0;
  68. count = lexer->recordpos - offset;
  69. result = grub_script_malloc (parser, count + 1);
  70. if (result) {
  71. grub_strncpy (result, lexer->recording + offset, count);
  72. result[count] = '\0';
  73. }
  74. if (lexer->record == 0)
  75. {
  76. grub_free (lexer->recording);
  77. lexer->recording = 0;
  78. lexer->recordlen = 0;
  79. lexer->recordpos = 0;
  80. }
  81. return result;
  82. }
  83. /* Record STR if input recording is enabled. */
  84. void
  85. grub_script_lexer_record (struct grub_parser_param *parser, char *str)
  86. {
  87. int len;
  88. char *old;
  89. struct grub_lexer_param *lexer = parser->lexerstate;
  90. if (!lexer->record || !lexer->recording)
  91. return;
  92. len = grub_strlen (str);
  93. if (lexer->recordpos + len + 1 > lexer->recordlen)
  94. {
  95. old = lexer->recording;
  96. if (lexer->recordlen < len)
  97. lexer->recordlen = len;
  98. if (grub_mul (lexer->recordlen, 2, &lexer->recordlen))
  99. goto fail;
  100. lexer->recording = grub_realloc (lexer->recording, lexer->recordlen);
  101. if (!lexer->recording)
  102. {
  103. fail:
  104. grub_free (old);
  105. lexer->recordpos = 0;
  106. lexer->recordlen = 0;
  107. grub_script_yyerror (parser, 0);
  108. return;
  109. }
  110. }
  111. grub_strcpy (lexer->recording + lexer->recordpos, str);
  112. lexer->recordpos += len;
  113. }
  114. /* Read next line of input if necessary, and set yyscanner buffers. */
  115. int
  116. grub_script_lexer_yywrap (struct grub_parser_param *parserstate,
  117. const char *input)
  118. {
  119. grub_size_t len = 0, sz;
  120. char *p = 0;
  121. char *line = 0;
  122. YY_BUFFER_STATE buffer;
  123. struct grub_lexer_param *lexerstate = parserstate->lexerstate;
  124. if (! lexerstate->refs && ! lexerstate->prefix && ! input)
  125. return 1;
  126. if (! lexerstate->getline && ! input)
  127. {
  128. grub_script_yyerror (parserstate, N_("unexpected end of file"));
  129. return 1;
  130. }
  131. line = 0;
  132. if (! input)
  133. lexerstate->getline (&line, 1, lexerstate->getline_data);
  134. else
  135. line = grub_strdup (input);
  136. if (! line)
  137. {
  138. grub_script_yyerror (parserstate, N_("out of memory"));
  139. return 1;
  140. }
  141. len = grub_strlen (line);
  142. /* Ensure '\n' at the end. */
  143. if (line[0] == '\0')
  144. {
  145. grub_free (line);
  146. line = grub_strdup ("\n");
  147. len = 1;
  148. }
  149. else if (len && line[len - 1] != '\n')
  150. {
  151. if (grub_add (len, 2, &sz))
  152. {
  153. grub_free (line);
  154. grub_script_yyerror (parserstate, N_("overflow is detected"));
  155. return 1;
  156. }
  157. p = grub_realloc (line, sz);
  158. if (p)
  159. {
  160. p[len++] = '\n';
  161. p[len] = '\0';
  162. }
  163. else
  164. grub_free (line);
  165. line = p;
  166. }
  167. if (! line)
  168. {
  169. grub_script_yyerror (parserstate, N_("out of memory"));
  170. return 1;
  171. }
  172. /* Prepend any left over unput-text. */
  173. if (lexerstate->prefix)
  174. {
  175. int plen = grub_strlen (lexerstate->prefix);
  176. p = grub_malloc (len + plen + 1);
  177. if (! p)
  178. {
  179. grub_free (line);
  180. return 1;
  181. }
  182. grub_strcpy (p, lexerstate->prefix);
  183. lexerstate->prefix = 0;
  184. grub_strcpy (p + plen, line);
  185. grub_free (line);
  186. line = p;
  187. len = len + plen;
  188. }
  189. buffer = yy_scan_string (line, lexerstate->yyscanner);
  190. grub_free (line);
  191. if (! buffer)
  192. {
  193. grub_script_yyerror (parserstate, 0);
  194. return 1;
  195. }
  196. return 0;
  197. }
  198. struct grub_lexer_param *
  199. grub_script_lexer_init (struct grub_parser_param *parser, char *script,
  200. grub_reader_getline_t arg_getline, void *getline_data)
  201. {
  202. struct grub_lexer_param *lexerstate;
  203. lexerstate = grub_zalloc (sizeof (*lexerstate));
  204. if (!lexerstate)
  205. return 0;
  206. lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE;
  207. lexerstate->text = grub_malloc (lexerstate->size);
  208. if (!lexerstate->text)
  209. {
  210. grub_free (lexerstate);
  211. return 0;
  212. }
  213. lexerstate->getline = arg_getline;
  214. lexerstate->getline_data = getline_data;
  215. /* The other elements of lexerstate are all zeros already. */
  216. if (yylex_init (&lexerstate->yyscanner))
  217. {
  218. grub_free (lexerstate->text);
  219. grub_free (lexerstate);
  220. return 0;
  221. }
  222. yyset_extra (parser, lexerstate->yyscanner);
  223. parser->lexerstate = lexerstate;
  224. if (grub_script_lexer_yywrap (parser, script ?: "\n"))
  225. {
  226. parser->lexerstate = 0;
  227. yylex_destroy (lexerstate->yyscanner);
  228. grub_free (lexerstate->text);
  229. grub_free (lexerstate);
  230. return 0;
  231. }
  232. return lexerstate;
  233. }
  234. void
  235. grub_script_lexer_fini (struct grub_lexer_param *lexerstate)
  236. {
  237. if (!lexerstate)
  238. return;
  239. yylex_destroy (lexerstate->yyscanner);
  240. grub_free (lexerstate->recording);
  241. grub_free (lexerstate->text);
  242. grub_free (lexerstate);
  243. }
  244. int
  245. grub_script_yylex (union YYSTYPE *value,
  246. struct grub_parser_param *parserstate)
  247. {
  248. char *str;
  249. int token;
  250. grub_script_arg_type_t type;
  251. struct grub_lexer_param *lexerstate = parserstate->lexerstate;
  252. value->arg = 0;
  253. if (parserstate->err)
  254. return GRUB_PARSER_TOKEN_BAD;
  255. if (lexerstate->eof)
  256. return GRUB_PARSER_TOKEN_EOF;
  257. /*
  258. * Words with environment variables, like foo${bar}baz needs
  259. * multiple tokens to be merged into a single grub_script_arg. We
  260. * use two variables to achieve this: lexerstate->merge_start and
  261. * lexerstate->merge_end
  262. */
  263. lexerstate->merge_start = 0;
  264. lexerstate->merge_end = 0;
  265. do
  266. {
  267. /* Empty lexerstate->text. */
  268. lexerstate->used = 1;
  269. lexerstate->text[0] = '\0';
  270. token = yylex (value, lexerstate->yyscanner);
  271. if (token == GRUB_PARSER_TOKEN_BAD)
  272. break;
  273. /* Merging feature uses lexerstate->text instead of yytext. */
  274. if (lexerstate->merge_start)
  275. {
  276. str = lexerstate->text;
  277. type = lexerstate->type;
  278. }
  279. else
  280. {
  281. str = yyget_text (lexerstate->yyscanner);
  282. type = GRUB_SCRIPT_ARG_TYPE_TEXT;
  283. }
  284. grub_dprintf("lexer", "token %u text [%s]\n", token, str);
  285. value->arg = grub_script_arg_add (parserstate, value->arg, type, str);
  286. }
  287. while (lexerstate->merge_start && !lexerstate->merge_end);
  288. if (!value->arg || parserstate->err)
  289. return GRUB_PARSER_TOKEN_BAD;
  290. return token;
  291. }
  292. void
  293. grub_script_yyerror (struct grub_parser_param *state, const char *err)
  294. {
  295. if (err)
  296. grub_error (GRUB_ERR_INVALID_COMMAND, "%s", err);
  297. grub_print_error ();
  298. state->err++;
  299. }