Chapter 7. XDR and RPC Language Structure

This chapter describes the XDR and RPC languages. Topics include:

XDR Language

This section describes the components of the XDR language.

Notational Conventions

This specification uses an extended Backus-Naur Form notation for describing the XDR language. This notation has the following characteristics:

  • These are the special characters:

    | ( ) [ ] " * 

  • Terminal symbols are strings of any characters surrounded by double quotes (" ").

  • Nonterminal symbols are strings of non-special characters.

  • Alternative items are separated by a vertical bar (|).

  • Optional items are enclosed in brackets ([]).

  • Items are grouped by enclosing them in parentheses (( )).

  • An asterisk (*) following an item means zero or more occurrences of that item.

For example, consider this pattern:

"a" "very" ("," "very")* ["cold" "and"]  "rainy" ("day" | "night") 

An infinite number of strings match this pattern; for example:

"a very rainy day"
"a very, very rainy day"
"a very cold and rainy day"
"a very, very, very cold and rainy night"

Lexical Notes

This section discusses some lexical features of the XDR language:

  • Comments begin with /* and end with */. For example:

    /* comment */ 

  • White space separates items and is otherwise ignored.

  • An identifier is a letter followed by an optional sequence of letters, digits, or an underscore (_). The case of identifiers is not ignored.

  • A constant is a sequence of one or more decimal digits, optionally preceded by a minus sign ().

Syntax Information

This section describes XDR language syntax:

declaration:
    type-specifier identifier
    | type-specifier identifier "[" value "]"
    | type-specifier identifier "<" [ value ] ">"
    | "opaque" identifier "[" value "]"
    | "opaque" identifier "<" [ value ] ">"
    | "string" identifier "<" [ value ] ">"
    | type-specifier "*" identifier
    | "void"
value:
    constant
    | identifier
type-specifier:
      [ "unsigned" ] "int"
    | [ "unsigned" ] "hyper"
    | "float"
    | "double"
    | "bool"
    | enum-type-spec
    | struct-type-spec
    | union-type-spec
    | identifier
enum-type-spec:
    "enum" enum-body
enum-body:
    "{"
    ( identifier "=" value )
    ( "," identifier "=" value )*
    "}"
struct-type-spec:
    "struct" struct-body
struct-body:
    "{"
    ( declaration ";" )
    ( declaration ";" )*
    "}"
union-type-spec:
    "union" union-body
union-body:
    "switch" "(" declaration ")" "{"
    ( "case" value ":" declaration ";" )
    ( "case" value ":" declaration ";" )*
    [ "default" ":" declaration ";" ]
    "}"
constant-def:
    "const" identifier "=" constant ";"
type-def:
    "typedef" declaration ";"
    | "enum" identifier enum-body ";"
    | "struct" identifier struct-body ";"
    | "union" identifier union-body ";"
definition:
    type-def
    | constant-def
specification:
    definition *

Syntax Notes

This section provides additional information about XDR language syntax:

  • The following keywords cannot be used as identifiers:

    bool        double      opaque    typedef
    case        enum        string    union
    const       float       struct    unsigned
    default     hyper       switch    void
    

  • Only unsigned constants may be used as size specifications for arrays. If an identifier is used, it must have been declared previously as an unsigned constant in a const definition.

  • Constant and type identifiers within the scope of a specification are in the same name space and must be declared uniquely within this scope.

  • Similarly, variable names must be unique within the scope of struct and union declarations. Nested struct and union declarations create new scopes.

  • The discriminant of a union must be of a type that evaluates to an integer. That is, int, unsigned int, bool, an enumerated type, or any typedef type that evaluates to one of these is legal. Also, the case values must be one of the legal values of the discriminant. Finally, a case value may not be specified more than once within the scope of a union declaration.

XDR Data Description Example

The following is a short XDR data description of a “file” that you might use to transfer files from one machine to another:

const MAXUSERNAME = 32;    /* max length of a user name */
const MAXFILELEN = 65535;  /* max length of a file */
const MAXNAMELEN = 255;    /* max length of a filename */

