[Libguestfs] [PATCH 1/3] builder: make the C index parser reentrant

Pino Toscano ptoscano at redhat.com
Wed Mar 19 16:53:09 UTC 2014


Switch the lex/yacc parser into reentrant mode, to ease the handling of
parsing-specific data; introduce a new parser_context struct for that,
which is added as extra data to the parser.

This should cause no behaviour changes in the parsing, just no more
global variables used for getting data in/out the parser.
---
 builder/index-parse.y    | 44 ++++++++++++++++++++++++++++++++++++------
 builder/index-parser-c.c | 24 +++++++++++++----------
 builder/index-scan.l     | 50 +++++++++++++++++++++++++++++++++---------------
 builder/index-struct.c   | 14 +++++++++-----
 builder/index-struct.h   | 19 +++++++++++-------
 builder/index-validate.c | 20 +++++++++++--------
 6 files changed, 120 insertions(+), 51 deletions(-)

diff --git a/builder/index-parse.y b/builder/index-parse.y
index a8d2f62..dee2aec 100644
--- a/builder/index-parse.y
+++ b/builder/index-parse.y
@@ -24,9 +24,14 @@
 #include <string.h>
 
 #include "index-struct.h"
+#include "index-parse.h"
 
-extern void yyerror (const char *);
-extern int yylex (void);
+#define YY_EXTRA_TYPE struct parse_context *
+
+extern void yyerror (YYLTYPE * yylloc, yyscan_t scanner, struct parse_context *context, const char *msg);
+extern int yylex (YYSTYPE * yylval, YYLTYPE * yylloc, yyscan_t scanner);
+extern void scanner_init (yyscan_t *scanner, struct parse_context *context, FILE *in);
+extern void scanner_destroy (yyscan_t scanner);
 
 /* Join two strings with \n */
 static char *
@@ -52,6 +57,14 @@ concat_newline (const char *str1, const char *str2)
 
 %}
 
+%code requires {
+#include "index-parse.h"
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+#endif
+}
+
 %locations
 
 %union {
@@ -71,13 +84,19 @@ concat_newline (const char *str1, const char *str2)
 %type <field>   fields field
 %type <str>     continuations
 
+%pure-parser
+
+%lex-param   { yyscan_t scanner }
+%parse-param { yyscan_t scanner }
+%parse-param { struct parse_context *context }
+
 %%
 
 index:
       sections
-        { parsed_index = $1; }
+        { context->parsed_index = $1; }
     | PGP_PROLOGUE sections PGP_EPILOGUE
-        { parsed_index = $2; }
+        { context->parsed_index = $2; }
 
 sections:
       section emptylines
@@ -122,8 +141,21 @@ emptylines:
 %%
 
 void
-yyerror (const char *msg)
+yyerror (YYLTYPE * yylloc, yyscan_t scanner, struct parse_context *context, const char *msg)
 {
   fprintf (stderr, "syntax error at line %d: %s\n",
-           yylloc.first_line, msg);
+           yylloc->first_line, msg);
+}
+
+int
+do_parse (struct parse_context *context, FILE *in)
+{
+  yyscan_t scanner;
+  int res;
+
+  scanner_init (&scanner, context, in);
+  res = yyparse (scanner, context);
+  scanner_destroy (scanner);
+
+  return res;
 }
diff --git a/builder/index-parser-c.c b/builder/index-parser-c.c
index fbbebff..7aeb6d0 100644
--- a/builder/index-parser-c.c
+++ b/builder/index-parser-c.c
@@ -43,7 +43,7 @@ extern void unix_error (int errcode, char * cmdname, value arg) Noreturn;
 #include "index-struct.h"
 #include "index-parse.h"
 
-extern FILE *yyin;
+extern int do_parse (struct parse_context *context, FILE *in);
 
 value
 virt_builder_parse_index (value filenamev)
@@ -52,26 +52,30 @@ virt_builder_parse_index (value filenamev)
   CAMLlocal5 (rv, v, sv, sv2, fv);
   struct section *sections;
   size_t i, nr_sections;
+  struct parse_context context;
+  FILE *in;
 
-  yyin = fopen (String_val (filenamev), "r");
-  if (yyin == NULL)
+  parse_context_init (&context);
+
+  in = fopen (String_val (filenamev), "r");
+  if (in == NULL)
     unix_error (errno, (char *) "fopen", filenamev);
 
