lexer.c 7.7 KB

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