--- /dev/null
+=head1 NAME
+
+rxgen - Stub generator for the Rx remote procedure call package
+
+=head1 SYNOPSIS
+
+B<rxgen> [B<-h> | B<-c> | B<-C> | B<-S> | B<-r>] [B<-dkpR>]
+ [B<-I> I<dir>] [B<-P> I<prefix>] [B<-o> I<outfile>] [I<infile>]
+
+B<rxgen> B<-s> I<transport> [B<-o> I<outfile>] [I<infile>]
+
+B<rxgen> B<-l> [B<-o> I<outfile>] [I<infile>]
+
+B<rxgen> B<-m> [B<-o> I<outfile>] [I<infile>]
+
+=head1 DESCRIPTION
+
+B<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. B<rxgen> is an extension of
+Sun's B<rpcgen> (version 3.9) and retains full B<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.
+
+=head1 OPTIONS
+
+B<rxgen> operates in several different modes. The generated output files
+can be produced individually (using one of B<-h>, B<-c>, B<-C>, or B<-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 (B<-C>
+and B<-S>) when the B<-r> flag is used. The following describes the types
+of generated output files (for simplicity, I<filename> refers to the main
+output filename):
+
+=over 4
+
+=item B<-h>
+
+Generate C data definitions (a header file) from standard RPCL definitions
+(default extension: I<filename>.h).
+
+=item B<-c>
+
+Compile the XDR routines required to serialize the protocol described by
+RPCL. Generate XDR routines for all declarations (default extension:
+I<filename>.xdr.c).
+
+=item B<-C>
+
+Generate all the client-side stub routines (default extension:
+I<filename>.cs.c). Calling a routine in this file will cause the
+arguments to be packed up and sent via Rx (or R).
+
+=item B<-S>
+
+Generate all the server-side stub routines (default extension:
+I<filename>.ss.c). Arguments are unpacked, and the corresponding server
+routine is called.
+
+=item B<-r>
+
+Generate the two default extension files produced by the B<-C> and B<-S>
+options.
+
+=back
+
+The following options can be used on any combination of B<rxgen> calls:
+
+=over 4
+
+=item B<-R>
+
+Generate code for the older \R protocol, as opposed to Rx, which is the
+default.
+
+=item B<-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.
+
+=item B<-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.
+
+=item B<-I> I<dir>
+
+Similar to the B<-I> flag in the C compiler (B<cc>). This flag is passed
+to the pre-processor (B<cpp>) so that directory I<dir> is searched before
+the standard lookup list for #include files. As expected, multiple B<-I>
+flags can be used simultaneously.
+
+=item B<-P> I<prefix>
+
+The I<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).
+
+=item B<-d>
+
+Debugging mode; only needed when B<rxgen> is to be debugged (say, via
+B<dbx>).
+
+=item B<-o> I<outfile>
+
+Specify the name of the output file. If none is specified, the standard
+output is used (B<-c>, B<-h>, B<-C>, and B<-S> modes only). Note that if
+an output file is specified in a multi-output file option (such as the
+default, or with option B<-r>), then the I<outfile> replaces the name
+generated by default (which is based on the configuration's main file
+name).
+
+=back
+
+The B<-s>, B<-l>, and B<-m> options are present only for B<rpcgen>
+support. See rpcgen(1) for information on their use.
+
+=head1 B<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))
+
+=head1 B<rxgen> COMMANDS
+
+=head2 Comments and Preprocessing
+
+The input interface may contain preprocessor directives which are passed
+through the C preprocessor (i.e. C<cpp>). Since the preprocessor runs on
+all input files before they are actually interpreted by B<rxgen>, all
+B<cpp> directives (#include, #ifdefs, #defines, etc.) are legal and
+welcomed within an B<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,
+B<rxgen> defines certain special B<cpp> symbols for use by the B<rxgen>
+programmer. These are RPC_HDR (defined when compiling into header,
+I<filename>.h, files), RPC_XDR (defined when compiling into xdr,
+I<filename>.xdr.c, files), RPC_CLIENT (defined when compiling into client
+stubs, I<filename>.cs.c, files), and RPC_SERVER (defined when compiling
+into server stubs, I<filename>.ss.c, files).
+
+In addition, B<rxgen> does a little preprocessing of its own. Any line
+beginning with C<%> is passed directly into the output file, uninterpreted
+by B<rxgen>. For a more heavy en masse dumping of uninterpreted code, it
+would be adviced to include all such code in an C<#include> file and pass
+it in preceded by C<%>. 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.
+B<rxgen> also provides a quite rich and helpful set of error reports,
+identifying them by exact line location and error type. Also, B<rxgen>
+will automatically generate #include lines for standard include files,
+such as F<rx/xdr.h> and F<rx/rx.h>, along with the generated header file
+from this interface.
+
+=head2 Prefixing stub procedures
+
+The I<package> statement tells B<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 C<_> 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 B<-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 I<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 C<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.
+
+=head2 B<rxgen> procedure declaration
+
+The I<proc> statement is the most common (and meaningful) in the B<rxgen>
+interface. Its syntax description is:
+
+ [proc] [<proc_name>] [<server_stub>] (<arg>, ..., <arg>)
+ [split | multi] [= <opcode>] ;
+
+where:
+
+=over 2
+
+=item *
+
+C<proc> is an optional prefix of the procedure statement. This is just a
+stylistic item and not a required procedure delimiter.
+
+=item *
+
+<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 I<package> statement (i.e.,
+C<package RCallBack> and the declaration of the C<RCallBack> procedure).
+
+=item *
+
+<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.
+
+=item *
+
+<opcode> is a constant or symbol that is the opcode for that procedure.
+One might use the preprocessor features (i.e., #define), the I<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 I<case> statement rather than the faster
+(and smaller, codewise) indexed array method.
+
+Also, B<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 B<rxgen> programmer. Also, notice that the I<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 I<startingopcode>
+(for lack of a better name) B<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 I<startingopcode> statement. B<rxgen> will
+complain in all such cases.
+
+=item *
+
+The I<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!)
+
+=item *
+
+C<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 C<Begin>) is used to pass
+all IN and INOUT parameters to the server side. The second (with the
+default prefix of C<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 I<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.
+
+=item *
+
+The C<multi> option is nearly identical to the C<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 C<multi> option is the generation of a
+special macro (i.e., C<< multi_<Procedure-name> >> which passes back as
+arguments the C<Begin> and C<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.
+
+=back
+
+=head2 OBSOLETE B<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 I<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 B<rxgen> that the entry C<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 I<special> is the I<customized> statement, which
+is simply the C<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 (B<-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 C<xdr_CBS> routine would be provided by the user where during the
+DECODE xdr opcode, appropriate space for the C<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.
+
+=head1 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 C<xdr_> prepended to it. A selected set of
+B<rxgen> features is presented below, but for a more comprehensive one
+(unions, complex examples, etc) please refer to the I<rpcgen Programming
+Guide> and I<eXternal Data Representation: Sun Technical Notes>.
+
+=head2 Typedefs
+
+The RPC typedef statement is identical to the C typedef (i.e. C<< typedef
+<declaration> >>). By default, most user declarations (i.e. structs,
+unions, etc) are automatically typedef'ed by B<rxgen>. Since it makes
+parsing simpler, its usage is recommended by B<rxgen> scripts.
+
+=head2 Strings
+
+The C 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 C<bigname>
+above) or, preferably, or specified in angle brackets (i.e. C<name> and
+C<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):
+
+=over 2
+
+=item *
+
+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 B<rxgen> client stubs (in
+I<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.}
+
+=item *
+
+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 I<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 B<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 B<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 **).
+
+=head2 Pointers
+
+Pointer declarations in RPC are also exactly as they are in C
+(i.e. C<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).
+
+=head2 Arrays
+
+Fixed arrays are just like standard C array declarations (i.e. C<struct
+UpdateEntry entries[20]>) without any side effect problems in
+B<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 C<bulk> array, and similarly for the C<< 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 B<rxgen> does the structure
+expansion and the xdr creation implicitly; therefore the user should be
+aware of the C<vldbentries_val> and C<vldbentries_len> fields as before
+(see following examples).
+
+=head3 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
+C<_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 B<rxgen> and should not be used.
+
+=head3 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 B<rxgen>
+stub; the client side should free the space allocated by the
+B<rxgen>-calling stub.
+
+=head3 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
+outweight 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...
+
+=head2 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;
+
+=head2 Output of an actual interface configuration
+
+We'll demonstrate some of the actual output generated by B<rxgen> by
+following an abbreviated actual interface configuration.
+
+=head3 Configuration file
+
+Contents of the interface configuration file (F<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
+(B<-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 C<rxgen
+vldbint.xg> will result in the creation of four files: F<vldbint.h>,
+F<vldbint.xdr.c>, F<vldbint.cs.c> and F<vldbint.ss.c>. A closer look at
+these files follows.
+
+=head3 Header file (F<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 C<const>s
+are converted to C<#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 B<rxgen> (i.e.,
+using the B<-c>, B<-h>, B<-C>, or B<-S> flags). Also, one of the side
+effects of the C<multi> option (in C<ReplaceEntry> proc) is the generation
+of the C<multi_VL_ReplaceEntry> above.
+
+=head3 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
+B<rxgen> programmer deal with types such as bulkentries_1,
+bulkentries_2...
+
+=head3 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 C<multi> feature (three different modules
+for C<ReplaceEntry> proc).
+
+=head3 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).
+
+=head1 NOTES
+
+B<rxgen> is implemented from Sun's B<rpcgen> utility. All of the standard
+B<rpcgen>'s functionality is fully maintained. Note that some active
+B<rpcgen> options that don't apply to B<rxgen>'s purpose aren't referenced
+here (i.e., B<-s>, B<-l>, B<-m> options) and the interested reader should
+refer to rpcgen(1) for details.
+
+When the C<%#include <include file>> feature is used make sure that you
+don't have any B<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.
+
+=head1 SEE ALSO
+
+I<Rxgen Syntax Summary>: Summary description of rxgen's grammar.
+
+I<Rpcgen Programming Guide>: Sun's RPC protocol compiler. B<rxgen> was
+implemented as an extension to that compiler.
+
+I<External Data Representation: Sun Technical Notes>: Detailed examples in
+using XDR.
+
+I<RPCL Syntax Summary>: Summary of Sun's Remote Procedure Call Language.
+
+I<Rx>: An extended Remote Procedure Call Protocol.
+
+I<rgen>: An earlier version of a similar stub generator used for the R RPC
+protocol.
+
+=head1 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 B<rxgen> manual to POD by Russ
+Allbery.