/* Types of files: */
enum filekind {
    TEXT = 0,            /* ascii data */
    DATA = 1,            /* raw data */
    EXEC = 2             /* executable */
};

/* File information, per kind of file: */
union filetype switch (filekind kind) {
    case TEXT:
      void;                /* no extra information */
    case DATA:
      string creator<MAXNAMELEN>;   /* data creator */
    case EXEC:
      string interpreter<MAXNAMELEN>;/*program interpreter*/
};

/* A complete file: */
struct file {
    string filename<MAXNAMELEN>;    /* name of file */
    filetype type;                  /* info about file */
    string owner<MAXUSERNAME>;      /* owner of file */
    opaque data<MAXFILELEN>;        /* file data  */
};

Suppose that a user named jean wants to store a LISP program sillyprog, which contains just the data “(quit).” The file would be encoded as shown in Table 7-1.

Table 7-1. DR Data Encoding Examples

Offset

Hex Bytes

ASCII

Description

0

00 00 00 09

....

Length of filename = 9

4

73 69 6c 6c

sill

Filename characters

8

79 70 72 6

ypro

More filename characters

12

67 00 00 00

g...

The last filename character plus 3 zero-bytes of fill

16

00 00 00 02

....

File kind is EXEC = 2

20

00 00 00 04

....

Length of interpreter name = 4

24

6c 69 73 70

lisp

Interpreter name

28

00 00 00 04

....

Length of owner name = 4

32

6a 65 61 6e

jean

Owner name

36

00 00 00 06

....

Length of data = 6

40

28 71 75 69

(qui

File data bytes

44

74 29 00 00

t)..

More data plus 2 zero-bytes of fill


RPC Language

RPC language is an extension of the XDR language; the sole extension is the addition of the program type.

Definitions

An RPC language file consists of a series of definitions:

definition-list:
    definition ";"
    definition ";" definition-list

It recognizes six types of definition:

definition:
    enum-definition
    struct-definition
    union-definition
    typedef-definition
    const-definition
    program-definition

Structures

An XDR structure is declared almost exactly like its C counterpart:

struct-definition:
    "struct" struct-ident "{"
        declaration-list
    "}"

declaration-list:
    declaration ";"
    declaration ";" declaration-list

For example, the following code is an XDR structure for a two–dimensional coordinate and the C structure into which it is compiled in the output header file:

struct coord {          struct coord {
    int x;       -->        int x;
    int y;                  int y;
};                      };
                        typedef struct coord coord;

The output is identical to the input, except for the added typedef at the end of the output. Using typedef allows you to use coord instead of struct coord when declaring items.

Unions

XDR unions are discriminated unions, and they look different from C unions. They are more analogous to Pascal variant records than they are to C unions:

union-definition:
    "union" union-ident "switch" "(" declaration ")" "{"
        case-list
    "}"
case-list:
    "case" value ":" declaration ";"
    "default" ":" declaration ";"
    "case" value ":" declaration ";" case-list

The next example shows a type that might be returned as the result of a read data operation. If no error, return a block of data. Otherwise, return nothing.

union read_result switch (int errno) {
case 0:
    opaque data[1024];
default:
    void;
}; 

This code is compiled into:

struct read_result {
    int errno;
    union {
        char data[1024];
    } read_result_u;
};
typedef struct read_result read_result;

Notice that the union component of the output struct has the name as the type name, except for the trailing _u.

Enumerations

XDR enumerations have the same syntax as C enumerations:

enum-definition:
    "enum" enum-ident "{"
        enum-value-list
    "}"

enum-value-list:
    enum-value
    enum-value "," enum-value-list

enum-value:
    enum-value-ident
    enum-value-ident "=" value

The XDR enum and the C enum are compiled into:

enum colortype {          enum colortype {
    RED = 0,                  RED = 0,
    GREEN = 1,     -->        GREEN = 1,
    BLUE = 2                  BLUE = 2
};                        };
                          typedef enum colortype colortype;

Typedefs

An XDR typedef has the same syntax as a C typedef:

typedef-definition:
    "typedef" declaration

The following example defines a fname_type used to declare filename strings that have a maximum length of 255 characters:

typedef string fname_type<255>; --> typedef char *fname_type;

Constants

XDR constants are symbolic constants. They may be used wherever an integer constant is used; for example, in array-size specifications:

const-definition:
     "const" const-ident "=" integer

For example, the following defines a constant DOZEN equal to 12:

const DOZEN = 12;  -->  #define DOZEN 12

Programs

RPC programs are declared using this syntax:

program-definition:
    "program" program-ident "{" 
        version-list
    "}" "=" value

version-list:
    version ";"
    version ";" version-list

version:
    "version" version-ident "{"
        procedure-list
    "}" "=" value

procedure-list:
    procedure ";"
    procedure ";" procedure-list

procedure:
    type-ident procedure-ident "(" type-ident ")" "=" value

This example shows the time protocol, revisited:

/*
 * time.x: Get or set the time. Time is represented as
 * number of seconds since 0:00, January 1, 1970.
 */
program TIMEPROG {
    version TIMEVERS {
        unsigned int TIMEGET(void) = 1;
        void TIMESET(unsigned) = 2;
    } = 1;
} = 44;

This file compiles into #defines in the output header file:

#define TIMEPROG 44
#define TIMEVERS 1
#define TIMEGET 1
#define TIMESET 2

Declarations

There are four kinds of declaration in XDR:

  • Simple declarations are like simple C declarations:

        simple-declaration:
            type-ident variable-ident
    

    For example:

        colortype color;    --> colortype color;
    

  • Fixed-array declarations are like array declarations in C:

        fixed-array-declaration:
            type-ident variable-ident "[" value "]"
    

    For example:

        colortype palette[8];    --> colortype palette[8];
    

  • Variable-array declarations have no explicit syntax in C, so XDR invents its own using angle brackets:

        variable-array-declaration:
            type-ident variable-ident "<" value ">"
            type-ident variable-ident "<" ">"
    

  • The maximum size is specified between the angle brackets. The size may be omitted, indicating that the array may be of any size:

        int heights<12>;     /* at most 12 items */
        int widths<>;        /* any number of items */
    

  • Since variable-length arrays have no explicit syntax in C, these declarations are actually compiled into structs. For example, the heights declaration gets compiled into the following struct:

        struct {
            u_int heights_len;       /* # of items in array */
            int *heights_val;        /* pointer to array */
        } heights;
    

  • The number of items in the array is stored in the _len component, and the pointer to the array is stored in the _val component. The first part of each component's name is the same as the name of the declared XDR variable.

  • Pointer declarations are made in XDR exactly as they are in C. You can't really send pointers over the network, but you can use XDR pointers to send recursive data types such as lists and trees. The type is actually called optional data, not pointer, in XDR language.

        pointer-declaration:
            type-ident "*" variable-ident
    

    For example:

        listitem *next;    -->    listitem *next;
    

Special Cases

There are a few exceptions to the rules described in the preceding sections:

  • Booleans

    C has no built-in boolean type. However, the RPC library does have a boolean type called bool_t that is either TRUE or FALSE. Things declared as type bool in XDR language are compiled into bool_t in the output header file. For example:

        bool married;    -->    bool_t married;
    

  • Strings

    C has no built-in string type, but instead uses the null-terminated char * convention. In XDR language, strings are declared using the string keyword and are compiled into char *s in the output header file. The number contained in the angle brackets specifies the maximum number of characters allowed in the string (not counting the NULL character). The maximum size may be left off, indicating a string of arbitrary length. For example:

        string name<32>;      -->    char *name;
        string longname<>;    -->    char *longname;
    

  • Opaque data

    Opaque data is used in RPC and XDR to describe untyped data; that is, just sequences of arbitrary bytes. Opaque data may be declared as either a fixed- or a variable-length array. For example:

        opaque diskblock[512];    -->    char diskblock[512];
        opaque filedata<1024>;    -->    struct {
                                            u_int filedata_len;
                                            char *filedata_val;
                                        } filedata;
    

  • Voids

    In a void declaration, the variable is not named. The declaration is just void and nothing else. Void declarations can occur in only two places: union definitions and program definitions (as the argument or the result of a remote procedure).