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