Remove sunrpc compatibility
[openafs.git] / doc / man-pages / pod1 / rxgen.pod
1 =head1 NAME
2
3 rxgen - Stub generator for the Rx remote procedure call package
4
5 =head1 SYNOPSIS
6
7 =for html
8 <div class="synopsis">
9
10 B<rxgen> [B<-h> | B<-c> | B<-C> | B<-S> | B<-r>] [B<-dkp>]
11     [B<-I> I<dir>] [B<-P> I<prefix>] [B<-o> I<outfile>] [I<infile>]
12
13 =for html
14 </div>
15
16 =head1 DESCRIPTION
17
18 B<rxgen> is a tool that generates C code to implement the Rx RPC protocol;
19 it takes as input a description of an application interface similar to C
20 and produces a number of server and/or client stub routines to be linked
21 with RPC-based programs.  These stubs allow programs to invoke remote
22 procedures through local procedure calls.  B<rxgen> is based on Sun's
23 B<rpcgen> (version 3.9) but does not maintain compatibility with rpcgen
24 RPC descriptions.
25
26 =head1 OPTIONS
27
28 B<rxgen> operates in several different modes.  The generated output files
29 can be produced individually (using one of B<-h>, B<-c>, B<-C>, or B<-S>)
30 or collectively.  All output files are created when the default is used
31 (i.e., no options), or the output is limited to the server stubs (B<-C>
32 and B<-S>) when the B<-r> flag is used.  The following describes the types
33 of generated output files (for simplicity, I<filename> refers to the main
34 output filename):
35
36 =over 4
37
38 =item B<-h>
39
40 Generate C data definitions (a header file) from standard RPCL definitions
41 (default extension: I<filename>.h).
42
43 =item B<-c>
44
45 Compile the XDR routines required to serialize the protocol described by
46 RPCL.  Generate XDR routines for all declarations (default extension:
47 I<filename>.xdr.c).
48
49 =item B<-C>
50
51 Generate all the client-side stub routines (default extension:
52 I<filename>.cs.c).  Calling a routine in this file will cause the
53 arguments to be packed up and sent via Rx (or R).
54
55 =item B<-S>
56
57 Generate all the server-side stub routines (default extension:
58 I<filename>.ss.c).  Arguments are unpacked, and the corresponding server
59 routine is called.
60
61 =item B<-r>
62
63 Generate the two default extension files produced by the B<-C> and B<-S>
64 options.
65
66 =back
67
68 The following options can be used on any combination of B<rxgen> calls:
69
70 =over 4
71
72 =item B<-k>
73
74 Must be specified when the generated code is intended to be used by the
75 kernel; special "includes" and other specifics are produced when the
76 target output is for the kernel.
77
78 =item B<-p>
79
80 Package combination flag: when multiple packages are included within a
81 single specification file, a single Execute Request routine will be used
82 for all of them as a result of this flag.  The default is to generate
83 individual Execute Request stubs for each package.
84
85 =item B<-I> I<dir>
86
87 Similar to the B<-I> flag in the C compiler (B<cc>). This flag is passed
88 to the pre-processor (B<cpp>) so that directory I<dir> is searched before
89 the standard lookup list for #include files.  As expected, multiple B<-I>
90 flags can be used simultaneously.
91
92 =item B<-P> I<prefix>
93
94 The I<prefix> string following this switch is prepended to all generated
95 output files; useful when multiple runs want to produce different versions
96 of the same interface (say, kernel and non-kernel versions).
97
98 =item B<-d>
99
100 Debugging mode; only needed when B<rxgen> is to be debugged (say, via
101 B<dbx>).
102
103 =item B<-o> I<outfile>
104
105 Specify the name of the output file.  If none is specified, the standard
106 output is used (B<-c>, B<-h>, B<-C>, and B<-S> modes only).  Note that if
107 an output file is specified in a multi-output file option (such as the
108 default, or with option B<-r>), then the I<outfile> replaces the name
109 generated by default (which is based on the configuration's main file
110 name).
111
112 =back
113
114 =head1 B<rxgen> SYNTAX SUMMARY
115
116     Specification file:
117
118         <Package description option> |
119         <Prefix description option> |
120         <StartingOpcode description option> |
121         <SplitPrefix description option> |
122         <Procedure description option> |
123         <RPCL language description option>
124
125     <Package description option>:
126
127         "package" <Package_ident>
128
129     <Prefix description option>:
130
131         "prefix" <Prefix_ident>
132
133     <StartingOpcode description option>:
134
135         "startingopcode" <constant>
136
137     <SplitPrefix description option>:
138
139         "splitprefix" <split options> ";"
140
141     <Split options>:
142
143         "IN =" <Start_prefix_ident> "|"
144         "OUT =" <End_prefix_ident> "|"
145         <Split options>
146
147     <Procedure description option>:
148
149         ["proc"] [<Procedure_ident>] [<ServerStub_ident>]
150             <Argument list> ["split" | "multi"]
151             ["=" <Opcode_ident>] ";"
152
153     <Argument list>:
154
155         "(" <Argument definition> <Comma_joined argument> ")"
156
157     <Argument definition>:
158
159         <Direction option> <Standard RPCL type decl> <Arg_ident>
160             ["<" <Max_size> ">" | "[" <Max_size> "]"] | NULL
161
162     <Comma_joined argument>:
163
164         "," <Argument definition> | NULL
165
166     <Direction option>:
167
168         "IN" | "OUT" | "INOUT" | NULL
169
170     <Max_size>:
171
172         <constant> | NULL
173
174     <Package_ident>:
175     <Prefix_ident>:
176     <String_ident>:
177     <Start_prefix_ident>:
178     <End_prefix_ident>:
179     <Procedure_ident>:
180     <ServerStub_ident>:
181     <Arg_ident>:
182     <Opcode_ident>:
183
184         <identifier>
185
186     <RPCL language description option>:
187     <Standard RPCL type decl>:
188
189         Sun's RPCL language syntax (see rpcgen(1))
190
191 =head1 B<rxgen> COMMANDS
192
193 =head2 Comments and Preprocessing
194
195 The input interface may contain preprocessor directives which are passed
196 through the C preprocessor (i.e. C<cpp>).  Since the preprocessor runs on
197 all input files before they are actually interpreted by B<rxgen>, all
198 B<cpp> directives (#include, #ifdefs, #defines, etc.) are legal and
199 welcomed within an B<rxgen> input file.  Of course, none of these
200 preprocessor directives will be included in any of the generated files.
201 To facilitate distinctions between the different types of output files,
202 B<rxgen> defines certain special B<cpp> symbols for use by the B<rxgen>
203 programmer.  These are RPC_HDR (defined when compiling into header,
204 I<filename>.h, files), RPC_XDR (defined when compiling into xdr,
205 I<filename>.xdr.c, files), RPC_CLIENT (defined when compiling into client
206 stubs, I<filename>.cs.c, files), and RPC_SERVER (defined when compiling
207 into server stubs, I<filename>.ss.c, files).
208
209 In addition, B<rxgen> does a little preprocessing of its own.  Any line
210 beginning with C<%> is passed directly into the output file, uninterpreted
211 by B<rxgen>.  For a more heavy en masse dumping of uninterpreted code, it
212 would be advised to include all such code in an C<#include> file and pass
213 it in preceded by C<%>.  The input interface may also contain any C-style
214 comments which are, of course, ignored. Interpretation is token-based,
215 thus special line-orientation of separate statements is not necessary.
216 B<rxgen> also provides a quite rich and helpful set of error reports,
217 identifying them by exact line location and error type.  Also, B<rxgen>
218 will automatically generate #include lines for standard include files,
219 such as F<rx/xdr.h> and F<rx/rx.h>, along with the generated header file
220 from this interface.
221
222 =head2 Prefixing stub procedures
223
224 The I<package> statement tells B<rxgen> the name of the interface package.
225 It is used for prefixing the naming of all generated stub routines and the
226 execute request procedure.  For example:
227
228     package AFS_
229
230 causes the execute request procedure to be named AFS_ExecuteRequest
231 (Warning: in the older version an additional C<_> was appended after the
232 package name to the ExecuteRequest name; thus make sure you don't have an
233 ExecuteRequest interface routine) and a given stub routine, say Fetch, to
234 be actually named AFS_Fetch.  Multiple package statements (current maximum
235 size is 10) per configuration are permitted and are useful when multiple
236 sets of interfaces are implemented (see the example at the end).  Note
237 that in such cases, use of the B<-p> flag results in the generation of
238 just one ExecuteRequest procedure which recognizes the multiple interfaces
239 and whose name is prefixed by the first package statement.  In the default
240 case, independent ExecuteRequest procedures will be created for each
241 packaged group of remote procedure calls.
242
243 The I<prefix> statement supplies a name to prepend to all calls to remote
244 procedure names in the ExecuteRequest stub routine.  It is useful when the
245 server makes RPC calls to other servers (say, for debugging purposes).
246 For example:
247
248     prefix S
249
250 causes the name C<S> to be prepended to the name of all routines called
251 from the server stubs.  The server can then call the original name and get
252 the client stubs.
253
254 =head2 B<rxgen> procedure declaration
255
256 The I<proc> statement is the most common (and meaningful) in the B<rxgen>
257 interface.  Its syntax description is:
258
259         [proc] [<proc_name>] [<server_stub>] (<arg>, ..., <arg>)
260             [split | multi] [= <opcode>] ;
261
262 where:
263
264 =over 2
265
266 =item *
267
268 C<proc> is an optional prefix of the procedure statement. This is just a
269 stylistic item and not a required procedure delimiter.
270
271 =item *
272
273 <proc_name> is the name of the procedure.  Note that even the name of the
274 procedure is optional.  This only makes sense when the name of the given
275 procedure is identical to the name of the last I<package> statement (i.e.,
276 C<package RCallBack> and the declaration of the C<RCallBack> procedure).
277
278 =item *
279
280 <server_stub>, if present, causes the ExecuteRequest procedure to call
281 that stub instead of the automatically generated stub when a call with
282 that opcode is decoded.
283
284 =item *
285
286 <opcode> is a constant or symbol that is the opcode for that procedure.
287 One might use the preprocessor features (i.e., #define), the I<const>
288 RPC-language feature, or the old good constants as opcodes. Some further
289 evaluation/processing of opcodes is done.  Particularly, checks for
290 duplicate and non-existent opcodes are performed, along with checks for
291 "holes" (i.e., gaps in consecutive opcodes) in the opcode sequences.  For
292 example, we use the fact that when "holes" in opcodes exist, the
293 ExecuteRequest procedure uses the I<case> statement rather than the faster
294 (and smaller, codewise) indexed array method.
295
296 Also, B<rxgen> defines (i.e., appends to the header file) three valuable
297 macros for each package group: <package-name>LOWEST_OPCODE,
298 <package-name>HIGHEST_OPCODE, and <package-name>NUMBER_OPCODES.  These may
299 be useful to the B<rxgen> programmer.  Also, notice that the I<opcode>
300 statement is an optional feature, and can be omitted.  In such cases,
301 automatic opcode numbers are generated sequentially, starting from 0.
302
303 One can change the initial opcode number by using the I<startingopcode>
304 (for lack of a better name) B<rxgen> command.  Its syntax is:
305
306     startingopcode <constant>
307
308 where <constant> must be reasonable!  Note that one can not mix
309 procedures, some with opcodes and some without, nor allow opcodes after
310 the specification of the I<startingopcode> statement.  B<rxgen> will
311 complain in all such cases.
312
313 =item *
314
315 The I<argument> entry represents a given parameter of the procedure.  Its
316 syntax is:
317
318     [IN | INOUT | OUT | <null>] <type_decl> <arg_name>
319         [<max>|<>|[max]|[]]
320
321 If the type is an indirect type (i.e., is followed by *), it is assumed
322 that the pointer should be followed one level and the data pointed to is
323 to be transmitted. This should normally be used for all structures/arrays
324 and out parameters.  A noticeable exception is when explicit
325 array/structure maximum size is given; since no array-of-pointer
326 declarations are allowed one should use typedefs to achieve the similar
327 effect.  The parameters could be input parameters (preceded by IN), output
328 parameters (preceded by OUT), or input/output parameters (preceded by
329 INOUT).  If not specified, then the direction of the previous parameter in
330 the procedure is used.  (Note: the first parameter must be preceded by the
331 directional primitive!)
332
333 =item *
334
335 C<split> is a hack to handle stub routines that do things such as file
336 transfers or any other operation that has to exchange information (e.g.,
337 length of a file) before the call returns its output parameters.  Because
338 of the particular handshake that is involved when doing remote file
339 transfer, we currently break all such calls into two client-side stub
340 routines.  The first (with the default prefix of C<Begin>) is used to pass
341 all IN and INOUT parameters to the server side.  The second (with the
342 default prefix of C<End>) is used to get back the INOUT and OUT parameters
343 from the server.  Between the two calls, the user is supposed to do the
344 appropriate calls for the file transfer. For example, the following
345 procedure declaration in package AFS_
346
347     Fetch (IN a, b,INOUT c, OUT d) split = FETCHOPCODE;
348
349 will roughly generate the two independent client stub routines:
350
351     BeginAFS_Fetch (IN a, b, c)
352
353 and
354
355     EndAFS_Fetch(OUT c, d)
356
357 The I<splitprefix> statement is used to change the default prefix names
358 used by the two client-side stub generated routines when dealing with file
359 transfer-related procedure calls.  For example:
360
361     splitprefix IN=Before_ OUT=After_
362
363 will cause the naming of the two client stubs for a file transfer-related
364 routine, say Fetch(), to be Before_AFS_Fetch() and After_AFS_Fetch(),
365 respectively.
366
367 =item *
368
369 The C<multi> option is nearly identical to the C<split> feature described
370 above.  The only significant visible difference is that along with the two
371 client stubs, the standard client stub is also generated.  Since the
372 intention is to handle the multi-Rx calls, we need the whole standard
373 procedure stub in the cases where no multi-Rx call of the procedure is
374 performed.  A side effect of the C<multi> option is the generation of a
375 special macro (i.e., C<< multi_<Procedure-name> >> which passes back as
376 arguments the C<Begin> and C<End> stubs in the header output file. This
377 macro is used directly by the Rx code when a multi-Rx call of this
378 procedure is performed.
379
380 =back
381
382 =head2 OBSOLETE B<rxgen> FEATURES
383
384 Although the following rxgen commands are still in effect, they will soon
385 be removed since there are better alternatives. DO NOT USE THEM!
386
387 The I<special> statement is a temporary hack used to handle certain
388 inefficiencies of standard xdr routines to handle some user-customized
389 declarations.  In particular, this applies to a string pointer specified
390 as part of a declaration.  For example,
391
392     special struct BBS SeqBody;
393
394 tells B<rxgen> that the entry C<SeqBody> in the user-defined BBS xdr
395 routine is a string (note that more than one string can be "special" per
396 structure -- multiple ones are separated by commas); it will thus allocate
397 and de-allocate space properly in the server-generated stubs that contain
398 this structure as an IN or INOUT parameter.
399
400 A better alternative to I<special> is the I<customized> statement, which
401 is simply the C<customized> token followed by the regular declaration of a
402 struct based on the RPCL rules. In this case, the declaration will be
403 included in the generated header file (B<-h> option) but no xdr routine
404 will be generated for this structure -- the user will supply this.  All
405 pointer entries in this structure will be remembered so when the structure
406 is used as an IN or INOUT in the server stub, no core leaks will occur.
407 For example, consider
408
409     customized struct CBS {
410         long Seqlen;
411         char *SeqBody;
412     }
413
414 The C<xdr_CBS> routine would be provided by the user where during the
415 DECODE xdr opcode, appropriate space for the C<SeqBody> string is
416 allocated.  Similarly, that space is freed during the FREE xdr opcode.
417
418 Note: Old style "Array parameter specifications" are not supported any
419 more.
420
421 =head1 EXAMPLES
422
423 In case there are some requirements not available by the current RPC
424 language, one can customize some XDR routines by leaving those data types
425 undefined. For every data type that is undefined, it will be assumed that
426 a routine exists with the name C<xdr_> prepended to it.  A selected set of
427 B<rxgen> features is presented below, but for a more comprehensive one
428 (unions, complex examples, etc) please refer to the I<rpcgen Programming
429 Guide> and I<eXternal Data Representation: Sun Technical Notes>.
430
431 =head2 Typedefs
432
433 The RPC typedef statement is identical to the C typedef (i.e. C<< typedef
434 <declaration> >>).  By default, most user declarations (i.e. structs,
435 unions, etc) are automatically typedef'ed by B<rxgen>.  Since it makes
436 parsing simpler, its usage is recommended by B<rxgen> scripts.
437
438 =head2 Strings
439
440 The C C<char *> string convention is kind of ambiguous, since it is
441 usually intended to mean a null-terminated string of characters, but it
442 could also represent a pointer to a single character, a pointer to an
443 array of characters, etc.  In the RPC language, a null-terminated string
444 is unambiguously called a "string".  Examples,
445
446     string bigname<>;
447     string name<MAXNAMELEN>;
448     typedef string volname<MAXVOLNAME>;
449
450 Notice that the maximum size of string can be arbitrary (like C<bigname>
451 above) or, preferably, or specified in angle brackets (i.e. C<name> and
452 C<volname> above).  In practice, one should always use only bounded
453 strings in interfaces.  A sample calling proc using the declarations above
454 would be:
455
456     GetEntryByName (IN volname name, 
457         OUT struct vldbentry *entry) = VL_GETENTRYBYNAME;
458
459 or, of course,
460
461     GetEntryByName (IN string volname<MAXVOLNAME>,
462         OUT struct vldbentry *entry) = VL_GETENTRYBYNAME;
463
464 It is very important for the user to understand when the string parameters
465 should be allocated and/or freed by the his/her client and/or server
466 programs. A short analysis on string parameters handling follows (note
467 that a similar method is used for the handling of variable length arrays
468 as it will be shown later on):
469
470 =over 2
471
472 =item *
473
474 In the client side: IN and INOUT string parameters are the programmer's
475 responsibility and should be allocated (static or via malloc) before
476 calling the rpc and freed (if malloc was used) after the rpc's return in
477 the user's client program; of course, for INOUT parameters, the returned
478 string can't be bigger than the malloced input string.
479
480 OUT string parameters are automatically malloced (based on the length of
481 the returned string and not the maxsize) by the B<rxgen> client stubs (in
482 I<filename>.cs.c) and must be freed by the client program; admittedly,
483 this could be somewhat confusing since the user needs to free something
484 that he/she didn't allocate.}
485
486 =item *
487
488 In the server side: IN and INOUT string parameters are automatically
489 malloced (based on the size of incoming strings) by the rxgen server stubs
490 (in I<filename>.ss.c) before they are passed to the user's server
491 procedure; that space is automatically freed just before the rxgen server
492 stub returns; therefore the user need not do anything special for IN and
493 INOUT string parameters.
494
495 OUT string parameters must be malloced by the user's server procedure
496 (i.e. null pointer is passed to it by the rxgen server stub) and it is
497 automatically freed at the end of the B<rxgen> server stub.  Like in the
498 client side, the OUT parameters are somewhat unorthodox (i.e. the server
499 routine must malloc a string without ever freeing it itself; this is done
500 by the B<rxgen> server stub).
501
502 =back
503
504 Note that for INOUT and OUT string parameters, in both the client and
505 server sides their arguments must be char of pointers (i.e. char **).
506
507 =head2 Pointers
508
509 Pointer declarations in RPC are also exactly as they are in C
510 (i.e. C<struct single_vldbentry *vldblist;>).  Of course, one can't send
511 pointers over the network, but one can use XDR pointers for sending
512 recursive data types such as lists and trees (an example of a linked list
513 will be demonstrated shortly).
514
515 =head2 Arrays
516
517 Fixed arrays are just like standard C array declarations (i.e. C<struct
518 UpdateEntry entries[20]>) without any side effect problems in
519 B<rxgen>. Since variable-length arrays have no explicit syntax in C, the
520 angle-brackets are used for it and the array declarations are actually
521 compiled into "struct"s. For example, declarations such as:
522
523     const   MAXBULKSIZE     = 10000;
524     const   MAXENTRIES      = 100;
525     opaque  bulk<MAXBULKSIZE>;           /* At most 10000 items */
526     int     hosts<>;                     /* any number of items */
527     typedef vldbentry blkentries<100>;   /* Preferable array decl */
528
529 are compiled into the following structs:
530
531     struct {
532         u_int   bulk_len;       /* no of items */
533         char    *bulk_val;      /* pointer to array */
534     } bulk;
535
536 for the C<bulk> array, and similarly for the C<< blkentries<100> >> array,
537
538     struct {
539         u_int      blkentries_len;   /* no of items in array */
540         vldbentry  *blkentries_val;  /* pointer to array */
541     } blkentries;
542
543 Therefore the user should be aware of the "magically" generated structure
544 entries such as the number of items in the array (<array_name>_len) and
545 the pointer to the array (<array_name>_val) since some of the entries will
546 have to be filled in from the client/server programs.  A sample proc would
547 be:
548
549     typedef vldbentry blkentries<MAXENTRIES>;
550     proc GetBlk (OUT blkentries *vlentries) = VL_GETBLK;
551
552 or, more directly,
553
554     GetBlk(OUT vldbentry vlentries<MAXENTRIES>) = VL_GETBLK;
555
556 Note that although the latest method is preferable since one does not have
557 to first use the typedef statement (and admittedly, programmers prefer
558 avoiding typedefs), one should realize that B<rxgen> does the structure
559 expansion and the xdr creation implicitly; therefore the user should be
560 aware of the C<vldbentries_val> and C<vldbentries_len> fields as before
561 (see following examples).
562
563 =head3 Array example I (least desirable)
564
565 Procedure declaration in the interface configuration:
566
567     proc ListAttributes (IN vldblistbyattributes *attributes, 
568                  INOUT blkentries *vldbentries) = VL_LISTATTRIBUTES;
569
570 Sample CLIENT code:
571
572     blkentries entries, *pnt;
573     entries.blkentries_len = 10;   /* max # returned entries */
574     entries.blkentries_val = (vldbentry *)malloc(LEN);
575                                    /* It must be set */
576
577     code = VL_ListAttributes(&attributes, &entries);
578     if (!code) {
579         pnt = entries.blkentries_val;
580         for (i=0; i < entries.blkentries_len; i++, pnt++)
581                 display_vldbentry(pnt);
582         /* Make sure you free the allocated space */
583         free((char *)entries.blkentries_val);   
584     }
585
586 Sample SERVER code:
587
588     VL_ListAttributes(attributes, entries)
589     {
590         vldbentry *singleentry = entries->blkentries_val;
591         entries->blkentries_len = 0;
592
593         while (copy_to_vldbentry(&vlentry, singleentry))
594             singleentry++, vldbentries->entries_len++;
595     }
596
597 Although this method for variable-size arrays works fine, there are some
598 major drawbacks.  The array parameter (i.e. vldbentries above) must be
599 declared as INOUT since we need to pass the max length of the expected
600 returned array; more importantly, a big (depending on the value of
601 C<_len>) chunk of junk code is going to be transferred to the server as
602 result of the IN(out) side-effect of the array.  It's an easy and
603 convenient method if the returned array size can be predicted from the
604 start and when the size is quite high.  This method is included as an
605 example of erroneous use (and abuse) of B<rxgen> and should not be used.
606
607 =head3 Array example II (Desirable method)
608
609 Procedure declaration in the interface configuration (using Example I
610 above):
611
612     proc ListAttributes (IN vldblistbyattributes *attributes, 
613         OUT blkentries *vldbentries) = VL_LISTATTRIBUTES;
614
615 Sample CLIENT code:
616
617     blkentries entries, *pnt;
618
619     code = VL_ListAttributes(&attributes, &entries);
620     if (!code) {
621         pnt = entries.blkentries_val;
622         for (i=0; i < entries.blkentries_len; i++, pnt++)
623                 display_vldbentry(pnt);
624         /* Make sure you free the allocated space (by rxgen) */
625         free((char *)entries.blkentries_val);   
626     }
627
628 Sample SERVER code:
629
630     VL_ListAttributes(attributes, entries)
631     {
632         vldbentry *singleentry;
633         entries->blkentries_len = 0;
634         singleentry = entries->blkentries_val
635             = (vldbentry *)malloc(MAXENTRIES * sizeof(vldbentry));
636
637         while (copy_to_vldbentry(&vlentry, singleentry))
638                 singleentry++, vldbentries->entries_len++;
639     }
640
641 This is the best (and simplest) way of using variable-size arrays as an
642 output parameter.  It is the responsibility of the server-side stub to
643 malloc() the adequate space which is automatically freed by the B<rxgen>
644 stub; the client side should free the space allocated by the
645 B<rxgen>-calling stub.
646
647 =head3 Array example III (Linked Lists)
648
649 Considering the following 3 declarations (could have applied some
650 optimizations) in the configuration file:
651
652     typedef struct single_vldbentry *vldblist;
653     struct single_vldbentry {
654         vldbentry vlentry;
655         vldblist  next_vldb;
656     };
657
658     struct vldb_list {
659         vldblist node;
660     };
661
662 and the rxgen procedure declaration:
663
664     LinkedList (IN vldblistbyattributes *attributes, 
665         OUT vldb_list *linkedentries) = VL_LINKEDLIST;
666
667 Sample CLIENT code:
668
669     vldb_list       linkedvldbs;
670     vldblist        vllist, vllist1;
671
672     bzero(&linkedvldbs, sizeof(vldb_list));
673     code = VL_LinkedList(&attributes, &nentries, &linkedvldbs);
674     if (!code) {
675         printf("We got %d vldb entries\n", nentries);
676         for (vllist = linkedvldbs.node; vllist; vllist = vllist1) {
677             vllist1 = vllist->next_vldb;
678             display_entry(&vllist->vlentry);
679             free((char *)vllist);
680         }
681     }
682
683 Sample SERVER code:
684
685     VL_LinkedList(rxcall, attributes, nentries, linkedvldbs);
686     {
687         vldblist vllist, *vllistptr = &linkedvldbs->node;
688         while (...) {
689             vllist = *vllistptr
690                 = (single_vldbentry *)malloc (sizeof (single_vldbentry));
691             copy_to_vldbentry(&tentry, &vllist->vlentry);
692             nentries++;     
693             vllistptr = &vllist->next_vldb;
694         };
695         *vllistptr = NULL;
696     }
697
698 Using a linked list offers many advantages: Nothing is passed to the
699 server (the parameter is OUT), no additional overhead is involved, and the
700 caller doesn't have to explicitly prepare for an arbitrary return size.  A
701 drawback is that the caller has the responsibility of malloc() (on the
702 server) and free (on the client) of each entry (to avoid unwanted
703 core-leaks).  Another drawback is that since it's a recursive call, the C
704 stack will grow linearly with respect to the number of nodes in the list
705 (so it's wise to increase the Rx LWP stack if huge amounts of data are
706 expected back -- default stack size is 4K).  The advantages should
707 outweigh the disadvantages here.
708
709 It's important to pay attention to the comments of the three array
710 examples above particularly when they're references to when the user
711 should allocate/free space for the variable length arrays.  The mechanism
712 is very similar to the handling of strings thus you might need to review
713 the strings section above; note that the linked lists are handled somewhat
714 differently...
715
716 =head2 Miscellaneous examples
717
718 Below is an abbreviated version of a random interface file which shows
719 some of the common cases.
720
721     /* Declaration of all structures used by the R.xg script interface */
722
723     struct AFSFid {
724         unsigned long Volume;
725         unsigned long Vnode;
726         unsigned long Unique;
727     };
728
729     typedef long ViceDataType;
730
731     /* Note that TEST would be equivalent to "HEADER" only during the 
732        processing of the header, *.h, file */
733
734     #ifdef RPC_HDR
735     #define TEST "HEADER"
736     #else
737     #define TEST "REST"
738     #endif
739
740     /* This is the standard *.xg specification file */
741
742     package AFS_
743     splitprefix IN=BEFORE_ OUT=AFTER_;
744     Prefix Test
745
746     proc Remove(IN struct AFSFid *Did, IN string volname<64>,
747         OUT struct AFSStatus *Status) = AFS_REMOVE;
748
749     DisconnectFS AUX_disconnectFS() = AFS_DISCONNECTFS;
750
751     proc GetVolumeInfo(IN string Vid, 
752         OUT struct VolumeInfo *Info) = AFS_GETVOLUMEINFO;
753
754     /* You could have more than an interface per configuration */
755
756     package VOTE_
757
758     /* Using the "multi" feature; thus VOTE_Beacon can be called as an 
759        multi-Rx call or as a regular call */
760
761     Beacon (IN long state, long voteStart, 
762         net_version *version, net_tid *tid) 
763         multi = VOTE_BEACON;
764
765     package DISK_
766
767     /* Using the "split" feature */
768
769     SendFile (IN long file, long offset, 
770         long length, net_version *version) 
771         split = DISK_SENDFILE;
772
773 =head2 Output of an actual interface configuration
774
775 We'll demonstrate some of the actual output generated by B<rxgen> by
776 following an abbreviated actual interface configuration.
777
778 =head3 Configuration file
779
780 Contents of the interface configuration file (F<vldbint.xg>):
781
782     package VL_
783     #include "vl_opcodes.h"   /* The opcodes are included here */
784     %#include "vl_opcodes.h"  /* directly to other places */
785
786     /* Current limitations on parameters that affect other packages
787        (i.e. volume) */
788
789     const   MAXNAMELEN      =       65;
790     const   MAXNSERVERS     =       8;
791     const   MAXTYPES        =       3;
792
793     /* External (visible) representation of an individual vldb entry */
794
795     struct vldbentry {
796         char    name[MAXNAMELEN];       
797         long    volumeType;             
798         long    nServers;               
799         long    serverNumber[MAXNSERVERS];
800         long    serverPartition[MAXNSERVERS];
801         long    serverFlags[MAXNSERVERS];
802         u_long  volumeId[MAXTYPES];     
803         long    flags;                  
804     };
805
806     typedef struct single_vldbentry  *vldblist;
807     struct single_vldbentry {
808         vldbentry VldbEntry;
809         vldblist next_vldb;
810     };
811
812     struct vldb_list {
813         vldblist node;
814     };
815
816     /* vldb interface calls */
817
818     CreateEntry     (IN long Volid, 
819                     vldbentry *newentry) = VLCREATEENTRY;
820
821     GetEntryByName  (IN string volumename<MAXNAMELEN>, 
822                     OUT vldbentry *entry) = VLGETENTRYBYNAME;
823
824     GetNewVolumeId  (IN long bumpcount,
825                     OUT long *newvolumid) = VLGETNEWVOLUMEID;
826
827     ReplaceEntry    (IN long Volid, 
828                     long voltype,
829                     vldbentry *newentry,
830                     long ReleaseType) multi = VLREPLACEENTRY;
831
832     ListAttributes  (IN VldbListByAttributes *attributes, 
833                     OUT long *nentries, 
834                     OUT vldbentry bulkentries<MAXVLDBLEN>) 
835                     = VLLISTATTRIBUTES;
836
837     LinkedList      (IN VldbListByAttributes *attributes, 
838                     OUT long *nentries, 
839                     OUT vldb_list *linkedentries) = VLLINKEDLIST;
840
841 For a detailed description on the Rx-related calls inside the generated
842 stubs (i.e., rx_NewCall(), rx_EndCall()), along with details on what happens
843 inside certain calls (like xdrrx_create()) please refer to the Rx
844 documentation. Typing C<rxgen vldbint.xg> will result in the creation of
845 four files: F<vldbint.h>, F<vldbint.xdr.c>, F<vldbint.cs.c> and
846 F<vldbint.ss.c>.  A closer look at these files follows.
847
848 =head3 Header file (F<vldbint.h>)
849
850     /* Machine generated file -- Do NOT edit */
851
852     #include "vl_opcodes.h"  /* directly to other places */
853     #define MAXNAMELEN 65
854     #define MAXNSERVERS 8
855     #define MAXTYPES 3
856
857     struct vldbentry {
858         char name[MAXNAMELEN];
859         long volumeType;
860         long nServers;
861         long serverNumber[MAXNSERVERS];
862         long serverPartition[MAXNSERVERS];
863         long serverFlags[MAXNSERVERS];
864         u_long volumeId[MAXTYPES];
865         long flags;
866     };
867     typedef struct vldbentry vldbentry;
868     bool_t xdr_vldbentry();
869
870     typedef struct single_vldbentry *vldblist;
871     bool_t xdr_vldblist();
872
873     struct single_vldbentry {
874         vldbentry VldbEntry;
875         vldblist next_vldb;
876     };
877     typedef struct single_vldbentry single_vldbentry;
878     bool_t xdr_single_vldbentry();
879
880     struct vldb_list {
881         vldblist node;
882     };
883     typedef struct vldb_list vldb_list;
884     bool_t xdr_vldb_list();
885
886     #include <rx/rx_multi.h>
887     #define multi_VL_ReplaceEntry(Volid, voltype, newentry, ReleaseType) \
888         multi_Body(StartVL_ReplaceEntry(multi_call, Volid, voltype,
889                    newentry, ReleaseType), EndVL_ReplaceEntry(multi_call))
890
891     typedef struct bulkentries {
892         u_int bulkentries_len;
893         vldbentry *bulkentries_val;
894     } bulkentries;
895     bool_t xdr_bulkentries();
896
897     /* Opcode-related useful stats for package: VL_ */
898     #define VL_LOWEST_OPCODE        501
899     #define VL_HIGHEST_OPCODE       506
900     #define VL_NUMBER_OPCODES       6
901
902 Notice that all structures are automatically typedef'ed and all C<const>s
903 are converted to C<#define>s. Some data structures, such as bulkentries,
904 are taken from procedure params (from ListAttributes proc). Thus, this
905 should be kept in mind when creating stubs piecemeal with B<rxgen> (i.e.,
906 using the B<-c>, B<-h>, B<-C>, or B<-S> flags).  Also, one of the side
907 effects of the C<multi> option (in C<ReplaceEntry> proc) is the generation
908 of the C<multi_VL_ReplaceEntry> above.
909
910 =head3 XDR routines for structures (vldbint.xdr.c)
911
912     /* Machine generated file -- Do NOT edit */
913
914     #include <rx/xdr.h>
915     #include "vldbint.h"
916
917     #include "vl_opcodes.h"  /* directly to other places */
918
919     bool_t
920     xdr_vldbentry(xdrs, objp)
921         XDR *xdrs;
922         vldbentry *objp;
923     {
924         if (!xdr_vector(xdrs, (char *)objp->name, MAXNAMELEN,
925                         sizeof(char), xdr_char))
926             return (FALSE);
927         if (!xdr_long(xdrs, &objp->volumeType))
928             return (FALSE);
929         if (!xdr_long(xdrs, &objp->nServers))
930             return (FALSE);
931         if (!xdr_vector(xdrs, (char *)objp->serverNumber, MAXNSERVERS,
932                         sizeof(long), xdr_long))
933             return (FALSE);
934         if (!xdr_vector(xdrs, (char *)objp->serverPartition,
935                         MAXNSERVERS, sizeof(long), xdr_long))
936             return (FALSE);
937         if (!xdr_vector(xdrs, (char *)objp->serverFlags, MAXNSERVERS,
938                         sizeof(long), xdr_long))
939             return (FALSE);
940         if (!xdr_vector(xdrs, (char *)objp->volumeId, MAXTYPES,
941                         sizeof(u_long), xdr_u_long))
942             return (FALSE);
943         if (!xdr_long(xdrs, &objp->flags))
944             return (FALSE);
945         return (TRUE);
946     }
947
948     bool_t
949     xdr_vldblist(xdrs, objp)
950         XDR *xdrs;
951         vldblist *objp;
952     {
953         if (!xdr_pointer(xdrs, (char **)objp,
954                          sizeof(struct single_vldbentry), 
955                          xdr_single_vldbentry))
956             return (FALSE);
957         return (TRUE);
958     }
959
960     bool_t
961     xdr_single_vldbentry(xdrs, objp)
962         XDR *xdrs;
963         single_vldbentry *objp;
964     {
965         if (!xdr_vldbentry(xdrs, &objp->VldbEntry))
966             return (FALSE);
967         if (!xdr_vldblist(xdrs, &objp->next_vldb))
968             return (FALSE);
969         return (TRUE);
970     }
971
972     bool_t
973     xdr_vldb_list(xdrs, objp)
974         XDR *xdrs;
975         vldb_list *objp;
976     {
977         if (!xdr_vldblist(xdrs, &objp->node))
978             return (FALSE);
979         return (TRUE);
980     }
981
982     bool_t
983     xdr_bulkentries(xdrs, objp)
984         XDR *xdrs;
985         bulkentries *objp;
986     {
987         if (!xdr_array(xdrs, (char **)&objp->bulkentries_val,
988                        (u_int *)&objp->bulkentries_len, MAXVLDBLEN,
989                        sizeof(vldbentry), xdr_vldbentry))
990             return (FALSE);
991         return (TRUE);
992     }
993
994 Note that the xdr_bulkentries() is automatically generated as a side
995 effect of a procedure parameter declaration.  Thus, if identical multiple
996 type parameter declarations are used, then multiply-defined xdr_* stubs
997 will be created!  We felt this was a better alternative to having the
998 B<rxgen> programmer deal with types such as bulkentries_1,
999 bulkentries_2...
1000
1001 =head3 Client-Side stub routines (vldbint.cs.c)
1002
1003     /* Machine generated file -- Do NOT edit */
1004
1005     #include <rx/xdr.h>
1006     #include <rx/rx.h>
1007     #include <afs/rxgen_consts.h>
1008     #include "vldbint.h"
1009
1010     #include "vl_opcodes.h"  /* directly to other places */
1011
1012     int VL_CreateEntry(z_conn, Volid, newentry)
1013         register struct rx_connection *z_conn;
1014         long Volid;
1015         vldbentry * newentry;
1016     {
1017         struct rx_call *z_call = rx_NewCall(z_conn);
1018         static int z_op = 501;
1019         int z_result;
1020         XDR z_xdrs;
1021
1022         xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1023
1024         /* Marshal the arguments */
1025         if ((!xdr_int(&z_xdrs, &z_op))
1026              || (!xdr_long(&z_xdrs, &Volid))
1027              || (!xdr_vldbentry(&z_xdrs, newentry))) {
1028                 z_result = RXGEN_CC_MARSHAL;
1029                 goto fail;
1030         }
1031
1032         z_result = RXGEN_SUCCESS;
1033     fail:
1034         return rx_EndCall(z_call, z_result);
1035     }
1036
1037     int VL_GetEntryByName(z_conn, volumename, entry)
1038         register struct rx_connection *z_conn;
1039         char * volumename;
1040         vldbentry * entry;
1041     {
1042         struct rx_call *z_call = rx_NewCall(z_conn);
1043         static int z_op = 504;
1044         int z_result;
1045         XDR z_xdrs;
1046
1047         xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1048
1049         /* Marshal the arguments */
1050         if ((!xdr_int(&z_xdrs, &z_op))
1051              || (!xdr_string(&z_xdrs, &volumename, 65))) {
1052                 z_result = RXGEN_CC_MARSHAL;
1053                 goto fail;
1054         }
1055
1056         /* Un-marshal the reply arguments */
1057         z_xdrs.x_op = XDR_DECODE;
1058         if ((!xdr_vldbentry(&z_xdrs, entry))) {
1059                 z_result = RXGEN_CC_UNMARSHAL;
1060                 goto fail;
1061         }
1062
1063         z_result = RXGEN_SUCCESS;
1064     fail:
1065         return rx_EndCall(z_call, z_result);
1066     }
1067
1068     int VL_GetNewVolumeId(z_conn, bumpcount, newvolumid)
1069         register struct rx_connection *z_conn;
1070         long bumpcount;
1071         long * newvolumid;
1072     {
1073         struct rx_call *z_call = rx_NewCall(z_conn);
1074         static int z_op = 505;
1075         int z_result;
1076         XDR z_xdrs;
1077
1078         xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1079
1080         /* Marshal the arguments */
1081         if ((!xdr_int(&z_xdrs, &z_op))
1082              || (!xdr_long(&z_xdrs, &bumpcount))) {
1083                 z_result = RXGEN_CC_MARSHAL;
1084                 goto fail;
1085         }
1086
1087         /* Un-marshal the reply arguments */
1088         z_xdrs.x_op = XDR_DECODE;
1089         if ((!xdr_long(&z_xdrs, newvolumid))) {
1090                 z_result = RXGEN_CC_UNMARSHAL;
1091                 goto fail;
1092         }
1093
1094         z_result = RXGEN_SUCCESS;
1095     fail:
1096         return rx_EndCall(z_call, z_result);
1097     }
1098
1099     int VL_ReplaceEntry(z_conn, Volid, voltype, newentry, ReleaseType)
1100         register struct rx_connection *z_conn;
1101         long Volid, voltype, ReleaseType;
1102         vldbentry * newentry;
1103     {
1104         struct rx_call *z_call = rx_NewCall(z_conn);
1105         static int z_op = 506;
1106         int z_result;
1107         XDR z_xdrs;
1108
1109         xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1110
1111         /* Marshal the arguments */
1112         if ((!xdr_int(&z_xdrs, &z_op))
1113              || (!xdr_long(&z_xdrs, &Volid))
1114              || (!xdr_long(&z_xdrs, &voltype))
1115              || (!xdr_vldbentry(&z_xdrs, newentry))
1116              || (!xdr_long(&z_xdrs, &ReleaseType))) {
1117                 z_result = RXGEN_CC_MARSHAL;
1118                 goto fail;
1119         }
1120
1121         z_result = RXGEN_SUCCESS;
1122     fail:
1123         return rx_EndCall(z_call, z_result);
1124     }
1125
1126     int StartVL_ReplaceEntry(z_call, Volid, voltype, newentry, ReleaseType)
1127         register struct rx_call *z_call;
1128         long Volid, voltype, ReleaseType;
1129         vldbentry * newentry;
1130     {
1131         static int z_op = 506;
1132         int z_result;
1133         XDR z_xdrs;
1134
1135         xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1136
1137         /* Marshal the arguments */
1138         if ((!xdr_int(&z_xdrs, &z_op))
1139              || (!xdr_long(&z_xdrs, &Volid))
1140              || (!xdr_long(&z_xdrs, &voltype))
1141              || (!xdr_vldbentry(&z_xdrs, newentry))
1142              || (!xdr_long(&z_xdrs, &ReleaseType))) {
1143                 z_result = RXGEN_CC_MARSHAL;
1144                 goto fail;
1145         }
1146
1147         z_result = RXGEN_SUCCESS;
1148     fail:
1149         return z_result;
1150     }
1151
1152     int EndVL_ReplaceEntry(z_call)
1153         register struct rx_call *z_call;
1154     {
1155         int z_result;
1156         XDR z_xdrs;
1157
1158         z_result = RXGEN_SUCCESS;
1159     fail:
1160         return z_result;
1161     }
1162
1163     int VL_ListAttributes(z_conn, attributes, nentries, bulkentries_1)
1164         register struct rx_connection *z_conn;
1165         VldbListByAttributes * attributes;
1166         long * nentries;
1167         bulkentries * bulkentries_1;
1168     {
1169         struct rx_call *z_call = rx_NewCall(z_conn);
1170         static int z_op = 511;
1171         int z_result;
1172         XDR z_xdrs;
1173
1174         xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1175
1176         /* Marshal the arguments */
1177         if ((!xdr_int(&z_xdrs, &z_op))
1178              || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) {
1179                 z_result = RXGEN_CC_MARSHAL;
1180                 goto fail;
1181         }
1182
1183         /* Un-marshal the reply arguments */
1184         z_xdrs.x_op = XDR_DECODE;
1185         if ((!xdr_long(&z_xdrs, nentries))
1186              || (!xdr_bulkentries(&z_xdrs, bulkentries_1))) {
1187                 z_result = RXGEN_CC_UNMARSHAL;
1188                 goto fail;
1189         }
1190
1191         z_result = RXGEN_SUCCESS;
1192     fail:
1193         return rx_EndCall(z_call, z_result);
1194     }
1195
1196     int VL_LinkedList(z_conn, attributes, nentries, linkedentries)
1197         register struct rx_connection *z_conn;
1198         VldbListByAttributes * attributes;
1199         long * nentries;
1200         vldb_list * linkedentries;
1201     {
1202         struct rx_call *z_call = rx_NewCall(z_conn);
1203         static int z_op = 512;
1204         int z_result;
1205         XDR z_xdrs;
1206
1207         xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1208
1209         /* Marshal the arguments */
1210         if ((!xdr_int(&z_xdrs, &z_op))
1211              || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) {
1212                 z_result = RXGEN_CC_MARSHAL;
1213                 goto fail;
1214         }
1215
1216         /* Un-marshal the reply arguments */
1217         z_xdrs.x_op = XDR_DECODE;
1218         if ((!xdr_long(&z_xdrs, nentries))
1219              || (!xdr_vldb_list(&z_xdrs, linkedentries))) {
1220                 z_result = RXGEN_CC_UNMARSHAL;
1221                 goto fail;
1222         }
1223
1224         z_result = RXGEN_SUCCESS;
1225     fail:
1226         return rx_EndCall(z_call, z_result);
1227     }
1228
1229 Notice the side effect of the C<multi> feature (three different modules
1230 for C<ReplaceEntry> proc).
1231
1232 =head3 Server-Side stub routines (vldbint.ss.c)
1233
1234     /* Machine generated file -- Do NOT edit */
1235
1236     #include <rx/xdr.h>
1237     #include <rx/rx.h>
1238     #include <afs/rxgen_consts.h>
1239     #include "vldbint.h"
1240
1241     #include "vl_opcodes.h"  /* directly to other places */
1242
1243     long _VL_CreateEntry(z_call, z_xdrs)
1244         struct rx_call *z_call;
1245         XDR *z_xdrs;
1246     {
1247         long z_result;
1248         long Volid;
1249         vldbentry newentry;
1250
1251         if ((!xdr_long(z_xdrs, &Volid))
1252              || (!xdr_vldbentry(z_xdrs, &newentry))) {
1253                 z_result = RXGEN_SS_UNMARSHAL;
1254                 goto fail;
1255         }
1256
1257         z_result = VL_CreateEntry(z_call, Volid, &newentry);
1258     fail:
1259         return z_result;
1260     }
1261
1262     long _VL_GetEntryByName(z_call, z_xdrs)
1263         struct rx_call *z_call;
1264         XDR *z_xdrs;
1265     {
1266         long z_result;
1267         char *volumename = (char *)0;
1268         vldbentry entry;
1269
1270         if ((!xdr_string(z_xdrs, &volumename, 65))) {
1271                 z_result = RXGEN_SS_UNMARSHAL;
1272                 goto fail;
1273         }
1274
1275         z_result = VL_GetEntryByName(z_call, &volumename, &entry);
1276         z_xdrs->x_op = XDR_ENCODE;
1277         if ((!xdr_vldbentry(z_xdrs, &entry)))
1278                 z_result = RXGEN_SS_MARSHAL;
1279     fail:
1280         z_xdrs->x_op = XDR_FREE;
1281         if (!xdr_string(z_xdrs, &volumename, 65)) goto fail1;
1282         return z_result;
1283     fail1:
1284         return RXGEN_SS_XDRFREE;
1285     }
1286
1287     long _VL_GetNewVolumeId(z_call, z_xdrs)
1288         struct rx_call *z_call;
1289         XDR *z_xdrs;
1290     {
1291         long z_result;
1292         long bumpcount;
1293         long newvolumid;
1294
1295         if ((!xdr_long(z_xdrs, &bumpcount))) {
1296                 z_result = RXGEN_SS_UNMARSHAL;
1297                 goto fail;
1298         }
1299
1300         z_result = VL_GetNewVolumeId(z_call, bumpcount, &newvolumid);
1301         z_xdrs->x_op = XDR_ENCODE;
1302         if ((!xdr_long(z_xdrs, &newvolumid)))
1303                 z_result = RXGEN_SS_MARSHAL;
1304     fail:
1305         return z_result;
1306     }
1307
1308     long _VL_ReplaceEntry(z_call, z_xdrs)
1309         struct rx_call *z_call;
1310         XDR *z_xdrs;
1311     {
1312         long z_result;
1313         long Volid, voltype, ReleaseType;
1314         vldbentry newentry;
1315
1316         if ((!xdr_long(z_xdrs, &Volid))
1317              || (!xdr_long(z_xdrs, &voltype))
1318              || (!xdr_vldbentry(z_xdrs, &newentry))
1319              || (!xdr_long(z_xdrs, &ReleaseType))) {
1320                 z_result = RXGEN_SS_UNMARSHAL;
1321                 goto fail;
1322         }
1323
1324         z_result = VL_ReplaceEntry(z_call, Volid, voltype, &newentry,
1325                                    ReleaseType);
1326     fail:
1327         return z_result;
1328     }
1329
1330     long _VL_ListAttributes(z_call, z_xdrs)
1331         struct rx_call *z_call;
1332         XDR *z_xdrs;
1333     {
1334         long z_result;
1335         VldbListByAttributes attributes;
1336         long nentries;
1337         bulkentries bulkentries_1;
1338
1339         if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) {
1340                 z_result = RXGEN_SS_UNMARSHAL;
1341                 goto fail;
1342         }
1343
1344         z_result = VL_ListAttributes(z_call, &attributes, &nentries,
1345                                      &bulkentries_1);
1346         z_xdrs->x_op = XDR_ENCODE;
1347         if ((!xdr_long(z_xdrs, &nentries))
1348              || (!xdr_bulkentries(z_xdrs, &bulkentries_1)))
1349                 z_result = RXGEN_SS_MARSHAL;
1350     fail:
1351         z_xdrs->x_op = XDR_FREE;
1352         if (!xdr_bulkentries(z_xdrs, &bulkentries_1)) goto fail1;
1353         return z_result;
1354     fail1:
1355         return RXGEN_SS_XDRFREE;
1356     }
1357
1358     long _VL_LinkedList(z_call, z_xdrs)
1359         struct rx_call *z_call;
1360         XDR *z_xdrs;
1361     {
1362         long z_result;
1363         VldbListByAttributes attributes;
1364         long nentries;
1365         vldb_list linkedentries;
1366
1367         if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) {
1368                 z_result = RXGEN_SS_UNMARSHAL;
1369                 goto fail;
1370         }
1371
1372         z_result = VL_LinkedList(z_call, &attributes, &nentries,
1373                                  &linkedentries);
1374         z_xdrs->x_op = XDR_ENCODE;
1375         if ((!xdr_long(z_xdrs, &nentries))
1376              || (!xdr_vldb_list(z_xdrs, &linkedentries)))
1377                 z_result = RXGEN_SS_MARSHAL;
1378     fail:
1379         return z_result;
1380     }
1381
1382     long _VL_CreateEntry();
1383     long _VL_GetEntryByName();
1384     long _VL_GetNewVolumeId();
1385     long _VL_ReplaceEntry();
1386     long _VL_ListAttributes();
1387     long _VL_LinkedList();
1388
1389     static long (*StubProcsArray0[])() = {_VL_CreateEntry,
1390         _VL_GetEntryByName, _VL_GetNewVolumeId, _VL_ReplaceEntry,
1391         _VL_ListAttributes, _VL_LinkedList};
1392
1393     VL_ExecuteRequest(z_call)
1394         register struct rx_call *z_call;
1395     {
1396         int op;
1397         XDR z_xdrs;
1398         long z_result;
1399
1400         xdrrx_create(&z_xdrs, z_call, XDR_DECODE);
1401         if (!xdr_int(&z_xdrs, &op))
1402             z_result = RXGEN_DECODE;
1403         else if (op < VL_LOWEST_OPCODE || op > VL_HIGHEST_OPCODE)
1404             z_result = RXGEN_OPCODE;
1405         else
1406             z_result = (*StubProcsArray0[op - VL_LOWEST_OPCODE])
1407                 (z_call, &z_xdrs);
1408         return z_result;
1409     }
1410
1411 If there were gaps in the procedures' opcode sequence the code for
1412 VL_ExecuteRequest() routine would be have been drastically different (it
1413 would have been a case statement for each procedure).
1414
1415 =head1 NOTES
1416
1417 B<rxgen> is implemented from Sun's B<rpcgen> utility.
1418
1419 When the C<%#include <include file>> feature is used make sure that you
1420 don't have any B<rxgen> language features (i.e. %#defines) since you'll
1421 get syntax errors during compilations..
1422
1423 Since this is an ongoing project many of the above may change/disappear
1424 without a major warning.
1425
1426 =head1 SEE ALSO
1427
1428 I<Rxgen Syntax Summary>: Summary description of rxgen's grammar.
1429
1430 I<Rpcgen Programming Guide>: Sun's RPC protocol compiler.  B<rxgen> was
1431 implemented as an extension to that compiler.
1432
1433 I<External Data Representation: Sun Technical Notes>: Detailed examples in
1434 using XDR.
1435
1436 I<RPCL Syntax Summary>: Summary of Sun's Remote Procedure Call Language.
1437
1438 I<Rx>: An extended Remote Procedure Call Protocol.
1439
1440 I<rgen>: An earlier version of a similar stub generator used for the R RPC
1441 protocol.
1442
1443 =head1 COPYRIGHT
1444
1445 IBM Corporation 2000. <http://www.ibm.com/> All Rights Reserved.
1446
1447 This documentation is covered by the IBM Public License Version 1.0.  It
1448 was converted from the original TeX B<rxgen> manual to POD by Russ
1449 Allbery.