Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
RXGEN(1)		     AFS Command Reference		      RXGEN(1)

NAME
       rxgen - Stub generator for the Rx remote	procedure call package

SYNOPSIS
       rxgen [-h | -c |	-C | -S	| -r] [-dkpR]
	   [-I dir] [-P	prefix]	[-o outfile] [infile]

       rxgen -s	transport [-o outfile] [infile]

       rxgen -l	[-o outfile] [infile]

       rxgen -m	[-o outfile] [infile]

DESCRIPTION
       rxgen is	a tool that generates C	code to	implement the Rx RPC protocol;
       it takes	as input a description of an application interface similar to
       C and produces a	number of server and/or	client stub routines to	be
       linked with RPC-based programs.	These stubs allow programs to invoke
       remote procedures through local procedure calls.	 rxgen is an extension
       of Sun's	rpcgen (version	3.9) and retains full rpcgen functionality (at
       least as	of that	version).  Please refer	to rpcgen(1) for more details
       on the Sun's RPC	specific flags,	and to the RPC programming guide
       regarding the RPC language along	with useful examples.

OPTIONS
       rxgen operates in several different modes.  The generated output	files
       can be produced individually (using one of -h, -c, -C, or -S) or
       collectively.  All output files are created when	the default is used
       (i.e., no options), or the output is limited to the server stubs	(-C
       and -S) when the	-r flag	is used.  The following	describes the types of
       generated output	files (for simplicity, filename	refers to the main
       output filename):

       -h  Generate C data definitions (a header file) from standard RPCL
	   definitions (default	extension: filename.h).

       -c  Compile the XDR routines required to	serialize the protocol
	   described by	RPCL.  Generate	XDR routines for all declarations
	   (default extension: filename.xdr.c).

       -C  Generate all	the client-side	stub routines (default extension:
	   filename.cs.c).  Calling a routine in this file will	cause the
	   arguments to	be packed up and sent via Rx (or R).

       -S  Generate all	the server-side	stub routines (default extension:
	   filename.ss.c).  Arguments are unpacked, and	the corresponding
	   server routine is called.

       -r  Generate the	two default extension files produced by	the -C and -S
	   options.

       The following options can be used on any	combination of rxgen calls:

       -R  Generate code for the older \R protocol, as opposed to Rx, which is
	   the default.

       -k  Must	be specified when the generated	code is	intended to be used by
	   the kernel; special "includes" and other specifics are produced
	   when	the target output is for the kernel.

       -p  Package combination flag: when multiple packages are	included
	   within a single specification file, a single	Execute	Request
	   routine will	be used	for all	of them	as a result of this flag.  The
	   default is to generate individual Execute Request stubs for each
	   package.

       -I dir
	   Similar to the -I flag in the C compiler (cc). This flag is passed
	   to the pre-processor	(cpp) so that directory	dir is searched	before
	   the standard	lookup list for	#include files.	 As expected, multiple
	   -I flags can	be used	simultaneously.

       -P prefix
	   The prefix string following this switch is prepended	to all
	   generated output files; useful when multiple	runs want to produce
	   different versions of the same interface (say, kernel and non-
	   kernel versions).

       -d  Debugging mode; only	needed when rxgen is to	be debugged (say, via
	   dbx).

       -o outfile
	   Specify the name of the output file.	 If none is specified, the
	   standard output is used (-c,	-h, -C,	and -S modes only).  Note that
	   if an output	file is	specified in a multi-output file option	(such
	   as the default, or with option -r), then the	outfile	replaces the
	   name	generated by default (which is based on	the configuration's
	   main	file name).

       The -s, -l, and -m options are present only for rpcgen support.	See
       rpcgen(1) for information on their use.

rxgen SYNTAX SUMMARY
	   Specification file:

	       <Package	description option> |
	       <Prefix description option> |
	       <StartingOpcode description option> |
	       <SplitPrefix description	option>	|
	       <Procedure description option> |
	       <RPCL language description option>

	   <Package description	option>:

	       "package" <Package_ident>

	   <Prefix description option>:

	       "prefix"	<Prefix_ident>

	   <StartingOpcode description option>:

	       "startingopcode"	<constant>

	   <SplitPrefix	description option>:

	       "splitprefix" <split options> ";"

	   <Split options>:

	       "IN =" <Start_prefix_ident> "|"
	       "OUT =" <End_prefix_ident> "|"
	       <Split options>

	   <Procedure description option>:

	       ["proc"]	[<Procedure_ident>] [<ServerStub_ident>]
		   <Argument list> ["split" | "multi"]
		   ["="	<Opcode_ident>]	";"

	   <Argument list>:

	       "(" <Argument definition> <Comma_joined argument> ")"

	   <Argument definition>:

	       <Direction option> <Standard RPCL type decl> <Arg_ident>
		   ["<"	<Max_size> ">" | "[" <Max_size>	"]"] | NULL

	   <Comma_joined argument>:

	       "," <Argument definition> | NULL

	   <Direction option>:

	       "IN" | "OUT" | "INOUT" |	NULL

	   <Max_size>:

	       <constant> | NULL

	   <Package_ident>:
	   <Prefix_ident>:
	   <String_ident>:
	   <Start_prefix_ident>:
	   <End_prefix_ident>:
	   <Procedure_ident>:
	   <ServerStub_ident>:
	   <Arg_ident>:
	   <Opcode_ident>:

	       <identifier>

	   <RPCL language description option>:
	   <Standard RPCL type decl>:

	       Sun's RPCL language syntax (see rpcgen(1))

