xref: /freebsd/usr.bin/rpcgen/rpc_main.c (revision 4f52dfbb)
1 /*
2  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3  * unrestricted use provided that this legend is included on all tape
4  * media and as a part of the software program in whole or part.  Users
5  * may copy or modify Sun RPC without charge, but are not authorized
6  * to license or distribute it to anyone else except as part of a product or
7  * program developed by the user.
8  *
9  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12  *
13  * Sun RPC is provided with no support and without any obligation on the
14  * part of Sun Microsystems, Inc. to assist in its use, correction,
15  * modification or enhancement.
16  *
17  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19  * OR ANY PART THEREOF.
20  *
21  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22  * or profits or other special, indirect and consequential damages, even if
23  * Sun has been advised of the possibility of such damages.
24  *
25  * Sun Microsystems, Inc.
26  * 2550 Garcia Avenue
27  * Mountain View, California  94043
28  */
29 
30 
31 #if 0
32 #ifndef lint
33 #ident	"@(#)rpc_main.c	1.21	94/04/25 SMI"
34 static char sccsid[] = "@(#)rpc_main.c 1.30 89/03/30 (C) 1987 SMI";
35 #endif
36 #endif
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 /*
42  * rpc_main.c, Top level of the RPC protocol compiler.
43  * Copyright (C) 1987, Sun Microsystems, Inc.
44  */
45 
46 #include <err.h>
47 #include <ctype.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <sys/file.h>
54 #include <sys/stat.h>
55 #include "rpc_parse.h"
56 #include "rpc_scan.h"
57 #include "rpc_util.h"
58 
59 static void c_output(const char *, const char *, int, const char *);
60 static void h_output(const char *, const char *, int, const char *, int);
61 static void l_output(const char *, const char *, int, const char *);
62 static void t_output(const char *, const char *, int, const char *);
63 static void clnt_output(const char *, const char *, int, const char * );
64 static char *generate_guard(const char *);
65 static void c_initialize(void);
66 
67 static void usage(void);
68 static void options_usage(void);
69 static int do_registers(int, const char **);
70 static int parseargs(int, const char **, struct commandline *);
71 static void svc_output(const char *, const char *, int, const char *);
72 static void mkfile_output(struct commandline *);
73 static void s_output(int, const char **, const char *, const char *, int, const char *, int, int);
74 
75 #define	EXTEND	1		/* alias for TRUE */
76 #define	DONT_EXTEND	0		/* alias for FALSE */
77 
78 static const char *svcclosetime = "120";
79 static const char *CPP = NULL;
80 static const char CPPFLAGS[] = "-C";
81 static char pathbuf[MAXPATHLEN + 1];
82 static const char *allv[] = {
83 	"rpcgen", "-s", "udp", "-s", "tcp",
84 };
85 static int allc = nitems(allv);
86 static const char *allnv[] = {
87 	"rpcgen", "-s", "netpath",
88 };
89 static int allnc = nitems(allnv);
90 
91 /*
92  * machinations for handling expanding argument list
93  */
94 static void addarg(const char *);	/* add another argument to the list */
95 static void insarg(int, const char *);	/* insert arg at specified location */
96 static void clear_args(void);		/* clear argument list */
97 static void checkfiles(const char *, const char *);
98 					/* check if out file already exists */
99 
100 
101 
102 #define	ARGLISTLEN	20
103 #define	FIXEDARGS	0
104 
105 static char *arglist[ARGLISTLEN];
106 static int argcount = FIXEDARGS;
107 
108 
109 int nonfatalerrors;	/* errors */
110 int inetdflag = 0;	/* Support for inetd is disabled by default, use -I */
111 int pmflag = 0;		/* Support for port monitors is disabled by default */
112 int tirpc_socket = 1;	/* TI-RPC on socket, no TLI library */
113 int logflag;		/* Use syslog instead of fprintf for errors */
114 int tblflag;		/* Support for dispatch table file */
115 int mtflag = 0;		/* Support for MT */
116 
117 #define INLINE 0
118 /* length at which to start doing an inline */
119 
120 int inline_size = INLINE;
121 /*
122  * Length at which to start doing an inline. INLINE = default
123  * if 0, no xdr_inline code
124  */
125 
126 int indefinitewait;	/* If started by port monitors, hang till it wants */
127 int exitnow;		/* If started by port monitors, exit after the call */
128 int timerflag;		/* TRUE if !indefinite && !exitnow */
129 int newstyle;		/* newstyle of passing arguments (by value) */
130 int CCflag = 0;		/* C++ files */
131 static int allfiles;   /* generate all files */
132 int tirpcflag = 1;    /* generating code for tirpc, by default */
133 xdrfunc *xdrfunc_head = NULL; /* xdr function list */
134 xdrfunc *xdrfunc_tail = NULL; /* xdr function list */
135 pid_t childpid;
136 
137 
138 int
139 main(int argc, const char *argv[])
140 {
141 	struct commandline cmd;
142 
143 	(void) memset((char *)&cmd, 0, sizeof (struct commandline));
144 	clear_args();
145 	if (!parseargs(argc, argv, &cmd))
146 		usage();
147 	/*
148 	 * Only the client and server side stubs are likely to be customized,
149 	 *  so in that case only, check if the outfile exists, and if so,
150 	 *  print an error message and exit.
151 	 */
152 	if (cmd.Ssflag || cmd.Scflag || cmd.makefileflag) {
153 		checkfiles(cmd.infile, cmd.outfile);
154 	}
155 	else
156 		checkfiles(cmd.infile, NULL);
157 
158 	if (cmd.cflag) {
159 		c_output(cmd.infile, "-DRPC_XDR", DONT_EXTEND, cmd.outfile);
160 	} else if (cmd.hflag) {
161 		h_output(cmd.infile, "-DRPC_HDR", DONT_EXTEND, cmd.outfile,
162 		    cmd.hflag);
163 	} else if (cmd.lflag) {
164 		l_output(cmd.infile, "-DRPC_CLNT", DONT_EXTEND, cmd.outfile);
165 	} else if (cmd.sflag || cmd.mflag || (cmd.nflag)) {
166 		s_output(argc, argv, cmd.infile, "-DRPC_SVC", DONT_EXTEND,
167 			cmd.outfile, cmd.mflag, cmd.nflag);
168 	} else if (cmd.tflag) {
169 		t_output(cmd.infile, "-DRPC_TBL", DONT_EXTEND, cmd.outfile);
170 	} else if  (cmd.Ssflag) {
171 		svc_output(cmd.infile, "-DRPC_SERVER", DONT_EXTEND,
172 			cmd.outfile);
173 	} else if (cmd.Scflag) {
174 		clnt_output(cmd.infile, "-DRPC_CLIENT", DONT_EXTEND,
175 			    cmd.outfile);
176 	} else if (cmd.makefileflag) {
177 		mkfile_output(&cmd);
178 	} else {
179 		/* the rescans are required, since cpp may effect input */
180 		c_output(cmd.infile, "-DRPC_XDR", EXTEND, "_xdr.c");
181 		reinitialize();
182 		h_output(cmd.infile, "-DRPC_HDR", EXTEND, ".h", cmd.hflag);
183 		reinitialize();
184 		l_output(cmd.infile, "-DRPC_CLNT", EXTEND, "_clnt.c");
185 		reinitialize();
186 		if (inetdflag || !tirpcflag)
187 			s_output(allc, allv, cmd.infile, "-DRPC_SVC", EXTEND,
188 			"_svc.c", cmd.mflag, cmd.nflag);
189 		else
190 			s_output(allnc, allnv, cmd.infile, "-DRPC_SVC",
191 				EXTEND, "_svc.c", cmd.mflag, cmd.nflag);
192 		if (tblflag) {
193 			reinitialize();
194 			t_output(cmd.infile, "-DRPC_TBL", EXTEND, "_tbl.i");
195 		}
196 
197 		if (allfiles) {
198 			reinitialize();
199 			svc_output(cmd.infile, "-DRPC_SERVER", EXTEND,
200 				"_server.c");
201 			reinitialize();
202 			clnt_output(cmd.infile, "-DRPC_CLIENT", EXTEND,
203 				"_client.c");
204 
205 		}
206 		if (allfiles || (cmd.makefileflag == 1)){
207 			reinitialize();
208 			mkfile_output(&cmd);
209 		}
210 
211 	}
212 	exit(nonfatalerrors);
213 	/* NOTREACHED */
214 }
215 
216 
217 /*
218  * add extension to filename
219  */
220 static char *
221 extendfile(const char *path, const char *ext)
222 {
223 	char *res;
224 	const char *p;
225 	const char *file;
226 
227 	if ((file = strrchr(path, '/')) == NULL)
228 		file = path;
229 	else
230 		file++;
231 	res = xmalloc(strlen(file) + strlen(ext) + 1);
232 	p = strrchr(file, '.');
233 	if (p == NULL) {
234 		p = file + strlen(file);
235 	}
236 	(void) strcpy(res, file);
237 	(void) strcpy(res + (p - file), ext);
238 	return (res);
239 }
240 
241 /*
242  * Open output file with given extension
243  */
244 static void
245 open_output(const char *infile, const char *outfile)
246 {
247 
248 	if (outfile == NULL) {
249 		fout = stdout;
250 		return;
251 	}
252 
253 	if (infile != NULL && streq(outfile, infile)) {
254 		warnx("%s already exists. No output generated", infile);
255 		crash();
256 	}
257 	fout = fopen(outfile, "w");
258 	if (fout == NULL) {
259 		warn("unable to open %s", outfile);
260 		crash();
261 	}
262 	record_open(outfile);
263 
264 	return;
265 }
266 
267 static void
268 add_warning(void)
269 {
270 	f_print(fout, "/*\n");
271 	f_print(fout, " * Please do not edit this file.\n");
272 	f_print(fout, " * It was generated using rpcgen.\n");
273 	f_print(fout, " */\n\n");
274 }
275 
276 /* clear list of arguments */
277 static void
278 clear_args(void)
279 {
280 	int i;
281 	for (i = FIXEDARGS; i < ARGLISTLEN; i++)
282 		arglist[i] = NULL;
283 	argcount = FIXEDARGS;
284 }
285 
286 /* prepend C-preprocessor and flags before arguments */
287 static void
288 prepend_cpp(void)
289 {
290 	int idx = 1;
291 	const char *var;
292 	char *dupvar, *s, *t;
293 
294 	if (CPP != NULL)
295 		insarg(0, CPP);
296 	else if ((var = getenv("RPCGEN_CPP")) == NULL)
297 		insarg(0, "/usr/bin/cpp");
298 	else {
299 		/* Parse command line in a rudimentary way */
300 		dupvar = xstrdup(var);
301 		for (s = dupvar, idx = 0; (t = strsep(&s, " \t")) != NULL; ) {
302 			if (t[0])
303 				insarg(idx++, t);
304 		}
305 		free(dupvar);
306 	}
307 
308 	insarg(idx, CPPFLAGS);
309 }
310 
311 /*
312  * Open input file with given define for C-preprocessor
313  */
314 static void
315 open_input(const char *infile, const char *define)
316 {
317 	int pd[2];
318 
319 	infilename = (infile == NULL) ? "<stdin>" : infile;
320 	(void) pipe(pd);
321 	switch (childpid = fork()) {
322 	case 0:
323 		prepend_cpp();
324 		addarg(define);
325 		if (infile)
326 			addarg(infile);
327 		addarg((char *)NULL);
328 		(void) close(1);
329 		(void) dup2(pd[1], 1);
330 		(void) close(pd[0]);
331 		execvp(arglist[0], arglist);
332 		err(1, "execvp %s", arglist[0]);
333 	case -1:
334 		err(1, "fork");
335 	}
336 	(void) close(pd[1]);
337 	fin = fdopen(pd[0], "r");
338 	if (fin == NULL) {
339 		warn("%s", infilename);
340 		crash();
341 	}
342 }
343 
344 /* valid tirpc nettypes */
345 static const char *valid_ti_nettypes[] =
346 {
347 	"netpath",
348 	"visible",
349 	"circuit_v",
350 	"datagram_v",
351 	"circuit_n",
352 	"datagram_n",
353 	"udp",
354 	"tcp",
355 	"raw",
356 	NULL
357 	};
358 
359 /* valid inetd nettypes */
360 static const char *valid_i_nettypes[] =
361 {
362 	"udp",
363 	"tcp",
364 	NULL
365 	};
366 
367 static int
368 check_nettype(const char *name, const char *list_to_check[])
369 {
370 	int i;
371 	for (i = 0; list_to_check[i] != NULL; i++) {
372 		if (strcmp(name, list_to_check[i]) == 0) {
373 			return (1);
374 		}
375 	}
376 	warnx("illegal nettype :\'%s\'", name);
377 	return (0);
378 }
379 
380 static const char *
381 file_name(const char *file, const char *ext)
382 {
383 	char *temp;
384 	temp = extendfile(file, ext);
385 
386 	if (access(temp, F_OK) != -1)
387 		return (temp);
388 	else
389 		return (" ");
390 
391 }
392 
393 
394 static void
395 c_output(const char *infile, const char *define, int extend, const char *outfile)
396 {
397 	definition *def;
398 	char *include;
399 	const char *outfilename;
400 	long tell;
401 
402 	c_initialize();
403 	open_input(infile, define);
404 	outfilename = extend ? extendfile(infile, outfile) : outfile;
405 	open_output(infile, outfilename);
406 	add_warning();
407 	if (infile && (include = extendfile(infile, ".h"))) {
408 		f_print(fout, "#include \"%s\"\n", include);
409 		free(include);
410 		/* .h file already contains rpc/rpc.h */
411 	} else
412 		f_print(fout, "#include <rpc/rpc.h>\n");
413 	tell = ftell(fout);
414 	while ( (def = get_definition()) ) {
415 		emit(def);
416 	}
417 	if (extend && tell == ftell(fout)) {
418 		(void) unlink(outfilename);
419 	}
420 }
421 
422 
423 void
424 c_initialize(void)
425 {
426 
427 	/* add all the starting basic types */
428 	add_type(1, "int");
429 	add_type(1, "long");
430 	add_type(1, "short");
431 	add_type(1, "bool");
432 	add_type(1, "u_int");
433 	add_type(1, "u_long");
434 	add_type(1, "u_short");
435 
436 }
437 
438 static const char rpcgen_table_dcl[] = "struct rpcgen_table {\n\
439 	char	*(*proc)(); \n\
440 	xdrproc_t	xdr_arg; \n\
441 	unsigned	len_arg; \n\
442 	xdrproc_t	xdr_res; \n\
443 	unsigned	len_res; \n\
444 }; \n";
445 
446 
447 char *
448 generate_guard(const char *pathname)
449 {
450 	const char *filename;
451 	char *guard, *tmp, *stopat;
452 
453 	filename = strrchr(pathname, '/');  /* find last component */
454 	filename = ((filename == NULL) ? pathname : filename+1);
455 	guard = xstrdup(filename);
456 	stopat = strrchr(guard, '.');
457 
458 	/*
459 	 * Convert to a valid C macro name and make it upper case.
460 	 * Map macro unfriendly characterss to '_'.
461 	 */
462 	for (tmp = guard; *tmp != '\000'; ++tmp) {
463 		if (islower(*tmp))
464 			*tmp = toupper(*tmp);
465 		else if (isupper(*tmp) || *tmp == '_')
466 			/* OK for C */;
467 		else if (tmp == guard)
468 			*tmp = '_';
469 		else if (isdigit(*tmp))
470 			/* OK for all but first character */;
471 		else if (tmp == stopat) {
472 			*tmp = '\0';
473 			break;
474 		} else
475 			*tmp = '_';
476 	}
477 	/*
478 	 * Can't have a '_' in front, because it'll end up being "__".
479 	 * "__" macros shoudln't be used. So, remove all of the
480 	 * '_' characters from the front.
481 	 */
482 	if (*guard == '_') {
483 		for (tmp = guard; *tmp == '_'; ++tmp)
484 			;
485 		strcpy(guard, tmp);
486 	}
487 	tmp = guard;
488 	guard = extendfile(guard, "_H_RPCGEN");
489 	free(tmp);
490 	return (guard);
491 }
492 
493 /*
494  * Compile into an XDR header file
495  */
496 
497 
498 static void
499 h_output(const char *infile, const char *define, int extend, const char *outfile, int headeronly)
500 {
501 	definition *def;
502 	const char *outfilename;
503 	long tell;
504 	const char *guard;
505 	list *l;
506 	xdrfunc *xdrfuncp;
507 	void *tmp = NULL;
508 
509 	open_input(infile, define);
510 	outfilename =  extend ? extendfile(infile, outfile) : outfile;
511 	open_output(infile, outfilename);
512 	add_warning();
513 	if (outfilename || infile){
514 		guard = tmp = generate_guard(outfilename ? outfilename: infile);
515 	} else
516 		guard = "STDIN_";
517 
518 	f_print(fout, "#ifndef _%s\n#define	_%s\n\n", guard,
519 		guard);
520 
521 	f_print(fout, "#include <rpc/rpc.h>\n");
522 
523 	if (mtflag)
524 		f_print(fout, "#include <pthread.h>\n");
525 
526 	/* put the C++ support */
527 	if (!CCflag) {
528 		f_print(fout, "\n#ifdef __cplusplus\n");
529 		f_print(fout, "extern \"C\" {\n");
530 		f_print(fout, "#endif\n\n");
531 	}
532 
533 	/* put in a typedef for quadprecision. Only with Cflag */
534 
535 	tell = ftell(fout);
536 
537 	/* print data definitions */
538 	while ( (def = get_definition()) ) {
539 		print_datadef(def, headeronly);
540 	}
541 
542 	/*
543 	 * print function declarations.
544 	 *  Do this after data definitions because they might be used as
545 	 *  arguments for functions
546 	 */
547 	for (l = defined; l != NULL; l = l->next) {
548 		print_funcdef(l->val, headeronly);
549 	}
550 	/* Now  print all xdr func declarations */
551 	if (xdrfunc_head != NULL){
552 
553 		f_print(fout,
554 			"\n/* the xdr functions */\n");
555 
556 		if (CCflag){
557 			f_print(fout, "\n#ifdef __cplusplus\n");
558 			f_print(fout, "extern \"C\" {\n");
559 			f_print(fout, "#endif\n");
560 		}
561 
562 		xdrfuncp = xdrfunc_head;
563 		while (xdrfuncp != NULL){
564 			print_xdr_func_def(xdrfuncp->name, xdrfuncp->pointerp);
565 			xdrfuncp = xdrfuncp->next;
566 		}
567 	}
568 
569 	if (extend && tell == ftell(fout)) {
570 		(void) unlink(outfilename);
571 	} else if (tblflag) {
572 		f_print(fout, rpcgen_table_dcl);
573 	}
574 
575 	f_print(fout, "\n#ifdef __cplusplus\n");
576 	f_print(fout, "}\n");
577 	f_print(fout, "#endif\n");
578 
579 	f_print(fout, "\n#endif /* !_%s */\n", guard);
580 	free(tmp);
581 }
582 
583 /*
584  * Compile into an RPC service
585  */
586 static void
587 s_output(int argc, const char *argv[], const char *infile, const char *define,
588     int extend, const char *outfile, int nomain, int netflag)
589 {
590 	char *include;
591 	definition *def;
592 	int foundprogram = 0;
593 	const char *outfilename;
594 
595 	open_input(infile, define);
596 	outfilename = extend ? extendfile(infile, outfile) : outfile;
597 	open_output(infile, outfilename);
598 	add_warning();
599 	if (infile && (include = extendfile(infile, ".h"))) {
600 		f_print(fout, "#include \"%s\"\n", include);
601 		free(include);
602 	} else
603 		f_print(fout, "#include <rpc/rpc.h>\n");
604 
605 	f_print(fout, "#include <stdio.h>\n");
606 	f_print(fout, "#include <stdlib.h> /* getenv, exit */\n");
607 	f_print (fout, "#include <rpc/pmap_clnt.h> /* for pmap_unset */\n");
608 	f_print (fout, "#include <string.h> /* strcmp */\n");
609 	if (tirpcflag)
610 		f_print(fout, "#include <rpc/rpc_com.h>\n");
611 	if (strcmp(svcclosetime, "-1") == 0)
612 		indefinitewait = 1;
613 	else if (strcmp(svcclosetime, "0") == 0)
614 		exitnow = 1;
615 	else if (inetdflag || pmflag) {
616 		f_print(fout, "#include <signal.h>\n");
617 		timerflag = 1;
618 	}
619 
620 	if (!tirpcflag && inetdflag)
621 		f_print(fout, "#include <sys/ttycom.h> /* TIOCNOTTY */\n");
622 	if (inetdflag || pmflag) {
623 		f_print(fout, "#ifdef __cplusplus\n");
624 		f_print(fout,
625 			"#include <sys/sysent.h> /* getdtablesize, open */\n");
626 		f_print(fout, "#endif /* __cplusplus */\n");
627 	}
628 	if (tirpcflag) {
629 		f_print(fout, "#include <fcntl.h> /* open */\n");
630 		f_print(fout, "#include <unistd.h> /* fork / setsid */\n");
631 		f_print(fout, "#include <sys/types.h>\n");
632 	}
633 
634 	f_print(fout, "#include <string.h>\n");
635 	if (inetdflag || !tirpcflag) {
636 		f_print(fout, "#include <sys/socket.h>\n");
637 		f_print(fout, "#include <netinet/in.h>\n");
638 	}
639 
640 	if ((netflag || pmflag) && tirpcflag && !nomain) {
641 		f_print(fout, "#include <netconfig.h>\n");
642 	}
643 	if (tirpcflag)
644 		f_print(fout, "#include <sys/resource.h> /* rlimit */\n");
645 	if (logflag || inetdflag || pmflag || tirpcflag)
646 		f_print(fout, "#include <syslog.h>\n");
647 
648 	f_print(fout, "\n#ifdef DEBUG\n#define	RPC_SVC_FG\n#endif\n");
649 	if (timerflag)
650 		f_print(fout, "\n#define	_RPCSVC_CLOSEDOWN %s\n",
651 			svcclosetime);
652 	while ( (def = get_definition()) ) {
653 		foundprogram |= (def->def_kind == DEF_PROGRAM);
654 	}
655 	if (extend && !foundprogram) {
656 		(void) unlink(outfilename);
657 		return;
658 	}
659 	write_most(infile, netflag, nomain);
660 	if (!nomain) {
661 		if (!do_registers(argc, argv)) {
662 			if (outfilename)
663 				(void) unlink(outfilename);
664 			usage();
665 		}
666 		write_rest();
667 	}
668 }
669 
670 /*
671  * generate client side stubs
672  */
673 static void
674 l_output(const char *infile, const char *define, int extend, const char *outfile)
675 {
676 	char *include;
677 	definition *def;
678 	int foundprogram = 0;
679 	const char *outfilename;
680 
681 	open_input(infile, define);
682 	outfilename = extend ? extendfile(infile, outfile) : outfile;
683 	open_output(infile, outfilename);
684 	add_warning();
685 	f_print (fout, "#include <string.h> /* for memset */\n");
686 	if (infile && (include = extendfile(infile, ".h"))) {
687 		f_print(fout, "#include \"%s\"\n", include);
688 		free(include);
689 	} else
690 		f_print(fout, "#include <rpc/rpc.h>\n");
691 	while ( (def = get_definition()) ) {
692 		foundprogram |= (def->def_kind == DEF_PROGRAM);
693 	}
694 	if (extend && !foundprogram) {
695 		(void) unlink(outfilename);
696 		return;
697 	}
698 	write_stubs();
699 }
700 
701 /*
702  * generate the dispatch table
703  */
704 static void
705 t_output(const char *infile, const char *define, int extend, const char *outfile)
706 {
707 	definition *def;
708 	int foundprogram = 0;
709 	const char *outfilename;
710 
711 	open_input(infile, define);
712 	outfilename = extend ? extendfile(infile, outfile) : outfile;
713 	open_output(infile, outfilename);
714 	add_warning();
715 	while ( (def = get_definition()) ) {
716 		foundprogram |= (def->def_kind == DEF_PROGRAM);
717 	}
718 	if (extend && !foundprogram) {
719 		(void) unlink(outfilename);
720 		return;
721 	}
722 	write_tables();
723 }
724 
725 /* sample routine for the server template */
726 static void
727 svc_output(const char *infile, const char *define, int extend, const char *outfile)
728 {
729 	definition *def;
730 	char *include;
731 	const char *outfilename;
732 	long tell;
733 	open_input(infile, define);
734 	outfilename = extend ? extendfile(infile, outfile) : outfile;
735 	checkfiles(infile, outfilename);
736 	/*
737 	 * Check if outfile already exists.
738 	 * if so, print an error message and exit
739 	 */
740 	open_output(infile, outfilename);
741 	add_sample_msg();
742 
743 	if (infile && (include = extendfile(infile, ".h"))) {
744 		f_print(fout, "#include \"%s\"\n", include);
745 		free(include);
746 	} else
747 		f_print(fout, "#include <rpc/rpc.h>\n");
748 
749 	tell = ftell(fout);
750 	while ( (def = get_definition()) ) {
751 		write_sample_svc(def);
752 	}
753 	if (extend && tell == ftell(fout)) {
754 		(void) unlink(outfilename);
755 	}
756 }
757 
758 /* sample main routine for client */
759 static void
760 clnt_output(const char *infile, const char *define, int extend, const char *outfile)
761 {
762 	definition *def;
763 	char *include;
764 	const char *outfilename;
765 	long tell;
766 	int has_program = 0;
767 
768 	open_input(infile, define);
769 	outfilename = extend ? extendfile(infile, outfile) : outfile;
770 	checkfiles(infile, outfilename);
771 	/*
772 	 * Check if outfile already exists.
773 	 * if so, print an error message and exit
774 	 */
775 
776 	open_output(infile, outfilename);
777 	add_sample_msg();
778 	if (infile && (include = extendfile(infile, ".h"))) {
779 		f_print(fout, "#include \"%s\"\n", include);
780 		free(include);
781 	} else
782 		f_print(fout, "#include <rpc/rpc.h>\n");
783 	f_print(fout, "#include <stdio.h>\n");
784 	f_print(fout, "#include <stdlib.h>\n");
785 	tell = ftell(fout);
786 	while ( (def = get_definition()) ) {
787 		has_program += write_sample_clnt(def);
788 	}
789 
790 	if (has_program)
791 		write_sample_clnt_main();
792 
793 	if (extend && tell == ftell(fout)) {
794 		(void) unlink(outfilename);
795 	}
796 }
797 
798 
799 static void mkfile_output(struct commandline *cmd)
800 {
801 	const char *mkfilename, *clientname, *clntname, *xdrname, *hdrname;
802 	const char *servername, *svcname, *servprogname, *clntprogname;
803 	char *temp, *mkftemp;
804 
805 	svcname = file_name(cmd->infile, "_svc.c");
806 	clntname = file_name(cmd->infile, "_clnt.c");
807 	xdrname = file_name(cmd->infile, "_xdr.c");
808 	hdrname = file_name(cmd->infile, ".h");
809 
810 
811 	if (allfiles){
812 		servername = extendfile(cmd->infile, "_server.c");
813 		clientname = extendfile(cmd->infile, "_client.c");
814 	}else{
815 		servername = " ";
816 		clientname = " ";
817 	}
818 	servprogname = extendfile(cmd->infile, "_server");
819 	clntprogname = extendfile(cmd->infile, "_client");
820 
821 	if (allfiles){
822 		mkftemp = xmalloc(strlen("makefile.") +
823 		                     strlen(cmd->infile) + 1);
824 		temp = strrchr(cmd->infile, '.');
825 		strcpy(mkftemp, "makefile.");
826 		(void) strncat(mkftemp, cmd->infile,
827 			(temp - cmd->infile));
828 		mkfilename = mkftemp;
829 	} else
830 		mkfilename = cmd->outfile;
831 
832 
833 	checkfiles(NULL, mkfilename);
834 	open_output(NULL, mkfilename);
835 
836 	f_print(fout, "\n# This is a template makefile generated\
837 		by rpcgen \n");
838 
839 	f_print(fout, "\n# Parameters \n\n");
840 
841 	f_print(fout, "CLIENT = %s\nSERVER = %s\n\n",
842 		clntprogname, servprogname);
843 	f_print(fout, "SOURCES_CLNT.c = \nSOURCES_CLNT.h = \n");
844 	f_print(fout, "SOURCES_SVC.c = \nSOURCES_SVC.h = \n");
845 	f_print(fout, "SOURCES.x = %s\n\n", cmd->infile);
846 	f_print(fout, "TARGETS_SVC.c = %s %s %s \n",
847 		svcname, servername, xdrname);
848 	f_print(fout, "TARGETS_CLNT.c = %s %s %s \n",
849 		clntname, clientname, xdrname);
850 	f_print(fout, "TARGETS = %s %s %s %s %s %s\n\n",
851 		hdrname, xdrname, clntname,
852 		svcname, clientname, servername);
853 
854 	f_print(fout, "OBJECTS_CLNT = $(SOURCES_CLNT.c:%%.c=%%.o) \
855 $(TARGETS_CLNT.c:%%.c=%%.o) ");
856 
857 	f_print(fout, "\nOBJECTS_SVC = $(SOURCES_SVC.c:%%.c=%%.o) \
858 $(TARGETS_SVC.c:%%.c=%%.o) ");
859 
860 
861 	f_print(fout, "\n# Compiler flags \n");
862 	if (mtflag)
863 		f_print(fout, "\nCFLAGS += -D_REENTRANT -D_THEAD_SAFE \nLDLIBS += -pthread\n");
864 
865 	f_print(fout, "RPCGENFLAGS = \n");
866 
867 	f_print(fout, "\n# Targets \n\n");
868 
869 	f_print(fout, "all : $(CLIENT) $(SERVER)\n\n");
870 	f_print(fout, "$(TARGETS) : $(SOURCES.x) \n");
871 	f_print(fout, "\trpcgen $(RPCGENFLAGS) $(SOURCES.x)\n\n");
872 	if (allfiles) {
873 		f_print(fout, "\trpcgen -Sc $(RPCGENFLAGS) $(SOURCES.x) -o %s\n\n", clientname);
874 		f_print(fout, "\trpcgen -Ss $(RPCGENFLAGS) $(SOURCES.x) -o %s\n\n", servername);
875 	}
876 	f_print(fout, "$(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) \
877 $(TARGETS_CLNT.c) \n\n");
878 
879 	f_print(fout, "$(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) \
880 $(TARGETS_SVC.c) \n\n");
881 	f_print(fout, "$(CLIENT) : $(OBJECTS_CLNT) \n");
882 	f_print(fout, "\t$(CC) -o $(CLIENT) $(OBJECTS_CLNT) \
883 $(LDLIBS) \n\n");
884 	f_print(fout, "$(SERVER) : $(OBJECTS_SVC) \n");
885 	f_print(fout, "\t$(CC) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS)\n\n");
886 	f_print(fout, "clean:\n\t rm -f core $(TARGETS) $(OBJECTS_CLNT) \
887 $(OBJECTS_SVC) $(CLIENT) $(SERVER)\n\n");
888 }
889 
890 
891 
892 /*
893  * Perform registrations for service output
894  * Return 0 if failed; 1 otherwise.
895  */
896 static int
897 do_registers(int argc, const char *argv[])
898 {
899 	int i;
900 
901 	if (inetdflag || !tirpcflag) {
902 		for (i = 1; i < argc; i++) {
903 			if (streq(argv[i], "-s")) {
904 				if (!check_nettype(argv[i + 1],
905 						    valid_i_nettypes))
906 					return (0);
907 				write_inetd_register(argv[i + 1]);
908 				i++;
909 			}
910 		}
911 	} else {
912 		for (i = 1; i < argc; i++)
913 			if (streq(argv[i], "-s")) {
914 				if (!check_nettype(argv[i + 1],
915 						    valid_ti_nettypes))
916 					return (0);
917 				write_nettype_register(argv[i + 1]);
918 				i++;
919 			} else if (streq(argv[i], "-n")) {
920 				write_netid_register(argv[i + 1]);
921 				i++;
922 			}
923 	}
924 	return (1);
925 }
926 
927 /*
928  * Add another argument to the arg list
929  */
930 static void
931 addarg(const char *cp)
932 {
933 	if (argcount >= ARGLISTLEN) {
934 		warnx("too many defines");
935 		crash();
936 		/*NOTREACHED*/
937 	}
938 	if (cp != NULL)
939 		arglist[argcount++] = xstrdup(cp);
940 	else
941 		arglist[argcount++] = NULL;
942 
943 }
944 
945 /*
946  * Insert an argument at the specified location
947  */
948 static void
949 insarg(int place, const char *cp)
950 {
951 	int i;
952 
953 	if (argcount >= ARGLISTLEN) {
954 		warnx("too many defines");
955 		crash();
956 		/*NOTREACHED*/
957 	}
958 
959 	/* Move up existing arguments */
960 	for (i = argcount - 1; i >= place; i--)
961 		arglist[i + 1] = arglist[i];
962 
963 	arglist[place] = xstrdup(cp);
964 	argcount++;
965 }
966 
967 /*
968  * if input file is stdin and an output file is specified then complain
969  * if the file already exists. Otherwise the file may get overwritten
970  * If input file does not exist, exit with an error
971  */
972 
973 static void
974 checkfiles(const char *infile, const char *outfile)
975 {
976 
977 	struct stat buf;
978 
979 	if (infile)		/* infile ! = NULL */
980 		if (stat(infile, &buf) < 0)
981 		{
982 			warn("%s", infile);
983 			crash();
984 		}
985 	if (outfile) {
986 		if (stat(outfile, &buf) < 0)
987 			return;	/* file does not exist */
988 		else {
989 			warnx("file '%s' already exists and may be overwritten", outfile);
990 			crash();
991 		}
992 	}
993 }
994 
995 /*
996  * Parse command line arguments
997  */
998 static int
999 parseargs(int argc, const char *argv[], struct commandline *cmd)
1000 {
1001 	int i;
1002 	int j;
1003 	char c, ch;
1004 	char flag[(1 << 8 * sizeof (char))];
1005 	int nflags;
1006 
1007 	cmd->infile = cmd->outfile = NULL;
1008 	if (argc < 2) {
1009 		return (0);
1010 	}
1011 	allfiles = 0;
1012 	flag['c'] = 0;
1013 	flag['h'] = 0;
1014 	flag['l'] = 0;
1015 	flag['m'] = 0;
1016 	flag['o'] = 0;
1017 	flag['s'] = 0;
1018 	flag['n'] = 0;
1019 	flag['t'] = 0;
1020 	flag['S'] = 0;
1021 	flag['C'] = 0;
1022 	flag['M'] = 0;
1023 
1024 	for (i = 1; i < argc; i++) {
1025 		if (argv[i][0] != '-') {
1026 			if (cmd->infile) {
1027 				warnx("cannot specify more than one input file");
1028 				return (0);
1029 			}
1030 			cmd->infile = argv[i];
1031 		} else {
1032 			for (j = 1; argv[i][j] != 0; j++) {
1033 				c = argv[i][j];
1034 				switch (c) {
1035 				case 'a':
1036 					allfiles = 1;
1037 					break;
1038 				case 'c':
1039 				case 'h':
1040 				case 'l':
1041 				case 'm':
1042 				case 't':
1043 					if (flag[(int)c]) {
1044 						return (0);
1045 					}
1046 					flag[(int)c] = 1;
1047 					break;
1048 				case 'S':
1049 					/*
1050 					 * sample flag: Ss or Sc.
1051 					 *  Ss means set flag['S'];
1052 					 *  Sc means set flag['C'];
1053 					 *  Sm means set flag['M'];
1054 					 */
1055 					ch = argv[i][++j]; /* get next char */
1056 					if (ch == 's')
1057 						ch = 'S';
1058 					else if (ch == 'c')
1059 						ch = 'C';
1060 					else if (ch == 'm')
1061 						ch = 'M';
1062 					else
1063 						return (0);
1064 
1065 					if (flag[(int)ch]) {
1066 						return (0);
1067 					}
1068 					flag[(int)ch] = 1;
1069 					break;
1070 				case 'C': /* ANSI C syntax */
1071 					ch = argv[i][j+1]; /* get next char */
1072 
1073 					if (ch != 'C')
1074 						break;
1075 					CCflag = 1;
1076 					break;
1077 				case 'b':
1078 					/*
1079 					 *  Turn TIRPC flag off for
1080 					 *  generating backward compatible
1081 					 *  code
1082 					 */
1083 					tirpcflag = 0;
1084 					break;
1085 
1086 				case 'I':
1087 					inetdflag = 1;
1088 					break;
1089 				case 'N':
1090 					newstyle = 1;
1091 					break;
1092 				case 'L':
1093 					logflag = 1;
1094 					break;
1095 				case 'P':
1096 					pmflag = 1;
1097 					break;
1098 				case 'K':
1099 					if (++i == argc) {
1100 						return (0);
1101 					}
1102 					svcclosetime = argv[i];
1103 					goto nextarg;
1104 				case 'T':
1105 					tblflag = 1;
1106 					break;
1107 				case 'M':
1108 					mtflag = 1;
1109 					break;
1110 				case 'i' :
1111 					if (++i == argc) {
1112 						return (0);
1113 					}
1114 					inline_size = atoi(argv[i]);
1115 					goto nextarg;
1116 				case 'n':
1117 				case 'o':
1118 				case 's':
1119 					if (argv[i][j - 1] != '-' ||
1120 					    argv[i][j + 1] != 0) {
1121 						return (0);
1122 					}
1123 					flag[(int)c] = 1;
1124 					if (++i == argc) {
1125 						return (0);
1126 					}
1127 					if (c == 'o') {
1128 						if (cmd->outfile) {
1129 							return (0);
1130 						}
1131 						cmd->outfile = argv[i];
1132 					}
1133 					goto nextarg;
1134 				case 'D':
1135 					if (argv[i][j - 1] != '-') {
1136 						return (0);
1137 					}
1138 					(void) addarg(argv[i]);
1139 					goto nextarg;
1140 				case 'Y':
1141 					if (++i == argc) {
1142 						return (0);
1143 					}
1144 					if (strlcpy(pathbuf, argv[i],
1145 					    sizeof(pathbuf)) >= sizeof(pathbuf)
1146 					    || strlcat(pathbuf, "/cpp",
1147 					    sizeof(pathbuf)) >=
1148 					    sizeof(pathbuf)) {
1149 						warnx("argument too long");
1150 						return (0);
1151 					}
1152 					CPP = pathbuf;
1153 					goto nextarg;
1154 
1155 
1156 
1157 				default:
1158 					return (0);
1159 				}
1160 			}
1161 		nextarg:
1162 			;
1163 		}
1164 	}
1165 
1166 	cmd->cflag = flag['c'];
1167 	cmd->hflag = flag['h'];
1168 	cmd->lflag = flag['l'];
1169 	cmd->mflag = flag['m'];
1170 	cmd->nflag = flag['n'];
1171 	cmd->sflag = flag['s'];
1172 	cmd->tflag = flag['t'];
1173 	cmd->Ssflag = flag['S'];
1174 	cmd->Scflag = flag['C'];
1175 	cmd->makefileflag = flag['M'];
1176 
1177 	if (tirpcflag) {
1178 		if (inetdflag)
1179 			pmflag = 0;
1180 		if ((inetdflag && cmd->nflag)) {
1181 			/* netid not allowed with inetdflag */
1182 			warnx("cannot use netid flag with inetd flag");
1183 			return (0);
1184 		}
1185 	} else {		/* 4.1 mode */
1186 		pmflag = 0;	/* set pmflag only in tirpcmode */
1187 		if (cmd->nflag) { /* netid needs TIRPC */
1188 			warnx("cannot use netid flag without TIRPC");
1189 			return (0);
1190 		}
1191 	}
1192 
1193 	if (newstyle && (tblflag || cmd->tflag)) {
1194 		warnx("cannot use table flags with newstyle");
1195 		return (0);
1196 	}
1197 
1198 	/* check no conflicts with file generation flags */
1199 	nflags = cmd->cflag + cmd->hflag + cmd->lflag + cmd->mflag +
1200 		cmd->sflag + cmd->nflag + cmd->tflag + cmd->Ssflag +
1201 			cmd->Scflag + cmd->makefileflag;
1202 
1203 	if (nflags == 0) {
1204 		if (cmd->outfile != NULL || cmd->infile == NULL) {
1205 			return (0);
1206 		}
1207 	} else if (cmd->infile == NULL &&
1208 	    (cmd->Ssflag || cmd->Scflag || cmd->makefileflag)) {
1209 		warnx("\"infile\" is required for template generation flags");
1210 		return (0);
1211 	} if (nflags > 1) {
1212 		warnx("cannot have more than one file generation flag");
1213 		return (0);
1214 	}
1215 	return (1);
1216 }
1217 
1218 static void
1219 usage(void)
1220 {
1221 	f_print(stderr, "%s\n%s\n%s\n%s\n%s\n",
1222 		"usage: rpcgen infile",
1223 		"       rpcgen [-abCLNTM] [-Dname[=value]] [-i size]\
1224 [-I -P [-K seconds]] [-Y path] infile",
1225 		"       rpcgen [-c | -h | -l | -m | -t | -Sc | -Ss | -Sm]\
1226 [-o outfile] [infile]",
1227 		"       rpcgen [-s nettype]* [-o outfile] [infile]",
1228 		"       rpcgen [-n netid]* [-o outfile] [infile]");
1229 	options_usage();
1230 	exit(1);
1231 }
1232 
1233 static void
1234 options_usage(void)
1235 {
1236 	f_print(stderr, "options:\n");
1237 	f_print(stderr, "-a\t\tgenerate all files, including samples\n");
1238 	f_print(stderr, "-b\t\tbackward compatibility mode (generates code \
1239 for FreeBSD 4.X)\n");
1240 	f_print(stderr, "-c\t\tgenerate XDR routines\n");
1241 	f_print(stderr, "-C\t\tANSI C mode\n");
1242 	f_print(stderr, "-Dname[=value]\tdefine a symbol (same as #define)\n");
1243 	f_print(stderr, "-h\t\tgenerate header file\n");
1244 	f_print(stderr, "-i size\t\tsize at which to start generating\
1245 inline code\n");
1246 	f_print(stderr, "-I\t\tgenerate code for inetd support in server\n");
1247 	f_print(stderr, "-K seconds\tserver exits after K seconds of\
1248 inactivity\n");
1249 	f_print(stderr, "-l\t\tgenerate client side stubs\n");
1250 	f_print(stderr, "-L\t\tserver errors will be printed to syslog\n");
1251 	f_print(stderr, "-m\t\tgenerate server side stubs\n");
1252 	f_print(stderr, "-M\t\tgenerate MT-safe code\n");
1253 	f_print(stderr, "-n netid\tgenerate server code that supports\
1254 named netid\n");
1255 	f_print(stderr, "-N\t\tsupports multiple arguments and\
1256 call-by-value\n");
1257 	f_print(stderr, "-o outfile\tname of the output file\n");
1258 	f_print(stderr, "-P\t\tgenerate code for port monitoring support in server\n");
1259 	f_print(stderr, "-s nettype\tgenerate server code that supports named\
1260 nettype\n");
1261 	f_print(stderr, "-Sc\t\tgenerate sample client code that uses remote\
1262 procedures\n");
1263 	f_print(stderr, "-Ss\t\tgenerate sample server code that defines\
1264 remote procedures\n");
1265 	f_print(stderr, "-Sm \t\tgenerate makefile template \n");
1266 
1267 	f_print(stderr, "-t\t\tgenerate RPC dispatch table\n");
1268 	f_print(stderr, "-T\t\tgenerate code to support RPC dispatch tables\n");
1269 	f_print(stderr, "-Y path\t\tpath where cpp is found\n");
1270 	exit(1);
1271 }
1272