man-page-rxgen-cmdebug-20051227
authorRuss Allbery <rra@stanford.edu>
Tue, 27 Dec 2005 23:30:22 +0000 (23:30 +0000)
committerRuss Allbery <rra@stanford.edu>
Tue, 27 Dec 2005 23:30:22 +0000 (23:30 +0000)
Add man pages for rxgen and cmdebug.  The cmdebug man page was written from
scratch based on the source code.  The rxgen man page is a conversion of an
old TeX document to POD.

doc/man-pages/pod1/cmdebug.pod [new file with mode: 0644]
doc/man-pages/pod1/rxgen.pod [new file with mode: 0644]

diff --git a/doc/man-pages/pod1/cmdebug.pod b/doc/man-pages/pod1/cmdebug.pod
new file mode 100644 (file)
index 0000000..36d981d
--- /dev/null
@@ -0,0 +1,104 @@
+=head1 NAME
+
+cmdebug - Reports the status of a host Cache Manager
+
+=head1 SYNOPSIS
+
+B<cmdebug> B<-servers> <I<machine>> [B<-port> <I<IP port>>]
+    [B<-long>] [B<-refcounts>] [B<-callbacks>] [B<-addrs>] [B<-cache>]
+
+B<cmdebug> B<-s> <I<machine>> [B<-p> <I<IP port>>] [B<-l>] [B<-r>] [B<-c>]
+    [B<-a>] [B<-h>]
+
+=head1 DESCRIPTION
+
+The B<cmdebug> command displays information about the Cache Manager and
+client cache status on an AFS client machine.  By default, it displays all
+locked cache entries, but other information can be requested via various
+options.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-servers> <I<machine>>
+
+Names the client machine for which to display Cache Manager status.
+Provide the machine's IP address in dotted decimal format, its fully
+qualified host name (for example, B<fs1.abc.com>), or the shortest
+abbreviated form of its host name that distinguishes it from other
+machines. Successful use of an abbreviated form depends on the
+availability of a name resolution service (such as the Domain Name Service
+or a local host table) at the time the command is issued.
+
+=item B<-port> <I<IP port>>
+
+Identifies the port on which to contact the Cache Manager.  By default,
+the standard port 7001 is used, so this option is very rarely needed.
+
+=item B<-long>
+
+Reports on all lock statuses and all cache entries, rather than only
+locked cache entries.  Do not use this option with B<-refcounts>,
+B<-callbacks>, B<-addrs>, or B<-cache>.
+
+=item B<-refcounts>
+
+Reports only those cache entries with non-zero reference counts.  Do not
+use this option with B<-long>, B<-callbacks>, B<-addrs>, or B<-cache>.
+
+=item B<-callbacks>
+
+Reports only those cache entries with callbacks.  Do not use this option
+with B<-long>, B<-refcounts>, B<-addrs>, or B<-cache>.
+
+=item B<-addrs>
+
+Rather than showing any cache entries, displays the interfaces the Cache
+Manager answers on, including their netmasks and MTUs.  This is useful for
+analyzing clients that are multihomed and identifying problems with
+netmasks or MTU settings.  Do not use this option with B<-long>,
+B<-refcounts>, B<-callbacks>, or B<-cache>.
+
+=item B<-cache>
+
+Rather than showing any cache entries, displays the cache configuration
+for the client machine.  The information displayed is essentially the
+information that can be configured via parameters to B<afsd>.  Do not use
+this option with B<-long>, B<-refcounts>, B<-callbacks>, or B<-addrs>.
+
+=item B<-help>
+
+Prints the online help for this command. All other valid options are
+ignored.
+
+=back
+
+=head1 EXAMPLES
+
+Displays all of the locked cache entries on C<client1>:
+
+    % cmdebug client1
+
+Displays the cache configuration for C<client1.abc.com>:
+
+    % cmdebug client1.abc.com -cache
+
+Displays all cache entries for C<client2.abc.com>:
+
+    % cmdebug client2.abc.com -long
+
+=head1 PRIVILEGE REQUIRED
+
+None
+
+=head1 SEE ALSO
+
+L<afsd(8)>
+
+=head1 COPYRIGHT
+
+Copyright 2005 Russ Allbery <rra@stanford.edu>
+
+This documentation is covered by the IBM Public License Version 1.0.  This
+man page was written by Russ Allbery for OpenAFS.
diff --git a/doc/man-pages/pod1/rxgen.pod b/doc/man-pages/pod1/rxgen.pod
new file mode 100644 (file)
index 0000000..d012f0e
--- /dev/null
@@ -0,0 +1,1463 @@
+=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.