rxgen COMMANDS
   Comments and	Preprocessing
       The input interface may contain preprocessor directives which are
       passed through the C preprocessor (i.e. "cpp").	Since the preprocessor
       runs on all input files before they are actually	interpreted by rxgen,
       all cpp directives (#include, #ifdefs, #defines,	etc.) are legal	and
       welcomed	within an rxgen	input file.  Of	course,	none of	these
       preprocessor directives will be included	in any of the generated	files.
       To facilitate distinctions between the different	types of output	files,
       rxgen defines certain special cpp symbols for use by the	rxgen
       programmer.  These are RPC_HDR (defined when compiling into header,
       filename.h, files), RPC_XDR (defined when compiling into	xdr,
       filename.xdr.c, files), RPC_CLIENT (defined when	compiling into client
       stubs, filename.cs.c, files), and RPC_SERVER (defined when compiling
       into server stubs, filename.ss.c, files).

       In addition, rxgen does a little	preprocessing of its own.  Any line
       beginning with "%" is passed directly into the output file,
       uninterpreted by	rxgen.	For a more heavy en masse dumping of
       uninterpreted code, it would be advised to include all such code	in an
       "#include" file and pass	it in preceded by "%".	The input interface
       may also	contain	any C-style comments which are,	of course, ignored.
       Interpretation is token-based, thus special line-orientation of
       separate	statements is not necessary.  rxgen also provides a quite rich
       and helpful set of error	reports, identifying them by exact line
       location	and error type.	 Also, rxgen will automatically	generate
       #include	lines for standard include files, such as rx/xdr.h and
       rx/rx.h,	along with the generated header	file from this interface.

   Prefixing stub procedures
       The package statement tells rxgen the name of the interface package.
       It is used for prefixing	the naming of all generated stub routines and
       the execute request procedure.  For example:

	   package AFS_

       causes the execute request procedure to be named	AFS_ExecuteRequest
       (Warning: in the	older version an additional "_"	was appended after the
       package name to the ExecuteRequest name;	thus make sure you don't have
       an ExecuteRequest interface routine) and	a given	stub routine, say
       Fetch, to be actually named AFS_Fetch.  Multiple	package	statements
       (current	maximum	size is	10) per	configuration are permitted and	are
       useful when multiple sets of interfaces are implemented (see the
       example at the end).  Note that in such cases, use of the -p flag
       results in the generation of just one ExecuteRequest procedure which
       recognizes the multiple interfaces and whose name is prefixed by	the
       first package statement.	 In the	default	case, independent
       ExecuteRequest procedures will be created for each packaged group of
       remote procedure	calls.

       The prefix statement supplies a name to prepend to all calls to remote
       procedure names in the ExecuteRequest stub routine.  It is useful when
       the server makes	RPC calls to other servers (say, for debugging
       purposes).  For example:

	   prefix S

       causes the name "S" to be prepended to the name of all routines called
       from the	server stubs.  The server can then call	the original name and
       get the client stubs.

   rxgen procedure declaration
       The proc	statement is the most common (and meaningful) in the rxgen
       interface.  Its syntax description is:

	       [proc] [<proc_name>] [<server_stub>] (<arg>, ..., <arg>)
		   [split | multi] [= <opcode>]	;

       where:

       o "proc"	is an optional prefix of the procedure statement. This is just
	 a stylistic item and not a required procedure delimiter.

       o <proc_name> is	the name of the	procedure.  Note that even the name of
	 the procedure is optional.  This only makes sense when	the name of
	 the given procedure is	identical to the name of the last package
	 statement (i.e., "package RCallBack" and the declaration of the
	 "RCallBack" procedure).

       o <server_stub>,	if present, causes the ExecuteRequest procedure	to
	 call that stub	instead	of the automatically generated stub when a
	 call with that	opcode is decoded.

       o <opcode> is a constant	or symbol that is the opcode for that
	 procedure.  One might use the preprocessor features (i.e., #define),
	 the const RPC-language	feature, or the	old good constants as opcodes.
	 Some further evaluation/processing of opcodes is done.	 Particularly,
	 checks	for duplicate and non-existent opcodes are performed, along
	 with checks for "holes" (i.e.,	gaps in	consecutive opcodes) in	the
	 opcode	sequences.  For	example, we use	the fact that when "holes" in
	 opcodes exist,	the ExecuteRequest procedure uses the case statement
	 rather	than the faster	(and smaller, codewise)	indexed	array method.

	 Also, rxgen defines (i.e., appends to the header file)	three valuable
	 macros	for each package group:	<package-name>LOWEST_OPCODE,
	 <package-name>HIGHEST_OPCODE, and <package-name>NUMBER_OPCODES.
	 These may be useful to	the rxgen programmer.  Also, notice that the
	 opcode	statement is an	optional feature, and can be omitted.  In such
	 cases,	automatic opcode numbers are generated sequentially, starting
	 from 0.

	 One can change	the initial opcode number by using the startingopcode
	 (for lack of a	better name) rxgen command.  Its syntax	is:

	     startingopcode <constant>

	 where <constant> must be reasonable!  Note that one can not mix
	 procedures, some with opcodes and some	without, nor allow opcodes
	 after the specification of the	startingopcode statement.  rxgen will
	 complain in all such cases.

       o The argument entry represents a given parameter of the	procedure.
	 Its syntax is:

	     [IN | INOUT | OUT | <null>] <type_decl> <arg_name>
		 [<max>|<>|[max]|[]]

	 If the	type is	an indirect type (i.e.,	is followed by *), it is
	 assumed that the pointer should be followed one level and the data
	 pointed to is to be transmitted. This should normally be used for all
	 structures/arrays and out parameters.	A noticeable exception is when
	 explicit array/structure maximum size is given; since no array-of-
	 pointer declarations are allowed one should use typedefs to achieve
	 the similar effect.  The parameters could be input parameters
	 (preceded by IN), output parameters (preceded by OUT),	or
	 input/output parameters (preceded by INOUT).  If not specified, then
	 the direction of the previous parameter in the	procedure is used.
	 (Note:	the first parameter must be preceded by	the directional
	 primitive!)

       o "split" is a hack to handle stub routines that	do things such as file
	 transfers or any other	operation that has to exchange information
	 (e.g.,	length of a file) before the call returns its output
	 parameters.  Because of the particular	handshake that is involved
	 when doing remote file	transfer, we currently break all such calls
	 into two client-side stub routines.  The first	(with the default
	 prefix	of "Begin") is used to pass all	IN and INOUT parameters	to the
	 server	side.  The second (with	the default prefix of "End") is	used
	 to get	back the INOUT and OUT parameters from the server.  Between
	 the two calls,	the user is supposed to	do the appropriate calls for
	 the file transfer. For	example, the following procedure declaration
	 in package AFS_

	     Fetch (IN a, b,INOUT c, OUT d) split = FETCHOPCODE;

	 will roughly generate the two independent client stub routines:

	     BeginAFS_Fetch (IN	a, b, c)

	 and

	     EndAFS_Fetch(OUT c, d)

	 The splitprefix statement is used to change the default prefix	names
	 used by the two client-side stub generated routines when dealing with
	 file transfer-related procedure calls.	 For example:

	     splitprefix IN=Before_ OUT=After_

	 will cause the	naming of the two client stubs for a file transfer-
	 related routine, say Fetch(), to be Before_AFS_Fetch()	and
	 After_AFS_Fetch(), respectively.

       o The "multi" option is nearly identical	to the "split" feature
	 described above.  The only significant	visible	difference is that
	 along with the	two client stubs, the standard client stub is also
	 generated.  Since the intention is to handle the multi-Rx calls, we
	 need the whole	standard procedure stub	in the cases where no multi-Rx
	 call of the procedure is performed.  A	side effect of the "multi"
	 option	is the generation of a special macro (i.e.,
	 "multi_<Procedure-name>" which	passes back as arguments the "Begin"
	 and "End" stubs in the	header output file. This macro is used
	 directly by the Rx code when a	multi-Rx call of this procedure	is
	 performed.

   OBSOLETE rxgen FEATURES
       Although	the following rxgen commands are still in effect, they will
       soon be removed since there are better alternatives. DO NOT USE THEM!

       The special statement is	a temporary hack used to handle	certain
       inefficiencies of standard xdr routines to handle some user-customized
       declarations.  In particular, this applies to a string pointer
       specified as part of a declaration.  For	example,

	   special struct BBS SeqBody;

       tells rxgen that	the entry "SeqBody" in the user-defined	BBS xdr
       routine is a string (note that more than	one string can be "special"
       per structure --	multiple ones are separated by commas);	it will	thus
       allocate	and de-allocate	space properly in the server-generated stubs
       that contain this structure as an IN or INOUT parameter.

       A better	alternative to special is the customized statement, which is
       simply the "customized" token followed by the regular declaration of a
       struct based on the RPCL	rules. In this case, the declaration will be
       included	in the generated header	file (-h option) but no	xdr routine
       will be generated for this structure -- the user	will supply this.  All
       pointer entries in this structure will be remembered so when the
       structure is used as an IN or INOUT in the server stub, no core leaks
       will occur.  For	example, consider

	   customized struct CBS {
	       long Seqlen;
	       char *SeqBody;
	   }

       The "xdr_CBS" routine would be provided by the user where during	the
       DECODE xdr opcode, appropriate space for	the "SeqBody" string is
       allocated.  Similarly, that space is freed during the FREE xdr opcode.

       Note: Old style "Array parameter	specifications"	are not	supported any
       more.

EXAMPLES
       In case there are some requirements not available by the	current	RPC
       language, one can customize some	XDR routines by	leaving	those data
       types undefined.	For every data type that is undefined, it will be
       assumed that a routine exists with the name "xdr_" prepended to it.  A
       selected	set of rxgen features is presented below, but for a more
       comprehensive one (unions, complex examples, etc) please	refer to the
       rpcgen Programming Guide	and eXternal Data Representation: Sun
       Technical Notes.

   Typedefs
       The RPC typedef statement is identical to the C typedef (i.e. "typedef
       <declaration>").	 By default, most user declarations (i.e. structs,
       unions, etc) are	automatically typedef'ed by rxgen.  Since it makes
       parsing simpler,	its usage is recommended by rxgen scripts.

   Strings
       The C "char *" string convention	is kind	of ambiguous, since it is
       usually intended	to mean	a null-terminated string of characters,	but it
       could also represent a pointer to a single character, a pointer to an
       array of	characters, etc.  In the RPC language, a null-terminated
       string is unambiguously called a	"string".  Examples,

	   string bigname<>;
	   string name<MAXNAMELEN>;
	   typedef string volname<MAXVOLNAME>;

       Notice that the maximum size of string can be arbitrary (like "bigname"
       above) or, preferably, or specified in angle brackets (i.e. "name" and
       "volname" above).  In practice, one should always use only bounded
       strings in interfaces.  A sample	calling	proc using the declarations
       above would be:

	   GetEntryByName (IN volname name,
	       OUT struct vldbentry *entry) = VL_GETENTRYBYNAME;

       or, of course,

	   GetEntryByName (IN string volname<MAXVOLNAME>,
	       OUT struct vldbentry *entry) = VL_GETENTRYBYNAME;

       It is very important for	the user to understand when the	string
       parameters should be allocated and/or freed by the his/her client
       and/or server programs. A short analysis	on string parameters handling
       follows (note that a similar method is used for the handling of
       variable	length arrays as it will be shown later	on):

       o In the	client side: IN	and INOUT string parameters are	the
	 programmer's responsibility and should	be allocated (static or	via
	 malloc) before	calling	the rpc	and freed (if malloc was used) after
	 the rpc's return in the user's	client program;	of course, for INOUT
	 parameters, the returned string can't be bigger than the malloced
	 input string.

	 OUT string parameters are automatically malloced (based on the	length
	 of the	returned string	and not	the maxsize) by	the rxgen client stubs
	 (in filename.cs.c) and	must be	freed by the client program;
	 admittedly, this could	be somewhat confusing since the	user needs to
	 free something	that he/she didn't allocate.}

       o In the	server side: IN	and INOUT string parameters are	automatically
	 malloced (based on the	size of	incoming strings) by the rxgen server
	 stubs (in filename.ss.c) before they are passed to the	user's server
	 procedure; that space is automatically	freed just before the rxgen
	 server	stub returns; therefore	the user need not do anything special
	 for IN	and INOUT string parameters.

	 OUT string parameters must be malloced	by the user's server procedure
	 (i.e. null pointer is passed to it by the rxgen server	stub) and it
	 is automatically freed	at the end of the rxgen	server stub.  Like in
	 the client side, the OUT parameters are somewhat unorthodox (i.e. the
	 server	routine	must malloc a string without ever freeing it itself;
	 this is done by the rxgen server stub).

       Note that for INOUT and OUT string parameters, in both the client and
       server sides their arguments must be char of pointers (i.e. char	**).

   Pointers
       Pointer declarations in RPC are also exactly as they are	in C (i.e.
       "struct single_vldbentry	*vldblist;").  Of course, one can't send
       pointers	over the network, but one can use XDR pointers for sending
       recursive data types such as lists and trees (an	example	of a linked
       list will be demonstrated shortly).

   Arrays
       Fixed arrays are	just like standard C array declarations	(i.e. "struct
       UpdateEntry entries[20]") without any side effect problems in rxgen.
       Since variable-length arrays have no explicit syntax in C, the angle-
       brackets	are used for it	and the	array declarations are actually
       compiled	into "struct"s.	For example, declarations such as:

	   const   MAXBULKSIZE	   = 10000;
	   const   MAXENTRIES	   = 100;
	   opaque  bulk<MAXBULKSIZE>;		/* At most 10000 items */
	   int	   hosts<>;			/* any number of items */
	   typedef vldbentry blkentries<100>;	/* Preferable array decl */

       are compiled into the following structs:

	   struct {
	       u_int   bulk_len;       /* no of	items */
	       char    *bulk_val;      /* pointer to array */
	   } bulk;

       for the "bulk" array, and similarly for the "blkentries<100>" array,

	   struct {
	       u_int	  blkentries_len;   /* no of items in array */
	       vldbentry  *blkentries_val;  /* pointer to array	*/
	   } blkentries;

       Therefore the user should be aware of the "magically" generated
       structure entries such as the number of items in	the array
       (<array_name>_len) and the pointer to the array (<array_name>_val)
       since some of the entries will have to be filled	in from	the
       client/server programs.	A sample proc would be:

	   typedef vldbentry blkentries<MAXENTRIES>;
	   proc	GetBlk (OUT blkentries *vlentries) = VL_GETBLK;

       or, more	directly,

	   GetBlk(OUT vldbentry	vlentries<MAXENTRIES>) = VL_GETBLK;

       Note that although the latest method is preferable since	one does not
       have to first use the typedef statement (and admittedly,	programmers
       prefer avoiding typedefs), one should realize that rxgen	does the
       structure expansion and the xdr creation	implicitly; therefore the user
       should be aware of the "vldbentries_val"	and "vldbentries_len" fields
       as before (see following	examples).

       Array example I (least desirable)

       Procedure declaration in	the interface configuration:

	   proc	ListAttributes (IN vldblistbyattributes	*attributes,
			INOUT blkentries *vldbentries) = VL_LISTATTRIBUTES;

       Sample CLIENT code:

	   blkentries entries, *pnt;
	   entries.blkentries_len = 10;	  /* max # returned entries */
	   entries.blkentries_val = (vldbentry *)malloc(LEN);
					  /* It	must be	set */

	   code	= VL_ListAttributes(&attributes, &entries);
	   if (!code) {
	       pnt = entries.blkentries_val;
	       for (i=0; i < entries.blkentries_len; i++, pnt++)
		       display_vldbentry(pnt);
	       /* Make sure you	free the allocated space */
	       free((char *)entries.blkentries_val);
	   }

       Sample SERVER code:

	   VL_ListAttributes(attributes, entries)
	   {
	       vldbentry *singleentry =	entries->blkentries_val;
	       entries->blkentries_len = 0;

	       while (copy_to_vldbentry(&vlentry, singleentry))
		   singleentry++, vldbentries->entries_len++;
	   }

       Although	this method for	variable-size arrays works fine, there are
       some major drawbacks.  The array	parameter (i.e.	vldbentries above)
       must be declared	as INOUT since we need to pass the max length of the
       expected	returned array;	more importantly, a big	(depending on the
       value of	"_len")	chunk of junk code is going to be transferred to the
       server as result	of the IN(out) side-effect of the array.  It's an easy
       and convenient method if	the returned array size	can be predicted from
       the start and when the size is quite high.  This	method is included as
       an example of erroneous use (and	abuse) of rxgen	and should not be
       used.

       Array example II	(Desirable method)

       Procedure declaration in	the interface configuration (using Example I
       above):

	   proc	ListAttributes (IN vldblistbyattributes	*attributes,
	       OUT blkentries *vldbentries) = VL_LISTATTRIBUTES;

       Sample CLIENT code:

	   blkentries entries, *pnt;

	   code	= VL_ListAttributes(&attributes, &entries);
	   if (!code) {
	       pnt = entries.blkentries_val;
	       for (i=0; i < entries.blkentries_len; i++, pnt++)
		       display_vldbentry(pnt);
	       /* Make sure you	free the allocated space (by rxgen) */
	       free((char *)entries.blkentries_val);
	   }

       Sample SERVER code:

	   VL_ListAttributes(attributes, entries)
	   {
	       vldbentry *singleentry;
	       entries->blkentries_len = 0;
	       singleentry = entries->blkentries_val
		   = (vldbentry	*)malloc(MAXENTRIES * sizeof(vldbentry));

	       while (copy_to_vldbentry(&vlentry, singleentry))
		       singleentry++, vldbentries->entries_len++;
	   }

       This is the best	(and simplest) way of using variable-size arrays as an
       output parameter.  It is	the responsibility of the server-side stub to
       malloc()	the adequate space which is automatically freed	by the rxgen
       stub; the client	side should free the space allocated by	the
       rxgen-calling stub.

       Array example III (Linked Lists)

       Considering the following 3 declarations	(could have applied some
       optimizations) in the configuration file:

	   typedef struct single_vldbentry *vldblist;
	   struct single_vldbentry {
	       vldbentry vlentry;
	       vldblist	 next_vldb;
	   };

	   struct vldb_list {
	       vldblist	node;
	   };

       and the rxgen procedure declaration:

	   LinkedList (IN vldblistbyattributes *attributes,
	       OUT vldb_list *linkedentries) = VL_LINKEDLIST;

       Sample CLIENT code:

	   vldb_list	   linkedvldbs;
	   vldblist	   vllist, vllist1;

	   bzero(&linkedvldbs, sizeof(vldb_list));
	   code	= VL_LinkedList(&attributes, &nentries,	&linkedvldbs);
	   if (!code) {
	       printf("We got %d vldb entries\n", nentries);
	       for (vllist = linkedvldbs.node; vllist; vllist =	vllist1) {
		   vllist1 = vllist->next_vldb;
		   display_entry(&vllist->vlentry);
		   free((char *)vllist);
	       }
	   }

       Sample SERVER code:

	   VL_LinkedList(rxcall, attributes, nentries, linkedvldbs);
	   {
	       vldblist	vllist,	*vllistptr = &linkedvldbs->node;
	       while (...) {
		   vllist = *vllistptr
		       = (single_vldbentry *)malloc (sizeof (single_vldbentry));
		   copy_to_vldbentry(&tentry, &vllist->vlentry);
		   nentries++;
		   vllistptr = &vllist->next_vldb;
	       };
	       *vllistptr = NULL;
	   }

       Using a linked list offers many advantages: Nothing is passed to	the
       server (the parameter is	OUT), no additional overhead is	involved, and
       the caller doesn't have to explicitly prepare for an arbitrary return
       size.  A	drawback is that the caller has	the responsibility of malloc()
       (on the server) and free	(on the	client)	of each	entry (to avoid
       unwanted	core-leaks).  Another drawback is that since it's a recursive
       call, the C stack will grow linearly with respect to the	number of
       nodes in	the list (so it's wise to increase the Rx LWP stack if huge
       amounts of data are expected back -- default stack size is 4K).	The
       advantages should outweigh the disadvantages here.

       It's important to pay attention to the comments of the three array
       examples	above particularly when	they're	references to when the user
       should allocate/free space for the variable length arrays.  The
       mechanism is very similar to the	handling of strings thus you might
       need to review the strings section above; note that the linked lists
       are handled somewhat differently...

   Miscellaneous examples
       Below is	an abbreviated version of a random interface file which	shows
       some of the common cases.

	   /* Declaration of all structures used by the	R.xg script interface */

	   struct AFSFid {
	       unsigned	long Volume;
	       unsigned	long Vnode;
	       unsigned	long Unique;
	   };

	   typedef long	ViceDataType;

	   /* Note that	TEST would be equivalent to "HEADER" only during the
	      processing of the	header,	*.h, file */

	   #ifdef RPC_HDR
	   #define TEST	"HEADER"
	   #else
	   #define TEST	"REST"
	   #endif

	   /* This is the standard *.xg	specification file */

	   package AFS_
	   splitprefix IN=BEFORE_ OUT=AFTER_;
	   Prefix Test

	   proc	Remove(IN struct AFSFid	*Did, IN string	volname<64>,
	       OUT struct AFSStatus *Status) = AFS_REMOVE;

	   DisconnectFS	AUX_disconnectFS() = AFS_DISCONNECTFS;

	   proc	GetVolumeInfo(IN string	Vid,
	       OUT struct VolumeInfo *Info) = AFS_GETVOLUMEINFO;

	   /* You could	have more than an interface per	configuration */

	   package VOTE_

	   /* Using the	"multi"	feature; thus VOTE_Beacon can be called	as an
	      multi-Rx call or as a regular call */

	   Beacon (IN long state, long voteStart,
	       net_version *version, net_tid *tid)
	       multi = VOTE_BEACON;

	   package DISK_

	   /* Using the	"split"	feature	*/

	   SendFile (IN	long file, long	offset,
	       long length, net_version	*version)
	       split = DISK_SENDFILE;

   Output of an	actual interface configuration
       We'll demonstrate some of the actual output generated by	rxgen by
       following an abbreviated	actual interface configuration.

       Configuration file

       Contents	of the interface configuration file (vldbint.xg):

	   package VL_
	   #include "vl_opcodes.h"   /*	The opcodes are	included here */
	   %#include "vl_opcodes.h"  /*	directly to other places */

	   /* Current limitations on parameters	that affect other packages
	      (i.e. volume) */

	   const   MAXNAMELEN	   =	   65;
	   const   MAXNSERVERS	   =	   8;
	   const   MAXTYPES	   =	   3;

	   /* External (visible) representation	of an individual vldb entry */

	   struct vldbentry {
	       char    name[MAXNAMELEN];
	       long    volumeType;
	       long    nServers;
	       long    serverNumber[MAXNSERVERS];
	       long    serverPartition[MAXNSERVERS];
	       long    serverFlags[MAXNSERVERS];
	       u_long  volumeId[MAXTYPES];
	       long    flags;
	   };

	   typedef struct single_vldbentry  *vldblist;
	   struct single_vldbentry {
	       vldbentry VldbEntry;
	       vldblist	next_vldb;
	   };

	   struct vldb_list {
	       vldblist	node;
	   };

	   /* vldb interface calls */

	   CreateEntry	   (IN long Volid,
			   vldbentry *newentry)	= VLCREATEENTRY;

	   GetEntryByName  (IN string volumename<MAXNAMELEN>,
			   OUT vldbentry *entry) = VLGETENTRYBYNAME;

	   GetNewVolumeId  (IN long bumpcount,
			   OUT long *newvolumid) = VLGETNEWVOLUMEID;

	   ReplaceEntry	   (IN long Volid,
			   long	voltype,
			   vldbentry *newentry,
			   long	ReleaseType) multi = VLREPLACEENTRY;

	   ListAttributes  (IN VldbListByAttributes *attributes,
			   OUT long *nentries,
			   OUT vldbentry bulkentries<MAXVLDBLEN>)
			   = VLLISTATTRIBUTES;

	   LinkedList	   (IN VldbListByAttributes *attributes,
			   OUT long *nentries,
			   OUT vldb_list *linkedentries) = VLLINKEDLIST;

       We'll concentrate only on the Rx	generated code since the R generated
       code (-R	option)	will soon be obsolete.	For a detailed description on
       the Rx-related calls inside the generated stubs (i.e., rx_NewCall(),
       rx_EndCall()), along with details on what happens inside	certain	calls
       (like xdrrx_create()) please refer to the Rx documentation. Typing
       "rxgen vldbint.xg" will result in the creation of four files:
       vldbint.h, vldbint.xdr.c, vldbint.cs.c and vldbint.ss.c.	 A closer look
       at these	files follows.

       Header file (vldbint.h)

	   /* Machine generated	file --	Do NOT edit */

	   #include "vl_opcodes.h"  /* directly	to other places	*/
	   #define MAXNAMELEN 65
	   #define MAXNSERVERS 8
	   #define MAXTYPES 3

	   struct vldbentry {
	       char name[MAXNAMELEN];
	       long volumeType;
	       long nServers;
	       long serverNumber[MAXNSERVERS];
	       long serverPartition[MAXNSERVERS];
	       long serverFlags[MAXNSERVERS];
	       u_long volumeId[MAXTYPES];
	       long flags;
	   };
	   typedef struct vldbentry vldbentry;
	   bool_t xdr_vldbentry();

	   typedef struct single_vldbentry *vldblist;
	   bool_t xdr_vldblist();

	   struct single_vldbentry {
	       vldbentry VldbEntry;
	       vldblist	next_vldb;
	   };
	   typedef struct single_vldbentry single_vldbentry;
	   bool_t xdr_single_vldbentry();

	   struct vldb_list {
	       vldblist	node;
	   };
	   typedef struct vldb_list vldb_list;
	   bool_t xdr_vldb_list();

	   #include <rx/rx_multi.h>
	   #define multi_VL_ReplaceEntry(Volid,	voltype, newentry, ReleaseType)	\
	       multi_Body(StartVL_ReplaceEntry(multi_call, Volid, voltype,
			  newentry, ReleaseType), EndVL_ReplaceEntry(multi_call))

	   typedef struct bulkentries {
	       u_int bulkentries_len;
	       vldbentry *bulkentries_val;
	   } bulkentries;
	   bool_t xdr_bulkentries();

	   /* Opcode-related useful stats for package: VL_ */
	   #define VL_LOWEST_OPCODE	   501
	   #define VL_HIGHEST_OPCODE	   506
	   #define VL_NUMBER_OPCODES	   6

       Notice that all structures are automatically typedef'ed and all
       "const"s	are converted to "#define"s. Some data structures, such	as
       bulkentries, are	taken from procedure params (from ListAttributes
       proc). Thus, this should	be kept	in mind	when creating stubs piecemeal
       with rxgen (i.e., using the -c, -h, -C, or -S flags).  Also, one	of the
       side effects of the "multi" option (in "ReplaceEntry" proc) is the
       generation of the "multi_VL_ReplaceEntry" above.

       XDR routines for	structures (vldbint.xdr.c)

	   /* Machine generated	file --	Do NOT edit */

	   #include <rx/xdr.h>
	   #include "vldbint.h"

	   #include "vl_opcodes.h"  /* directly	to other places	*/

	   bool_t
	   xdr_vldbentry(xdrs, objp)
	       XDR *xdrs;
	       vldbentry *objp;
	   {
	       if (!xdr_vector(xdrs, (char *)objp->name, MAXNAMELEN,
			       sizeof(char), xdr_char))
		   return (FALSE);
	       if (!xdr_long(xdrs, &objp->volumeType))
		   return (FALSE);
	       if (!xdr_long(xdrs, &objp->nServers))
		   return (FALSE);
	       if (!xdr_vector(xdrs, (char *)objp->serverNumber, MAXNSERVERS,
			       sizeof(long), xdr_long))
		   return (FALSE);
	       if (!xdr_vector(xdrs, (char *)objp->serverPartition,
			       MAXNSERVERS, sizeof(long), xdr_long))
		   return (FALSE);
	       if (!xdr_vector(xdrs, (char *)objp->serverFlags,	MAXNSERVERS,
			       sizeof(long), xdr_long))
		   return (FALSE);
	       if (!xdr_vector(xdrs, (char *)objp->volumeId, MAXTYPES,
			       sizeof(u_long), xdr_u_long))
		   return (FALSE);
	       if (!xdr_long(xdrs, &objp->flags))
		   return (FALSE);
	       return (TRUE);
	   }

	   bool_t
	   xdr_vldblist(xdrs, objp)
	       XDR *xdrs;
	       vldblist	*objp;
	   {
	       if (!xdr_pointer(xdrs, (char **)objp,
				sizeof(struct single_vldbentry),
				xdr_single_vldbentry))
		   return (FALSE);
	       return (TRUE);
	   }

	   bool_t
	   xdr_single_vldbentry(xdrs, objp)
	       XDR *xdrs;
	       single_vldbentry	*objp;
	   {
	       if (!xdr_vldbentry(xdrs,	&objp->VldbEntry))
		   return (FALSE);
	       if (!xdr_vldblist(xdrs, &objp->next_vldb))
		   return (FALSE);
	       return (TRUE);
	   }

	   bool_t
	   xdr_vldb_list(xdrs, objp)
	       XDR *xdrs;
	       vldb_list *objp;
	   {
	       if (!xdr_vldblist(xdrs, &objp->node))
		   return (FALSE);
	       return (TRUE);
	   }

	   bool_t
	   xdr_bulkentries(xdrs, objp)
	       XDR *xdrs;
	       bulkentries *objp;
	   {
	       if (!xdr_array(xdrs, (char **)&objp->bulkentries_val,
			      (u_int *)&objp->bulkentries_len, MAXVLDBLEN,
			      sizeof(vldbentry), xdr_vldbentry))
		   return (FALSE);
	       return (TRUE);
	   }

       Note that the xdr_bulkentries() is automatically	generated as a side
       effect of a procedure parameter declaration.  Thus, if identical
       multiple	type parameter declarations are	used, then multiply-defined
       xdr_* stubs will	be created!  We	felt this was a	better alternative to
       having the rxgen	programmer deal	with types such	as bulkentries_1,
       bulkentries_2...

       Client-Side stub	routines (vldbint.cs.c)

	   /* Machine generated	file --	Do NOT edit */

	   #include <rx/xdr.h>
	   #include <rx/rx.h>
	   #include <afs/rxgen_consts.h>
	   #include "vldbint.h"

	   #include "vl_opcodes.h"  /* directly	to other places	*/

	   int VL_CreateEntry(z_conn, Volid, newentry)
	       register	struct rx_connection *z_conn;
	       long Volid;
	       vldbentry * newentry;
	   {
	       struct rx_call *z_call =	rx_NewCall(z_conn);
	       static int z_op = 501;
	       int z_result;
	       XDR z_xdrs;

	       xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

	       /* Marshal the arguments	*/
	       if ((!xdr_int(&z_xdrs, &z_op))
		    || (!xdr_long(&z_xdrs, &Volid))
		    || (!xdr_vldbentry(&z_xdrs,	newentry))) {
		       z_result	= RXGEN_CC_MARSHAL;
		       goto fail;
	       }

	       z_result	= RXGEN_SUCCESS;
	   fail:
	       return rx_EndCall(z_call, z_result);
	   }

	   int VL_GetEntryByName(z_conn, volumename, entry)
	       register	struct rx_connection *z_conn;
	       char * volumename;
	       vldbentry * entry;
	   {
	       struct rx_call *z_call =	rx_NewCall(z_conn);
	       static int z_op = 504;
	       int z_result;
	       XDR z_xdrs;

	       xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

	       /* Marshal the arguments	*/
	       if ((!xdr_int(&z_xdrs, &z_op))
		    || (!xdr_string(&z_xdrs, &volumename, 65)))	{
		       z_result	= RXGEN_CC_MARSHAL;
		       goto fail;
	       }

	       /* Un-marshal the reply arguments */
	       z_xdrs.x_op = XDR_DECODE;
	       if ((!xdr_vldbentry(&z_xdrs, entry))) {
		       z_result	= RXGEN_CC_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= RXGEN_SUCCESS;
	   fail:
	       return rx_EndCall(z_call, z_result);
	   }

	   int VL_GetNewVolumeId(z_conn, bumpcount, newvolumid)
	       register	struct rx_connection *z_conn;
	       long bumpcount;
	       long * newvolumid;
	   {
	       struct rx_call *z_call =	rx_NewCall(z_conn);
	       static int z_op = 505;
	       int z_result;
	       XDR z_xdrs;

	       xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

	       /* Marshal the arguments	*/
	       if ((!xdr_int(&z_xdrs, &z_op))
		    || (!xdr_long(&z_xdrs, &bumpcount))) {
		       z_result	= RXGEN_CC_MARSHAL;
		       goto fail;
	       }

	       /* Un-marshal the reply arguments */
	       z_xdrs.x_op = XDR_DECODE;
	       if ((!xdr_long(&z_xdrs, newvolumid))) {
		       z_result	= RXGEN_CC_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= RXGEN_SUCCESS;
	   fail:
	       return rx_EndCall(z_call, z_result);
	   }

	   int VL_ReplaceEntry(z_conn, Volid, voltype, newentry, ReleaseType)
	       register	struct rx_connection *z_conn;
	       long Volid, voltype, ReleaseType;
	       vldbentry * newentry;
	   {
	       struct rx_call *z_call =	rx_NewCall(z_conn);
	       static int z_op = 506;
	       int z_result;
	       XDR z_xdrs;

	       xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

	       /* Marshal the arguments	*/
	       if ((!xdr_int(&z_xdrs, &z_op))
		    || (!xdr_long(&z_xdrs, &Volid))
		    || (!xdr_long(&z_xdrs, &voltype))
		    || (!xdr_vldbentry(&z_xdrs,	newentry))
		    || (!xdr_long(&z_xdrs, &ReleaseType))) {
		       z_result	= RXGEN_CC_MARSHAL;
		       goto fail;
	       }

	       z_result	= RXGEN_SUCCESS;
	   fail:
	       return rx_EndCall(z_call, z_result);
	   }

	   int StartVL_ReplaceEntry(z_call, Volid, voltype, newentry, ReleaseType)
	       register	struct rx_call *z_call;
	       long Volid, voltype, ReleaseType;
	       vldbentry * newentry;
	   {
	       static int z_op = 506;
	       int z_result;
	       XDR z_xdrs;

	       xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

	       /* Marshal the arguments	*/
	       if ((!xdr_int(&z_xdrs, &z_op))
		    || (!xdr_long(&z_xdrs, &Volid))
		    || (!xdr_long(&z_xdrs, &voltype))
		    || (!xdr_vldbentry(&z_xdrs,	newentry))
		    || (!xdr_long(&z_xdrs, &ReleaseType))) {
		       z_result	= RXGEN_CC_MARSHAL;
		       goto fail;
	       }

	       z_result	= RXGEN_SUCCESS;
	   fail:
	       return z_result;
	   }

	   int EndVL_ReplaceEntry(z_call)
	       register	struct rx_call *z_call;
	   {
	       int z_result;
	       XDR z_xdrs;

	       z_result	= RXGEN_SUCCESS;
	   fail:
	       return z_result;
	   }

	   int VL_ListAttributes(z_conn, attributes, nentries, bulkentries_1)
	       register	struct rx_connection *z_conn;
	       VldbListByAttributes * attributes;
	       long * nentries;
	       bulkentries * bulkentries_1;
	   {
	       struct rx_call *z_call =	rx_NewCall(z_conn);
	       static int z_op = 511;
	       int z_result;
	       XDR z_xdrs;

	       xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

	       /* Marshal the arguments	*/
	       if ((!xdr_int(&z_xdrs, &z_op))
		    || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) {
		       z_result	= RXGEN_CC_MARSHAL;
		       goto fail;
	       }

	       /* Un-marshal the reply arguments */
	       z_xdrs.x_op = XDR_DECODE;
	       if ((!xdr_long(&z_xdrs, nentries))
		    || (!xdr_bulkentries(&z_xdrs, bulkentries_1))) {
		       z_result	= RXGEN_CC_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= RXGEN_SUCCESS;
	   fail:
	       return rx_EndCall(z_call, z_result);
	   }

	   int VL_LinkedList(z_conn, attributes, nentries, linkedentries)
	       register	struct rx_connection *z_conn;
	       VldbListByAttributes * attributes;
	       long * nentries;
	       vldb_list * linkedentries;
	   {
	       struct rx_call *z_call =	rx_NewCall(z_conn);
	       static int z_op = 512;
	       int z_result;
	       XDR z_xdrs;

	       xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

	       /* Marshal the arguments	*/
	       if ((!xdr_int(&z_xdrs, &z_op))
		    || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) {
		       z_result	= RXGEN_CC_MARSHAL;
		       goto fail;
	       }

	       /* Un-marshal the reply arguments */
	       z_xdrs.x_op = XDR_DECODE;
	       if ((!xdr_long(&z_xdrs, nentries))
		    || (!xdr_vldb_list(&z_xdrs,	linkedentries))) {
		       z_result	= RXGEN_CC_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= RXGEN_SUCCESS;
	   fail:
	       return rx_EndCall(z_call, z_result);
	   }

       Notice the side effect of the "multi" feature (three different modules
       for "ReplaceEntry" proc).

       Server-Side stub	routines (vldbint.ss.c)

	   /* Machine generated	file --	Do NOT edit */

	   #include <rx/xdr.h>
	   #include <rx/rx.h>
	   #include <afs/rxgen_consts.h>
	   #include "vldbint.h"

	   #include "vl_opcodes.h"  /* directly	to other places	*/

	   long	_VL_CreateEntry(z_call,	z_xdrs)
	       struct rx_call *z_call;
	       XDR *z_xdrs;
	   {
	       long z_result;
	       long Volid;
	       vldbentry newentry;

	       if ((!xdr_long(z_xdrs, &Volid))
		    || (!xdr_vldbentry(z_xdrs, &newentry))) {
		       z_result	= RXGEN_SS_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= VL_CreateEntry(z_call, Volid,	&newentry);
	   fail:
	       return z_result;
	   }

	   long	_VL_GetEntryByName(z_call, z_xdrs)
	       struct rx_call *z_call;
	       XDR *z_xdrs;
	   {
	       long z_result;
	       char *volumename	= (char	*)0;
	       vldbentry entry;

	       if ((!xdr_string(z_xdrs,	&volumename, 65))) {
		       z_result	= RXGEN_SS_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= VL_GetEntryByName(z_call, &volumename, &entry);
	       z_xdrs->x_op = XDR_ENCODE;
	       if ((!xdr_vldbentry(z_xdrs, &entry)))
		       z_result	= RXGEN_SS_MARSHAL;
	   fail:
	       z_xdrs->x_op = XDR_FREE;
	       if (!xdr_string(z_xdrs, &volumename, 65)) goto fail1;
	       return z_result;
	   fail1:
	       return RXGEN_SS_XDRFREE;
	   }

	   long	_VL_GetNewVolumeId(z_call, z_xdrs)
	       struct rx_call *z_call;
	       XDR *z_xdrs;
	   {
	       long z_result;
	       long bumpcount;
	       long newvolumid;

	       if ((!xdr_long(z_xdrs, &bumpcount))) {
		       z_result	= RXGEN_SS_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= VL_GetNewVolumeId(z_call, bumpcount, &newvolumid);
	       z_xdrs->x_op = XDR_ENCODE;
	       if ((!xdr_long(z_xdrs, &newvolumid)))
		       z_result	= RXGEN_SS_MARSHAL;
	   fail:
	       return z_result;
	   }

	   long	_VL_ReplaceEntry(z_call, z_xdrs)
	       struct rx_call *z_call;
	       XDR *z_xdrs;
	   {
	       long z_result;
	       long Volid, voltype, ReleaseType;
	       vldbentry newentry;

	       if ((!xdr_long(z_xdrs, &Volid))
		    || (!xdr_long(z_xdrs, &voltype))
		    || (!xdr_vldbentry(z_xdrs, &newentry))
		    || (!xdr_long(z_xdrs, &ReleaseType))) {
		       z_result	= RXGEN_SS_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= VL_ReplaceEntry(z_call, Volid, voltype, &newentry,
					  ReleaseType);
	   fail:
	       return z_result;
	   }

	   long	_VL_ListAttributes(z_call, z_xdrs)
	       struct rx_call *z_call;
	       XDR *z_xdrs;
	   {
	       long z_result;
	       VldbListByAttributes attributes;
	       long nentries;
	       bulkentries bulkentries_1;

	       if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) {
		       z_result	= RXGEN_SS_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= VL_ListAttributes(z_call, &attributes, &nentries,
					    &bulkentries_1);
	       z_xdrs->x_op = XDR_ENCODE;
	       if ((!xdr_long(z_xdrs, &nentries))
		    || (!xdr_bulkentries(z_xdrs, &bulkentries_1)))
		       z_result	= RXGEN_SS_MARSHAL;
	   fail:
	       z_xdrs->x_op = XDR_FREE;
	       if (!xdr_bulkentries(z_xdrs, &bulkentries_1)) goto fail1;
	       return z_result;
	   fail1:
	       return RXGEN_SS_XDRFREE;
	   }

	   long	_VL_LinkedList(z_call, z_xdrs)
	       struct rx_call *z_call;
	       XDR *z_xdrs;
	   {
	       long z_result;
	       VldbListByAttributes attributes;
	       long nentries;
	       vldb_list linkedentries;

	       if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) {
		       z_result	= RXGEN_SS_UNMARSHAL;
		       goto fail;
	       }

	       z_result	= VL_LinkedList(z_call,	&attributes, &nentries,
					&linkedentries);
	       z_xdrs->x_op = XDR_ENCODE;
	       if ((!xdr_long(z_xdrs, &nentries))
		    || (!xdr_vldb_list(z_xdrs, &linkedentries)))
		       z_result	= RXGEN_SS_MARSHAL;
	   fail:
	       return z_result;
	   }

	   long	_VL_CreateEntry();
	   long	_VL_GetEntryByName();
	   long	_VL_GetNewVolumeId();
	   long	_VL_ReplaceEntry();
	   long	_VL_ListAttributes();
	   long	_VL_LinkedList();

	   static long (*StubProcsArray0[])() =	{_VL_CreateEntry,
	       _VL_GetEntryByName, _VL_GetNewVolumeId, _VL_ReplaceEntry,
	       _VL_ListAttributes, _VL_LinkedList};

	   VL_ExecuteRequest(z_call)
	       register	struct rx_call *z_call;
	   {
	       int op;
	       XDR z_xdrs;
	       long z_result;

	       xdrrx_create(&z_xdrs, z_call, XDR_DECODE);
	       if (!xdr_int(&z_xdrs, &op))
		   z_result = RXGEN_DECODE;
	       else if (op < VL_LOWEST_OPCODE || op > VL_HIGHEST_OPCODE)
		   z_result = RXGEN_OPCODE;
	       else
		   z_result = (*StubProcsArray0[op - VL_LOWEST_OPCODE])
		       (z_call,	&z_xdrs);
	       return z_result;
	   }

       If there	were gaps in the procedures' opcode sequence the code for
       VL_ExecuteRequest() routine would be have been drastically different
       (it would have been a case statement for	each procedure).

NOTES
       rxgen is	implemented from Sun's rpcgen utility.	All of the standard
       rpcgen's	functionality is fully maintained.  Note that some active
       rpcgen options that don't apply to rxgen's purpose aren't referenced
       here (i.e., -s, -l, -m options) and the interested reader should	refer
       to rpcgen(1) for	details.

       When the	"%#include <include file"> feature is used make	sure that you
       don't have any rxgen language features (i.e. %#defines) since you'll
       get syntax errors during	compilations..

       Since this is an	ongoing	project	many of	the above may change/disappear
       without a major warning.

SEE ALSO
       Rxgen Syntax Summary: Summary description of rxgen's grammar.

       Rpcgen Programming Guide: Sun's RPC protocol compiler.  rxgen was
       implemented as an extension to that compiler.

       External	Data Representation: Sun Technical Notes: Detailed examples in
       using XDR.

       RPCL Syntax Summary: Summary of Sun's Remote Procedure Call Language.

       Rx: An extended Remote Procedure	Call Protocol.

       rgen: An	earlier	version	of a similar stub generator used for the R RPC
       protocol.

COPYRIGHT
       IBM Corporation 2000. <http://www.ibm.com/> All Rights Reserved.

       This documentation is covered by	the IBM	Public License Version 1.0.
       It was converted	from the original TeX rxgen manual to POD by Russ
       Allbery.

OpenAFS				  2016-12-14			      RXGEN(1)

NAME | SYNOPSIS | DESCRIPTION | OPTIONS | rxgen SYNTAX SUMMARY | rxgen COMMANDS | EXAMPLES | NOTES | SEE ALSO | COPYRIGHT

Want to link to this manual page? Use this URL:
<https://www.freebsd.org/cgi/man.cgi?query=rxgen&sektion=1&manpath=FreeBSD+12.0-RELEASE+and+Ports>

home | help