Working parser for rpcgen files.
authorrjones <rjones>
Wed, 7 Jan 2009 13:09:11 +0000 (13:09 +0000)
committerrjones <rjones>
Wed, 7 Jan 2009 13:09:11 +0000 (13:09 +0000)
.cvsignore
Makefile.am
configure.ac
rpcgen_int.h [new file with mode: 0644]
rpcgen_main.c [new file with mode: 0644]
rpcgen_parse.y [new file with mode: 0644]
rpcgen_scan.l [new file with mode: 0644]

index 113a38c..3719ab1 100644 (file)
@@ -20,4 +20,9 @@ missing
 config.guess
 config.sub
 depcomp
-*.loT
\ No newline at end of file
+*.loT
+portable-rpcgen
+remote_protocol.x
+rpcgen_parse.c
+rpcgen_parse.h
+rpcgen_scan.c
index cc79d5f..fb0fcb3 100644 (file)
@@ -7,3 +7,13 @@ libportablexdr_la_SOURCES = xdr_array.c xdr_float.c xdr_mem.c xdr_reference.c \
        rpc/rpc.h rpc/types.h rpc/xdr.h
 libportablexdr_la_CFLAGS = -Wall -Werror -fno-strict-aliasing
 libportablexdr_la_LDFLAGS = @MINGW_EXTRA_LDFLAGS@
+
+# Replacement 'rpcgen'.  Don't call it 'rpcgen' however so that
+# we can avoid conflicting with the system rpcgen.
+BUILT_SOURCES = rpcgen_parse.h
+AM_YFLAGS = -d
+bin_PROGRAMS = portable-rpcgen
+portable_rpcgen_SOURCES = rpcgen_parse.y rpcgen_scan.l \
+       rpcgen_int.h rpcgen_main.c
+portable_rpcgen_CFLAGS = -Wall
+#portable_rpcgen_CFLAGS += -DYYDEBUG
index f82f4fe..95d2f2b 100644 (file)
@@ -1,23 +1,37 @@
-AC_INIT([portablexdr], [4.0.11])
+AC_INIT(portablexdr, 5.0.0)
 AM_INIT_AUTOMAKE
 
 AC_CONFIG_HEADERS([config.h])
 
 AC_CANONICAL_HOST
 AC_PROG_CC
-AC_CHECK_PROGS([AR], [ar])
+AC_CHECK_PROGS(AR, ar)
 AC_PROG_INSTALL
 AC_PROG_LIBTOOL
 
+AC_PROG_LEX
+AC_PROG_YACC
+
+dnl Look for an external 'cpp' program which can run on a file with any
+dnl extension.  The normal CPP can only run on files with a '.c'
+dnl extension, therefore we prefer to use /usr/bin/cpp if it exists.
+AC_PATH_PROG([EXTCPP], [cpp], [no])
+if test "x$EXTCPP" = "xno"; then
+  AC_MSG_FAILURE([Cannot find a working 'cpp' (C preprocessor) program])
+fi
+AC_DEFINE_UNQUOTED([EXTCPP], ["$EXTCPP"],
+       [The C preprocessor command (may include command line options).])
+
 MINGW_EXTRA_LDFLAGS=
 case "$host" in
  *-*-mingw*)
    MINGW_EXTRA_LDFLAGS="-no-undefined"
    ;;
 esac
-AC_SUBST([MINGW_EXTRA_LDFLAGS])
+AC_SUBST(MINGW_EXTRA_LDFLAGS)
 
 AC_CHECK_HEADERS([arpa/inet.h sys/param.h])
 AC_CHECK_FUNCS([ntohl htonl ntohs htons])
 
