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