diff --git a/src/files.c b/src/files.c
index 0be0750..0393c24 100644
--- a/src/files.c
+++ b/src/files.c
@@ -65,6 +65,9 @@ static int generated_files_size = 0;

 uniqstr grammar_file = NULL;
 uniqstr current_file = NULL;
+uniqstr *include_dirs = NULL;
+int include_dirs_size = 0;
+int includes_used = 0;

 /* If --output=dir/foo.c was specified,
    DIR_PREFIX is 'dir/' and ALL_BUT_EXT and ALL_BUT_TAB_EXT are 'dir/foo'.
@@ -96,7 +99,7 @@ static char *header_extension = NULL;
 | STR1, and STR2.                                                  |
 `-----------------------------------------------------------------*/

-static char *
+char *
 concat2 (char const *str1, char const *str2)
 {
   size_t len = strlen (str1) + strlen (str2);
diff --git a/src/files.h b/src/files.h
index 9970e88..3e33212 100644
--- a/src/files.h
+++ b/src/files.h
@@ -58,6 +58,9 @@ extern uniqstr grammar_file;
 /* The current file name.  Might change with %include, or with #line.  */
 extern uniqstr current_file;

+/* Show whether %include statements are present. */
+extern int includes_used;
+
 /* The computed base for output file names.  */
 extern char *all_but_ext;

@@ -79,4 +82,8 @@ FILE *xfopen (const char *name, char const *mode);
 void xfclose (FILE *ptr);
 FILE *xfdopen (int fd, char const *mode);

+/* Provided include dirs. */
+extern uniqstr *include_dirs;
+extern int include_dirs_size;
+
 #endif /* !FILES_H_ */
diff --git a/src/getargs.c b/src/getargs.c
index 228fc53..63323fc 100644
--- a/src/getargs.c
+++ b/src/getargs.c
@@ -290,6 +291,8 @@ Operation modes:\n\
 Parser:\n\
   -L, --language=LANGUAGE          specify the output programming language\n\
   -S, --skeleton=FILE              specify the skeleton to use\n\
+  -I, --include=DIR                specify include directory for\n\
+                                   %include directives\n\
   -t, --debug                      instrument the parser for tracing\n\
                                    same as '-Dparse.trace'\n\
       --locations                  enable location support\n\
@@ -456,6 +459,7 @@ language_argmatch (char const *arg, int prio, location loc)
 static char const short_options[] =
   "D:"
   "F:"
+  "I:"
   "L:"
   "S:"
   "T::"
@@ -498,6 +502,9 @@ static struct option const long_options[] =
   /* Parser. */
   { "name-prefix",   required_argument,   0,   'p' },

+  /* Input. */
+  { "include",     required_argument,   0,   'I' },
+
   /* Output. */
   { "file-prefix", required_argument,   0,   'b' },
   { "output",      required_argument,   0,   'o' },
@@ -552,7 +559,6 @@ command_line_location (void)
   return res;
 }

-
 void
 getargs (int argc, char *argv[])
 {
@@ -599,6 +605,17 @@ getargs (int argc, char *argv[])
         }
         break;

+      case 'I':
+          if (include_dirs_size == 0)
+          {
+              include_dirs = xmalloc(sizeof(uniqstr));
+              include_dirs_size++;
+          }
+          else
+              include_dirs = xnrealloc(include_dirs, ++include_dirs_size, sizeof(uniqstr));
+          include_dirs[include_dirs_size - 1] = optarg;
+          break;
+
       case 'L':
         language_argmatch (optarg, command_line_prio,
                            command_line_location ());
diff --git a/src/location.c b/src/location.c
index 88fd608..0d57d37 100644
--- a/src/location.c
+++ b/src/location.c
@@ -27,6 +27,7 @@
 #include "location.h"

 location const empty_location = EMPTY_LOCATION_INIT;
+extern int includes_used;

 /* If BUF is null, add BUFSIZE (which in this case must be less than
    INT_MAX) to COLUMN; otherwise, add mbsnwidth (BUF, BUFSIZE, 0) to
@@ -163,6 +164,8 @@ location_caret (location loc, FILE *out)
 {
   /* FIXME: find a way to support multifile locations, and only open once each
      file. That would make the procedure future-proof.  */
+  if (includes_used)
+      return;
   if (! (caret_info.source
          || (caret_info.source = fopen (loc.start.file, "r")))
       || loc.start.column == -1 || loc.start.line == -1)
