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