-  if (yyparse () != 0) {
-    fclose (yyin);
+  if (do_parse (&context, in) != 0) {
+    fclose (in);
     caml_invalid_argument ("parse error");
   }
 
-  if (fclose (yyin) == EOF)
+  if (fclose (in) == EOF)
     unix_error (errno, (char *) "fclose", filenamev);
 
   /* Convert the parsed data to OCaml structures. */
   nr_sections = 0;
-  for (sections = parsed_index; sections != NULL; sections = sections->next)
+  for (sections = context.parsed_index; sections != NULL; sections = sections->next)
     nr_sections++;
   rv = caml_alloc (nr_sections, 0);
 
-  for (i = 0, sections = parsed_index; sections != NULL;
+  for (i = 0, sections = context.parsed_index; sections != NULL;
        i++, sections = sections->next) {
     struct field *fields;
     size_t j, nr_fields;
@@ -105,8 +109,8 @@ virt_builder_parse_index (value filenamev)
     Store_field (rv, i, v);     /* assign to return array of sections */
   }
 
-  /* Free parsed global data. */
-  free_index ();
+  /* Free parsed data. */
+  parse_context_free (&context);
 
   CAMLreturn (rv);
 }
diff --git a/builder/index-scan.l b/builder/index-scan.l
index 832ea51..073d85f 100644
--- a/builder/index-scan.l
+++ b/builder/index-scan.l
@@ -23,18 +23,22 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "index-parse.h"
 #include "index-struct.h"
+#include "index-parse.h"
 
-#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
+#define YY_EXTRA_TYPE struct parse_context *
+#define YY_USER_ACTION yylloc->first_line = yylloc->last_line = yylineno;
 
-extern void yyerror (const char *);
+extern void yyerror (YYLTYPE * yylloc, yyscan_t scanner, struct parse_context *context, const char *msg);
 
 %}
 
 %option nounput
 %option noyywrap
 %option yylineno
+%option reentrant
+%option bison-bridge
+%option bison-locations
 
 %%
 
@@ -46,38 +50,38 @@ extern void yyerror (const char *);
   */
 
   /* Ignore comments - '#' MUST appear at the start of a line. */
-^"#".*\n                { seen_comments++; }
+^"#".*\n                { yyextra->seen_comments++; }
 
   /* An empty line is significant. */
 ^\n                                     { return EMPTY_LINE; }
 
   /* [...] marks beginning of a section. */
 ^"["[-A-Za-z0-9._]+"]"\n {
-                      yylval.str = strndup (yytext+1, yyleng-3);
+                      yylval->str = strndup (yytext+1, yyleng-3);
                       return SECTION_HEADER;
                     }
 
   /* field=value or field[subfield]=value */
 ^[A-Za-z0-9_.]+("["[A-Za-z0-9_,.]+"]")?"=".*\n {
                       size_t i = strcspn (yytext, "=[");
-                      yylval.field = malloc (sizeof (struct field));
-                      yylval.field->next = NULL;
-                      yylval.field->key = strndup (yytext, i);
+                      yylval->field = malloc (sizeof (struct field));
+                      yylval->field->next = NULL;
+                      yylval->field->key = strndup (yytext, i);
                       if (yytext[i] == '[') {
                         size_t j = strcspn (yytext+i+1, "]");
-                        yylval.field->subkey = strndup (yytext+i+1, j);
+                        yylval->field->subkey = strndup (yytext+i+1, j);
                         i += 1+j+1;
                       } else {
-                        yylval.field->subkey = NULL;
+                        yylval->field->subkey = NULL;
                       }
                       /* Note we chop the final \n off here. */
-                      yylval.field->value = strndup (yytext+i+1, yyleng-(i+2));
+                      yylval->field->value = strndup (yytext+i+1, yyleng-(i+2));
                       return FIELD;
                     }
 
   /* Continuation line for multi-line values. */
 ^[[:blank:]].*\n        {
-                      yylval.str = strndup (yytext+1, yyleng-2);
+                      yylval->str = strndup (yytext+1, yyleng-2);
                       return VALUE_CONT;
                     }
 
@@ -86,7 +90,7 @@ extern void yyerror (const char *);
   int c, prevnl = 0;
 
   /* Eat everything to the first blank line. */