diff --git a/src/parse-gram.y b/src/parse-gram.y
index 3fb6b19..c646830 100644
--- a/src/parse-gram.y
+++ b/src/parse-gram.y
@@ -88,6 +88,12 @@
   /* A string that describes a char (e.g., 'a' -> "'a'").  */
   static char const *char_name (char);

+  /* indicates how many EOFS we expect */
+  int include_depth = 0;
+
+  /* include files stack */
+  uniqstr *current_file_stack = 0;
+
   #define YYTYPE_INT16 int_fast16_t
   #define YYTYPE_INT8 int_fast8_t
   #define YYTYPE_UINT16 uint_fast16_t
@@ -147,6 +153,7 @@
   PERCENT_FLAG            "%<flag>"
   PERCENT_FILE_PREFIX     "%file-prefix"
   PERCENT_GLR_PARSER      "%glr-parser"
+  PERCENT_INCLUDE         "%include"
   PERCENT_INITIAL_ACTION  "%initial-action"
   PERCENT_LANGUAGE        "%language"
   PERCENT_NAME_PREFIX     "%name-prefix"
@@ -276,6 +283,7 @@ prologue_declarations:

 prologue_declaration:
   grammar_declaration
+| include
 | "%{...%}"
     {
       muscle_code_grow (union_seen ? "post_prologue" : "pre_prologue",
@@ -582,6 +590,7 @@ grammar:
    body of the grammar.  */
 rules_or_grammar_declaration:
   rules
+| include
 | grammar_declaration ";"
 | error ";"
     {
@@ -629,6 +638,64 @@ named_ref.opt:
 | BRACKETED_ID   { $$ = named_ref_new ($1, @1); }
 ;

+/*---------------------.
+|      %includes       |
+`---------------------*/
+
+include:
+  "%include" STRING
+    {
+      includes_used = 1;
+
+      const char *fn = $2;
+      FILE *f = fopen(fn, "r");
+      if (!f)
+        {
+          for (int i = 0; i < include_dirs_size; i++)
+          {
+            const char *fn2 = concat2(include_dirs[i], fn);
+            f = fopen(fn2, "r");
+            if (f)
+            {
+              fn = fn2;
+              break;
+            }
+          }
+          if (!f)
+          {
+            yyerror(&yylloc, YY_("cannot open a file"));
+            YYERROR;
+          }
+        }
+
+      if (include_depth == 0)
+        current_file_stack = xmalloc(sizeof(uniqstr));
+      else
+        current_file_stack = xnrealloc(current_file_stack, include_depth + 1, sizeof(uniqstr));
+
+      current_file = fn;
+      current_file_stack[include_depth] = fn;
+
+      void *b = gram__create_buffer(f, 16 * 1024);
+      gram_push_buffer_state(b);
+      include_depth++;
+    }
+| GRAM_EOF
+    {
+      if (include_depth == 0)
+        {
+          yyerror (&yylloc, YY_("syntax error, unexpected end of file"));
+          YYERROR;
+        }
+      gram_pop_buffer_state();
+      include_depth--;
+      current_file = current_file_stack[include_depth];
+
+      if (include_depth != 0)
+        free(current_file_stack[include_depth + 1]);
+    }
+;
+
 /*---------------------.
 | variable and value.  |
 `---------------------*/
@@ -707,8 +774,8 @@ string_as_id:
 ;

 epilogue.opt:
-  %empty
-| "%%" EPILOGUE
+  //%empty |
+  "%%" EPILOGUE
     {
       muscle_code_grow ("epilogue", translate_code ($2, @2, true), @2);
       code_scanner_last_string_free ();
diff --git a/src/scan-gram.l b/src/scan-gram.l
index 2b2a7f2..04c2844 100644
--- a/src/scan-gram.l
+++ b/src/scan-gram.l
@@ -227,8 +227,9 @@ eqopt    ([[:space:]]*=)?
   "%expect-rr"                      return PERCENT_EXPECT_RR;
   "%file-prefix"                    return PERCENT_FILE_PREFIX;
   "%fixed-output-files"             return PERCENT_YACC;
-  "%initial-action"                 return PERCENT_INITIAL_ACTION;
   "%glr-parser"                     return PERCENT_GLR_PARSER;
+  "%include"                        return PERCENT_INCLUDE;
+  "%initial-action"                 return PERCENT_INITIAL_ACTION;
   "%language"                       return PERCENT_LANGUAGE;
   "%left"                           return PERCENT_LEFT;
   "%lex-param"                      RETURN_PERCENT_PARAM(lex);
