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