-  while ((c = input ()) != EOF) {
+  while ((c = input (yyscanner)) != EOF) {
     if (c == '\n' && prevnl)
       break;
     prevnl = c == '\n';
@@ -98,7 +102,7 @@ extern void yyerror (const char *);
  /* Hack to eat the PGP epilogue. */
 ^"-----BEGIN PGP SIGNATURE-----\n"  {
   /* Eat everything to the end of the file. */
-  while (input () != EOF)
+  while (input (yyscanner) != EOF)
     ;
 
   return PGP_EPILOGUE;
@@ -106,6 +110,22 @@ extern void yyerror (const char *);
 
  /* anything else is an error */
 . {
-  yyerror ("unexpected character in input");
+  yyerror (yylloc, yyscanner, yyextra, "unexpected character in input");
   exit (EXIT_FAILURE);
 }
+
+%%
+
+void
+scanner_init (yyscan_t *scanner, struct parse_context *context, FILE *in)
+{
+  yylex_init (scanner);
+  yyset_extra (context, *scanner);
+  yyset_in (in, *scanner);
+}
+
+void
+scanner_destroy (yyscan_t scanner)
+{
+  yylex_destroy (scanner);
+}
diff --git a/builder/index-struct.c b/builder/index-struct.c
index fe5b0e3..f32534c 100644
--- a/builder/index-struct.c
+++ b/builder/index-struct.c
@@ -20,19 +20,23 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "index-struct.h"
 
-struct section *parsed_index = NULL;
-int seen_comments = 0;
-
 static void free_section (struct section *section);
 static void free_field (struct field *field);
 
 void
-free_index (void)
+parse_context_init (struct parse_context *context)
+{
+  memset (context, 0, sizeof *context);
+}
+
+void
+parse_context_free (struct parse_context *context)
 {
-  free_section (parsed_index);
+  free_section (context->parsed_index);
 }
 
 static void
diff --git a/builder/index-struct.h b/builder/index-struct.h
index f92e01d..9480526 100644
--- a/builder/index-struct.h
+++ b/builder/index-struct.h
@@ -36,14 +36,19 @@ struct field {
   char *value;
 };
 
-/* The parser (yyparse) stores the result here. */
-extern struct section *parsed_index;
+/* A struct holding the data needed during the parsing. */
+struct parse_context {
+  struct section *parsed_index;        /* The result of the parsing. */
+  /* yyparse sets this if any comments were seen.  Required for checking
+   * compatibility with virt-builder 1.24.
+   */
+  int seen_comments;
+};
 
-/* yyparse sets this if any comments were seen.  Required for checking
- * compatibility with virt-builder 1.24.
- */
-extern int seen_comments;
+/* Initialize the content of a parse_context. */
+extern void parse_context_init (struct parse_context *state);
 
-extern void free_index (void);
+/* Free the content of a parse_context.  The actual pointer is not freed. */
+extern void parse_context_free (struct parse_context *state);
 
 #endif /* INDEX_STRUCT_H */
diff --git a/builder/index-validate.c b/builder/index-validate.c
index 7f02ffb..4b7fe93 100644
--- a/builder/index-validate.c
+++ b/builder/index-validate.c
@@ -33,7 +33,7 @@
 #include "index-struct.h"
 #include "index-parse.h"
 
-extern FILE *yyin;
+extern int do_parse (struct parse_context *context, FILE *in);
 
 static void
 usage (int exit_status)
@@ -60,11 +60,15 @@ main (int argc, char *argv[])
   int compat_1_24_1 = 0;
   const char *input;
   struct section *sections;
+  struct parse_context context;
+  FILE *in;
 
   setlocale (LC_ALL, "");
   bindtextdomain (PACKAGE, LOCALEBASEDIR);
   textdomain (PACKAGE);
 
+  parse_context_init (&context);
+
   for (;;) {
     c = getopt_long (argc, argv, options, long_options, &option_index);
     if (c == -1) break;
@@ -99,32 +103,32 @@ main (int argc, char *argv[])
 
   input = argv[optind++];
 
-  yyin = fopen (input, "r");
-  if (yyin == NULL) {
+  in = fopen (input, "r");
+  if (in == NULL) {
     perror (input);
     exit (EXIT_FAILURE);
   }
 
-  if (yyparse () != 0) {
+  if (do_parse (&context, in) != 0) {
     fprintf (stderr, _("%s: '%s' could not be validated, see errors above\n"),
              program_name, input);
     exit (EXIT_FAILURE);
   }
 
-  if (fclose (yyin) == EOF) {
+  if (fclose (in) == EOF) {
     fprintf (stderr, _("%s: %s: error reading input file: %m\n"),
              program_name, input);
     exit (EXIT_FAILURE);
   }
 
-  if (compat_1_24_1 && seen_comments) {
+  if (compat_1_24_1 && context.seen_comments) {
     fprintf (stderr, _("%s: %s contains comments which will not work with virt-builder 1.24.1\n"),
              program_name, input);
     exit (EXIT_FAILURE);
   }
 
   /* Iterate over the parsed sections, semantically validating it. */
-  for (sections = parsed_index; sections != NULL; sections = sections->next) {
+  for (sections = context.parsed_index; sections != NULL; sections = sections->next) {
     int seen_sig = 0;
     struct field *fields;
 
@@ -165,7 +169,7 @@ main (int argc, char *argv[])
   }
 
   /* Free the parsed data. */
-  free_index ();
+  parse_context_free (&context);
 
   printf ("%s validated OK\n", input);
 
-- 
1.8.3.1




More information about the Libguestfs mailing list