Working parser for rpcgen files.
[portablexdr.git] / rpcgen_main.c
1 /* -*- C -*-
2  * rpcgen - Generate XDR bindings automatically.
3  * Copyright (C) 2008 Red Hat Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <getopt.h>
29
30 #include "rpcgen_int.h"
31
32 static void print_version (void);
33 static void usage (const char *progname);
34 static void do_rpcgen (const char *filename);
35 static char *make_cpp_command (const char *filename);
36
37 /* Symbols exported from the scanner. */
38 extern FILE *yyin, *yyout;
39 extern int yyparse (void);
40 extern int yylineno;
41 extern int yydebug;
42
43 int
44 main (int argc, char *argv[])
45 {
46   int opt;
47
48   /* To enable debugging in the parser, you also need to compile
49    * with -DYYDEBUG
50    */
51 #if 0
52   yydebug = 1;
53 #endif
54
55   /* Note on command line arguments: We only support a small subset
56    * of command line arguments, because of the reduced functionality
57    * available in this version of rpcgen.  However we accept the
58    * command line parameters from both GNU rpcgen and BSD rpcgen
59    * and print appropriate errors for any we don't understand.
60    */
61   while ((opt = getopt (argc, argv, "AD:IK:LMSTVchlmno:s:t")) != -1) {
62     switch (opt)
63       {
64         /*-- Options supported by any rpcgen that we don't support. --*/
65       case 'D': case 'T': case 'K': case 'l': case 'm': case 't':
66       case 's':
67         error ("option '%c' is not supported by this PortableXDR rpcgen.\nYou may need to use an alternative rpcgen program instead.", opt);
68
69         /*-- Options supported only by GNU rpcgen that we don't support. --*/
70       case 'I': case 'n':
71         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);
72
73         /*-- Options supported only by BSD rpcgen that we don't support. --*/
74       case 'A': case 'M': case 'L': case 'S':
75         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);
76
77         /*-- Options that we do support. --*/
78       case 'c':
79       case 'h':
80       case 'o':
81         ;
82
83         /* None of the other versions of rpcgen support a way to print
84          * the version number, which is extremely annoying because
85          * there are so many different variations of rpcgen around.
86          * So this option at least should be useful!
87          */
88       case 'V':
89         print_version ();
90         exit (0);
91
92         /*-- Usage case. --*/
93       default:
94         usage (argv[0]);
95         exit (1);
96       }
97   }
98
99   if (optind >= argc)
100     error ("expected name of input file after options");
101
102   while (optind < argc)
103     do_rpcgen (argv[optind++]);
104
105   exit (0);
106 }
107
108 static void
109 print_version (void)
110 {
111   printf ("PortableXDR rpcgen %s\n", PACKAGE_VERSION);
112 }
113
114 static void
115 usage (const char *progname)
116 {
117   print_version ();
118   printf
119     ("Generate XDR bindings automatically.\n"
120      "\n"
121      "Usage:\n"
122      "  portable-rpcgen infile.x\n"
123      "  portable-rpcgen -c|-h [-o outfile] infile.x\n"
124      "  portable-rpcgen -V\n"
125      "\n"
126      "Options:\n"
127      "  -c     Generate C output file only.\n"
128      "  -h     Generate header output file only.\n"
129      "  -o     Name of output file (normally it is 'infile.[ch]').\n"
130      "  -V     Print the version and exit.\n"
131      "\n"
132      "In the first form, without -c or -h, we generate both output files.\n"
133      "\n"
134      "You can also list more than one input file on the command line, in\n"
135      "which case each input file is processed separately.\n"
136      "\n"
137      );
138   exit (0);
139 }
140
141 /* Called for each input file. */
142 static void
143 do_rpcgen (const char *filename)
144 {
145   char *cmd;
146   int r;
147
148   free (input_filename);
149   input_filename = NULL;
150
151   cmd = make_cpp_command (filename);
152
153   yyin = popen (cmd, "r");
154   if (yyin == NULL)
155     perrorf ("%s", cmd);
156   free (cmd);
157
158   yyout = stdout;
159
160   /* Parse the input file.  This either succeeds or exits with an error. */
161   r = yyparse ();
162   pclose (yyin);
163
164   if (r == 1)
165     error ("parsing failed, file is not a valid rpcgen input");
166   else if (r == 2)
167     error ("parsing failed because we ran out of memory");
168
169   free (input_filename);
170   input_filename = NULL;
171 }
172
173 /* Concatenate $EXTCPP and filename, and make sure the filename is
174  * quoted correctly.  Tedious.
175  */
176 static char *
177 make_cpp_command (const char *filename)
178 {
179   static const char good[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.";
180 #define is_good(c) (strchr (good, (c)) != NULL)
181   const char *p;
182
183   /* We can use start_string etc. because this function is only used
184    * outside the scanner.
185    */
186   start_string ();
187   add_string (EXTCPP);
188   add_char (' ');
189
190   for (p = filename; *p; p++) {
191     if (is_good (*p)) add_char (*p);
192     else {
193       add_char ('\\');
194       add_char (*p);
195     }
196   }
197
198   return end_string ();
199 }
200
201 /* Some fairly random functions which are used by the scanner for
202  * constructing strings, reporting errors, etc.
203  */
204
205 /* The scanner sets this to the name of the input file (from cpp)
206  * if known.
207  */
208 char *input_filename = NULL;
209
210 static char *str = NULL;
211 static int str_used, str_alloc;
212
213 void
214 start_string (void)
215 {
216   if (str != NULL)
217     error ("scanner called start_string without calling end_string");
218
219   str_alloc = 128;
220   str_used = 0;
221   str = malloc (str_alloc);
222   if (!str) perrorf ("malloc");
223 }
224
225 char *
226 end_string (void)
227 {
228   char *s;
229
230   if (str == NULL)
231     error ("scanner called end_string without calling start_string");
232
233   s = realloc (str, str_used+1);
234   if (!s) perrorf ("realloc");
235
236   str = NULL;
237   s[str_used] = '\0';
238   return s;
239 }
240
241 void
242 add_char (int c)
243 {
244   str_used++;
245   while (str_used >= str_alloc) {
246     str_alloc <<= 1;
247     str = realloc (str, str_alloc);
248     if (!str) perrorf ("realloc");
249   }
250   str[str_used-1] = c;
251 }
252
253 void
254 add_string (const char *s)
255 {
256   int i = str_used;
257   int len = strlen (s);
258
259   str_used += len;
260   while (str_used >= str_alloc) {
261     str_alloc <<= 1;
262     str = realloc (str, str_alloc);
263     if (!str) perrorf ("realloc");
264   }
265   memcpy (str+i, s, len);
266 }
267
268 void
269 error (const char *fs, ...)
270 {
271   va_list arg;
272
273   if (input_filename == NULL)
274     fputs (PACKAGE, stderr);
275   else
276     fprintf (stderr, "%s:%d", input_filename, yylineno);
277   fputs (": ", stderr);
278
279   va_start (arg, fs);
280   vfprintf (stderr, fs, arg);
281   va_end (arg);
282
283   fputc ('\n', stderr);
284
285   exit (1);
286 }
287
288 void
289 perrorf (const char *fs, ...)
290 {
291   va_list arg;
292   int e = errno;
293
294   if (input_filename == NULL)
295     fputs (PACKAGE, stderr);
296   else
297     fprintf (stderr, "%s:%d", input_filename, yylineno);
298   fputs (": ", stderr);
299
300   va_start (arg, fs);
301   vfprintf (stderr, fs, arg);
302   va_end (arg);
303
304   fputs (": ", stderr);
305   errno = e;
306   perror (NULL);
307
308   exit (1);
309 }