Header generation working.
[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 enum output_mode output_mode;
33
34 static void print_version (void);
35 static void usage (const char *progname);
36 static void do_rpcgen (const char *filename, const char *out);
37 static char *make_cpp_command (const char *filename);
38
39 int
40 main (int argc, char *argv[])
41 {
42   int opt;
43   char *filename;
44   int output_modes = 0;
45   char *out = NULL;
46
47 #if YYDEBUG
48   yydebug = 1;
49 #endif
50
51   /* Note on command line arguments: We only support a small subset
52    * of command line arguments, because of the reduced functionality
53    * available in this version of rpcgen.  However we accept the
54    * command line parameters from both GNU rpcgen and BSD rpcgen
55    * and print appropriate errors for any we don't understand.
56    */
57   while ((opt = getopt (argc, argv, "AD:IK:LMSTVchlmno:s:t")) != -1) {
58     switch (opt)
59       {
60         /*-- Options supported by any rpcgen that we don't support. --*/
61       case 'D': case 'T': case 'K': case 'l': case 'm': case 't':
62       case 's':
63         error ("option '%c' is not supported by this PortableXDR rpcgen.\nYou may need to use an alternative rpcgen program instead.", opt);
64
65         /*-- Options supported only by GNU rpcgen that we don't support. --*/
66       case 'I': case 'n':
67         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);
68
69         /*-- Options supported only by BSD rpcgen that we don't support. --*/
70       case 'A': case 'M': case 'L': case 'S':
71         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);
72
73         /*-- Options that we do support. --*/
74       case 'c':
75         output_modes |= 1 << output_c;
76         break;
77
78       case 'h':
79         output_modes |= 1 << output_h;
80         break;
81
82       case 'o':
83         out = optarg;
84         break;
85
86         /* None of the other versions of rpcgen support a way to print
87          * the version number, which is extremely annoying because
88          * there are so many different variations of rpcgen around.
89          * So this option at least should be useful!
90          */
91       case 'V':
92         print_version ();
93         exit (0);
94
95         /*-- Usage case. --*/
96       default:
97         usage (argv[0]);
98         exit (1);
99       }
100   }
101
102   if (optind >= argc)
103     error ("expected name of input file after options");
104
105   while (optind < argc) {
106     filename = argv[optind++];
107
108     if (output_modes == 0) {
109       output_mode = output_h;
110       do_rpcgen (filename, out);
111       output_mode = output_c;
112       do_rpcgen (filename, out);
113     } else {
114       if ((output_modes & (1 << output_h)) != 0) {
115         output_mode = output_h;
116         do_rpcgen (filename, out);
117       }
118       if ((output_modes & (1 << output_c)) != 0) {
119         output_mode = output_c;
120         do_rpcgen (filename, out);
121       }
122     }
123   }
124
125   exit (0);
126 }
127
128 static void
129 print_version (void)
130 {
131   printf ("PortableXDR rpcgen %s\n", PACKAGE_VERSION);
132 }
133
134 static void
135 usage (const char *progname)
136 {
137   print_version ();
138   printf
139     ("Generate XDR bindings automatically.\n"
140      "\n"
141      "Usage:\n"
142      "  portable-rpcgen infile.x\n"
143      "  portable-rpcgen -c|-h [-o outfile] infile.x\n"
144      "  portable-rpcgen -V\n"
145      "\n"
146      "Options:\n"
147      "  -c     Generate C output file only.\n"
148      "  -h     Generate header output file only.\n"
149      "  -o     Name of output file (normally it is 'infile.[ch]').\n"
150      "  -V     Print the version and exit.\n"
151      "\n"
152      "In the first form, without -c or -h, we generate both output files.\n"
153      "\n"
154      "You can also list more than one input file on the command line, in\n"
155      "which case each input file is processed separately.\n"
156      "\n"
157      );
158   exit (0);
159 }
160
161 /* This is a global so the error functions can delete the output file. */
162 const char *output_filename = NULL;
163 int unlink_output_filename;
164
165 /* Called for each input file. */
166 static void
167 do_rpcgen (const char *filename, const char *out)
168 {
169   char *cmd, *t = NULL;
170   int r, len;
171   const char *ext;
172
173   /* Open the output file. */
174   switch (output_mode) {
175   case output_c: ext = ".c"; break;
176   case output_h: ext = ".h"; break;
177   default: error ("internal error in do_rpcgen / output_mode");
178   }
179
180   if (out && strcmp (out, "-") == 0) {
181     output_filename = NULL;
182     unlink_output_filename = 0;
183     yyout = stdout;
184   }
185   else if (out) {
186     output_filename = out;
187     unlink_output_filename = 1;
188     yyout = fopen (output_filename, "w");
189     if (yyout == NULL)
190       perrorf ("%s", output_filename);
191   }
192   else {
193     len = strlen (filename);
194     t = malloc (len + 3);
195     if (t == NULL)
196       perrorf ("malloc");
197     strcpy (t, filename);
198     if (len >= 2 && strcmp (t + len - 2, ".x") == 0)
199       strcpy (t + len - 2, ext);
200     else
201       strcat (t, ext);
202     output_filename = t;
203     unlink_output_filename = 1;
204     yyout = fopen (output_filename, "w");
205     if (yyout == NULL)
206       perrorf ("%s", output_filename);
207   }
208
209   free (input_filename);
210   input_filename = NULL;
211
212   /* Make the CPP command and open a pipe. */
213   cmd = make_cpp_command (filename);
214
215   yyin = popen (cmd, "r");
216   if (yyin == NULL)
217     perrorf ("%s", cmd);
218   free (cmd);
219
220   gen_prologue (filename);
221
222   /* Parse the input file, this also generates the output as a side-effect. */
223   r = yyparse ();
224   pclose (yyin);
225
226   if (r == 1)
227     error ("parsing failed, file is not a valid rpcgen input");
228   else if (r == 2)
229     error ("parsing failed because we ran out of memory");
230
231   gen_epilogue ();
232
233   if (yyout != stdout)
234     fclose (yyout);
235   output_filename = NULL;
236   unlink_output_filename = 0;
237
238   free (input_filename);
239   input_filename = NULL;
240
241   free (t);
242 }
243
244 /* Concatenate $EXTCPP and filename, and make sure the filename is
245  * quoted correctly.  Tedious.
246  */
247 static char *
248 make_cpp_command (const char *filename)
249 {
250   static const char good[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.";
251 #define is_good(c) (strchr (good, (c)) != NULL)
252   const char *p;
253
254   /* We can use start_string etc. because this function is only used
255    * outside the scanner.
256    */
257   start_string ();
258   add_string (EXTCPP);
259   add_char (' ');
260
261   for (p = filename; *p; p++) {
262     if (is_good (*p)) add_char (*p);
263     else {
264       add_char ('\\');
265       add_char (*p);
266     }
267   }
268
269   return end_string ();
270 }
271
272 /* Some fairly random functions which are used by the scanner for
273  * constructing strings, reporting errors, etc.
274  */
275
276 /* The scanner sets this to the name of the input file (from cpp)
277  * if known.
278  */
279 char *input_filename = NULL;
280
281 static char *str = NULL;
282 static int str_used, str_alloc;
283
284 void
285 start_string (void)
286 {
287   if (str != NULL)
288     error ("scanner called start_string without calling end_string");
289
290   str_alloc = 128;
291   str_used = 0;
292   str = malloc (str_alloc);
293   if (!str) perrorf ("malloc");
294 }
295
296 char *
297 end_string (void)
298 {
299   char *s;
300
301   if (str == NULL)
302     error ("scanner called end_string without calling start_string");
303
304   s = realloc (str, str_used+1);
305   if (!s) perrorf ("realloc");
306
307   str = NULL;
308   s[str_used] = '\0';
309   return s;
310 }
311
312 void
313 add_char (int c)
314 {
315   str_used++;
316   while (str_used >= str_alloc) {
317     str_alloc <<= 1;
318     str = realloc (str, str_alloc);
319     if (!str) perrorf ("realloc");
320   }
321   str[str_used-1] = c;
322 }
323
324 void
325 add_string (const char *s)
326 {
327   int i = str_used;
328   int len = strlen (s);
329
330   str_used += len;
331   while (str_used >= str_alloc) {
332     str_alloc <<= 1;
333     str = realloc (str, str_alloc);
334     if (!str) perrorf ("realloc");
335   }
336   memcpy (str+i, s, len);
337 }
338
339 void
340 error (const char *fs, ...)
341 {
342   va_list arg;
343
344   if (output_filename && unlink_output_filename)
345     unlink (output_filename);
346
347   if (input_filename == NULL)
348     fputs (PACKAGE, stderr);
349   else
350     fprintf (stderr, "%s:%d", input_filename, yylineno);
351   fputs (": ", stderr);
352
353   va_start (arg, fs);
354   vfprintf (stderr, fs, arg);
355   va_end (arg);
356
357   fputc ('\n', stderr);
358
359   exit (1);
360 }
361
362 void
363 perrorf (const char *fs, ...)
364 {
365   va_list arg;
366   int e = errno;
367
368   if (output_filename && unlink_output_filename)
369     unlink (output_filename);
370
371   if (input_filename == NULL)
372     fputs (PACKAGE, stderr);
373   else
374     fprintf (stderr, "%s:%d", input_filename, yylineno);
375   fputs (": ", stderr);
376
377   va_start (arg, fs);
378   vfprintf (stderr, fs, arg);
379   va_end (arg);
380
381   fputs (": ", stderr);
382   errno = e;
383   perror (NULL);
384
385   exit (1);
386 }