From e9558f1dd242f2eb6a528c5509f1f8911fffe5d7 Mon Sep 17 00:00:00 2001 From: rjones Date: Wed, 7 Jan 2009 13:09:11 +0000 Subject: [PATCH] Working parser for rpcgen files. --- .cvsignore | 7 +- Makefile.am | 10 ++ configure.ac | 22 +++- rpcgen_int.h | 37 +++++++ rpcgen_main.c | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rpcgen_parse.y | 170 +++++++++++++++++++++++++++++++ rpcgen_scan.l | 157 +++++++++++++++++++++++++++++ 7 files changed, 707 insertions(+), 5 deletions(-) create mode 100644 rpcgen_int.h create mode 100644 rpcgen_main.c create mode 100644 rpcgen_parse.y create mode 100644 rpcgen_scan.l diff --git a/.cvsignore b/.cvsignore index 113a38c..3719ab1 100644 --- a/.cvsignore +++ b/.cvsignore @@ -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 diff --git a/Makefile.am b/Makefile.am index cc79d5f..fb0fcb3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/configure.ac b/configure.ac index f82f4fe..95d2f2b 100644 --- a/configure.ac +++ b/configure.ac @@ -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 index 0000000..e372295 --- /dev/null +++ b/rpcgen_int.h @@ -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 index 0000000..6ad467f --- /dev/null +++ b/rpcgen_main.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..04b073a --- /dev/null +++ b/rpcgen_parse.y @@ -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 +#include +#include +#include "rpcgen_int.h" + +extern void yyerror (const char *str); +%} + +%union { + char *str; +} + +%type 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 IDENT +%token INTLIT +%token 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 index 0000000..061dbbf --- /dev/null +++ b/rpcgen_scan.l @@ -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 +#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); +{ + \" BEGIN (INITIAL); yylval.str = end_string(); return STRLIT; + + \n error ("unterminated string constant"); + <> 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]); -- 1.8.3.1