-AC_OUTPUT([Makefile])
+AC_OUTPUT(Makefile)
+
diff --git a/rpcgen_int.h b/rpcgen_int.h
new file mode 100644 (file)
index 0000000..e372295
--- /dev/null
@@ -0,0 +1,37 @@
+/* -*- C -*-
+ * rpcgen - Generate XDR bindings automatically.
+ * Copyright (C) 2008 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef RPCGEN_INT_H
+#define RPCGEN_INT_H
+
+extern char *input_filename;
+
+/* Global functions used by the scanner. */
+extern void start_string (void);
+extern char *end_string (void);
+extern void add_char (int);
+extern void add_string (const char *);
+
+/* These functions print an error and then exit. */
+extern void error (const char *, ...)
+  __attribute__((noreturn, format(printf,1,2)));
+extern void perrorf (const char *, ...)
+  __attribute__((noreturn, format(printf,1,2)));
+
+#endif /* RPCGEN_INT_H */
diff --git a/rpcgen_main.c b/rpcgen_main.c
new file mode 100644 (file)
index 0000000..6ad467f
--- /dev/null
@@ -0,0 +1,309 @@
+/* -*- C -*-
+ * rpcgen - Generate XDR bindings automatically.
+ * Copyright (C) 2008 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "rpcgen_int.h"
+
+static void print_version (void);
+static void usage (const char *progname);
+static void do_rpcgen (const char *filename);
+static char *make_cpp_command (const char *filename);
+
+/* Symbols exported from the scanner. */
+extern FILE *yyin, *yyout;
+extern int yyparse (void);
+extern int yylineno;
+extern int yydebug;
+
+int
+main (int argc, char *argv[])
+{
+  int opt;
+
+  /* To enable debugging in the parser, you also need to compile
+   * with -DYYDEBUG
+   */
+#if 0
+  yydebug = 1;
+#endif
+
+  /* Note on command line arguments: We only support a small subset
+   * of command line arguments, because of the reduced functionality
+   * available in this version of rpcgen.  However we accept the
+   * command line parameters from both GNU rpcgen and BSD rpcgen
+   * and print appropriate errors for any we don't understand.
+   */
+  while ((opt = getopt (argc, argv, "AD:IK:LMSTVchlmno:s:t")) != -1) {
+    switch (opt)
+      {
+       /*-- Options supported by any rpcgen that we don't support. --*/
+      case 'D': case 'T': case 'K': case 'l': case 'm': case 't':
+      case 's':
+       error ("option '%c' is not supported by this PortableXDR rpcgen.\nYou may need to use an alternative rpcgen program instead.", opt);
+
+       /*-- Options supported only by GNU rpcgen that we don't support. --*/
+      case 'I': case 'n':
+       error ("option '%c' is not supported by this PortableXDR rpcgen.\nIf you were expecting to use GNU rpcgen, try /usr/bin/rpcgen on a GNU host.", opt);
+
+       /*-- Options supported only by BSD rpcgen that we don't support. --*/
+      case 'A': case 'M': case 'L': case 'S':
+       error ("option '%c' is not supported by this PortableXDR rpcgen.\nIf you were expecting to use BSD rpcgen, try /usr/bin/rpcgen on a BSD host.", opt);
+
+       /*-- Options that we do support. --*/
+      case 'c':
+      case 'h':
+      case 'o':
+       ;
+
+       /* None of the other versions of rpcgen support a way to print
+        * the version number, which is extremely annoying because
+        * there are so many different variations of rpcgen around.
+        * So this option at least should be useful!
+        */
+      case 'V':
+       print_version ();
+        exit (0);
+
+       /*-- Usage case. --*/
+      default:
+       usage (argv[0]);
+       exit (1);
+      }
+  }
+
+  if (optind >= argc)
+    error ("expected name of input file after options");
+
+  while (optind < argc)
+    do_rpcgen (argv[optind++]);
+
+  exit (0);
+}
+
+static void
+print_version (void)
+{
+  printf ("PortableXDR rpcgen %s\n", PACKAGE_VERSION);
+}
+
+static void
+usage (const char *progname)
+{
+  print_version ();
+  printf
+    ("Generate XDR bindings automatically.\n"
+     "\n"
+     "Usage:\n"
+     "  portable-rpcgen infile.x\n"
+     "  portable-rpcgen -c|-h [-o outfile] infile.x\n"
+     "  portable-rpcgen -V\n"
+     "\n"
+     "Options:\n"
+     "  -c     Generate C output file only.\n"
+     "  -h     Generate header output file only.\n"
+     "  -o     Name of output file (normally it is 'infile.[ch]').\n"
+     "  -V     Print the version and exit.\n"
+     "\n"
+     "In the first form, without -c or -h, we generate both output files.\n"
+     "\n"
+     "You can also list more than one input file on the command line, in\n"
+     "which case each input file is processed separately.\n"
+     "\n"
+     );
+  exit (0);
+}
+
+/* Called for each input file. */
+static void
+do_rpcgen (const char *filename)
+{
+  char *cmd;
+  int r;
+
+  free (input_filename);
+  input_filename = NULL;
+
+  cmd = make_cpp_command (filename);
+
+  yyin = popen (cmd, "r");
+  if (yyin == NULL)
+    perrorf ("%s", cmd);
+  free (cmd);
+
+  yyout = stdout;
+
+  /* Parse the input file.  This either succeeds or exits with an error. */
+  r = yyparse ();
+  pclose (yyin);
+
+  if (r == 1)
+    error ("parsing failed, file is not a valid rpcgen input");
+  else if (r == 2)
+    error ("parsing failed because we ran out of memory");
+
+  free (input_filename);
+  input_filename = NULL;
+}
+
+/* Concatenate $EXTCPP and filename, and make sure the filename is
+ * quoted correctly.  Tedious.
+ */
+static char *
+make_cpp_command (const char *filename)
+{
+  static const char good[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.";
+#define is_good(c) (strchr (good, (c)) != NULL)
+  const char *p;
+
+  /* We can use start_string etc. because this function is only used
+   * outside the scanner.
+   */
+  start_string ();
+  add_string (EXTCPP);
+  add_char (' ');
+
+  for (p = filename; *p; p++) {
+    if (is_good (*p)) add_char (*p);
+    else {
+      add_char ('\\');
+      add_char (*p);
+    }
+  }
+
+  return end_string ();
+}
+
+/* Some fairly random functions which are used by the scanner for
+ * constructing strings, reporting errors, etc.
+ */
+
+/* The scanner sets this to the name of the input file (from cpp)
+ * if known.
+ */
+char *input_filename = NULL;
+
+static char *str = NULL;
+static int str_used, str_alloc;
+
+void
+start_string (void)
+{
+  if (str != NULL)
+    error ("scanner called start_string without calling end_string");
+
+  str_alloc = 128;
+  str_used = 0;
+  str = malloc (str_alloc);
+  if (!str) perrorf ("malloc");
+}
+
+char *
+end_string (void)
+{
+  char *s;
+
+  if (str == NULL)
+    error ("scanner called end_string without calling start_string");
+
+  s = realloc (str, str_used+1);
+  if (!s) perrorf ("realloc");
+
+  str = NULL;
+  s[str_used] = '\0';
+  return s;
+}
+
+void
+add_char (int c)
+{
+  str_used++;
+  while (str_used >= str_alloc) {
+    str_alloc <<= 1;
+    str = realloc (str, str_alloc);
+    if (!str) perrorf ("realloc");
+  }
+  str[str_used-1] = c;
+}
+
+void
+add_string (const char *s)
+{
+  int i = str_used;
+  int len = strlen (s);
+
+  str_used += len;
+  while (str_used >= str_alloc) {
+    str_alloc <<= 1;
+    str = realloc (str, str_alloc);
+    if (!str) perrorf ("realloc");
+  }
+  memcpy (str+i, s, len);
+}
+
+void
+error (const char *fs, ...)
+{
+  va_list arg;
+
+  if (input_filename == NULL)
+    fputs (PACKAGE, stderr);
+  else
+    fprintf (stderr, "%s:%d", input_filename, yylineno);
+  fputs (": ", stderr);
+
+  va_start (arg, fs);
+  vfprintf (stderr, fs, arg);
+  va_end (arg);
+
+  fputc ('\n', stderr);
+
+  exit (1);
+}
+
+void
+perrorf (const char *fs, ...)
+{
+  va_list arg;
+  int e = errno;
+
+  if (input_filename == NULL)
+    fputs (PACKAGE, stderr);
+  else
+    fprintf (stderr, "%s:%d", input_filename, yylineno);
+  fputs (": ", stderr);
+
+  va_start (arg, fs);
+  vfprintf (stderr, fs, arg);
+  va_end (arg);
+
+  fputs (": ", stderr);
+  errno = e;
+  perror (NULL);
+
+  exit (1);
+}
diff --git a/rpcgen_parse.y b/rpcgen_parse.y
new file mode 100644 (file)
index 0000000..04b073a
--- /dev/null
@@ -0,0 +1,170 @@
+/* rpcgen - Generate XDR bindings automatically.    -*- text -*-
+ * Copyright (C) 2008 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+%{
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "rpcgen_int.h"
+
+extern void yyerror (const char *str);
+%}
+
+%union {
+  char *str;
+}
+
+%type <str> const
+
+%token STRUCT
+%token ENUM
+%token CONST
+%token TYPEDEF
+%token UNION
+%token SWITCH
+%token CASE
+%token DEFAULT
+%token PROGRAM
+
+%token UNSIGNED
+%token SIGNED
+%token CHAR
+%token SHORT
+%token INT
+%token HYPER
+%token DOUBLE
+%token STRING
+%token OPAQUE
+
+/* This is sometimes lumped together with the other types, but
+ * the special keyword void can only occur after "default:" in
+ * union statements.
+ */
+%token VOID
+
+%token <str> IDENT
+%token <str> INTLIT
+%token <str> STRLIT
+
+%%
+
+file   : /* empty */
+       | stmts
+       ;
+
+/* Statements. */
+stmts  : stmt ';'
+       | stmts stmt ';'
+       ;
+
+stmt   : ENUM IDENT '{' enum_values '}'
+       | STRUCT IDENT '{' decls '}'
+       | UNION IDENT SWITCH '(' decl ')' '{' union_cases '}'
+       | TYPEDEF decl
+       | CONST IDENT '=' const
+       | PROGRAM
+       ;
+
+/* Declarations used inside structs and unions.  eg. "int foo;" */
+decls  : decl ';'
+       | decls decl ';'
+       ;
+
+decl   : simple_decl
+       | fixed_array_decl
+       | variable_array_decl
+       | pointer_decl
+       ;
+
+simple_decl
+       : type_ident IDENT
+       ;
+
+fixed_array_decl
+       : type_ident IDENT '[' const ']'
+       ;
+
+variable_array_decl
+       : type_ident IDENT '<' const '>'
+       | type_ident IDENT '<' '>'
+       ;
+
+pointer_decl
+       : type_ident '*' IDENT
+       ;
+
+/* Enumerations. */
+enum_values
+       : enum_value
+       | enum_values ',' enum_value
+       ;
+
+enum_value
+       : IDENT
+       | IDENT '=' const
+       ;
+
+/* Case list inside a union. */
+union_cases
+       : union_case ';'
+       | union_cases union_case ';'
+       ;
+
+union_case
+       : CASE const ':' decl
+       | DEFAULT ':' VOID
+       | DEFAULT ':' decl
+       ;
+
+/* Constants, which may be integer literals or refer to previously
+ * defined constants (using "const" keyword).
+ * XXX In future we should probably allow computed constants.
+ */
+const  : INTLIT
+       | IDENT
+       ;
+
+/* Types. */
+type_ident
+       : CHAR
+       | SIGNED CHAR
+       | UNSIGNED CHAR
+       | SHORT
+       | SIGNED SHORT
+       | UNSIGNED SHORT
+       | INT
+       | SIGNED INT
+       | UNSIGNED INT
+       | HYPER
+       | SIGNED HYPER
+       | UNSIGNED HYPER
+       | SIGNED
+       | UNSIGNED
+       | DOUBLE
+       | STRING
+       | OPAQUE
+       | IDENT
+       ;
+
+%%
+
+void
+yyerror (const char *str)
+{
+  error ("%s", str);
+}
diff --git a/rpcgen_scan.l b/rpcgen_scan.l
new file mode 100644 (file)
index 0000000..061dbbf
--- /dev/null
@@ -0,0 +1,157 @@
+/* -*- C -*-
+ * rpcgen - Generate XDR bindings automatically.
+ * Copyright (C) 2008 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+%{
+#include <config.h>
+#include "rpcgen_parse.h"
+#include "rpcgen_int.h"
+%}
+
+%option noyywrap
+%option nounput
+%option yylineno
+
+HEXLIT     "0x"[0-9a-fA-F]+
+DECLIT     0|[1-9][0-9]*
+INTLIT     {HEXLIT}|{DECLIT}
+IDENT      [a-zA-Z_][a-zA-Z_0-9]*
+WS         [[:space:]]+
+
+%x cstring
+
+%%
+
+ /* # lineno "filename"
+  * cpp itself sets file and line numbers using these directives.  We
+  * have to parse this ourselves, since these could occur anywhere in
+  * the input, eg. in the middle of a struct definition.
+  */
+^"#".*\n    {
+  char *filename;
+  int lineno;
+
+  /* Allocate enough space to store the returned filename string. */
+  filename = malloc (strlen (yytext));
+  if (filename == NULL) perrorf ("malloc");
+
+  if (sscanf (yytext+2, "%d \"%[^\"]\"", &lineno, filename) == 2) {
+    yylineno = lineno - 1;
+    free (input_filename);
+    input_filename = filename;
+  }
+  else free (filename);
+ }
+
+ /* Anything on a line beginning with % is passed to the output.  Again
+  * we have to handle this within the scanner.
+  */
+^"%".*\n   fputs (yytext+1, yyout);
+
+ /* C string constants. */
+\"         start_string(); BEGIN (cstring);
+<cstring>{
+  \"       BEGIN (INITIAL); yylval.str = end_string(); return STRLIT;
+
+  \n       error ("unterminated string constant");
+  <<EOF>>  error ("unterminated string constant");
+
+  \\[0-7]{1,3} {
+    /* octal escape sequence */
+    int result;
+
+    (void) sscanf (yytext + 1, "%o", &result);
+
+    if (result > 0xff)
+      error ("octal constant is out of range");
+
+    add_char (result);
+  }
+
+  /* Does C allow these?? Maybe confusing it with OCaml.
+  \\x[0-9a-fA-F]{2} {
+    int result;
+
+    (void) sscanf( yytext + 1, "%x", &result );
+
+    add_char (result);
+  } */
+
+  \\[0-9]+ {
+    /* generate error - bad escape sequence; something
+     * like '\48' or '\0777777'
+     */
+    error ("bad escape sequence: %s\n", yytext);
+  }
+
+  \\n  add_char ('\n');
+  \\t  add_char ('\t');
+  \\r  add_char ('\r');
+  \\b  add_char ('\b');
+  \\f  add_char ('\f');
+
+  /* Backslash followed by a literal newline character. */
+  \\\n add_char ('\n');
+
+  /* Unrecognised escape character - should be an error? */
+  \\.  add_char (yytext[1]);
+
+  [^\\\n\"]+ {
+    add_string (yytext);
+  }
+}
+
+ /* Keywords. */
+struct     return STRUCT;
+enum       return ENUM;
+const      return CONST;
+typedef    return TYPEDEF;
+union      return UNION;
+switch     return SWITCH;
+case       return CASE;
+default    return DEFAULT;
+program    return PROGRAM;
+
+unsigned   return UNSIGNED;
+signed     return SIGNED;
+char       return CHAR;
+short      return SHORT;
+int        return INT;
+hyper      return HYPER;
+double     return DOUBLE;
+string     return STRING;
+opaque     return OPAQUE;
+
+void       return VOID;
+
+ /* Identifiers. */
+{IDENT}    { yylval.str = strdup (yytext); return IDENT; }
+
+ /* Numeric constants are tricky to scan accurately, so keep them as
+  * strings and pass them through directly to the C compiler.
+  */
+{INTLIT}   { yylval.str = strdup (yytext); return INTLIT; }
+
+ /* Single characters with special meaning. */
+":"|";"|","|"{"|"}"|"("|")"|"["|"]"|"<"|">"|"="|"*" return yytext[0];
+
+ /* Ignore whitespace. */
+{WS}
+
+ /* Anything else is an error. */
+.          error ("invalid character in input near '%c'", yytext[0]);