Annotation of 43BSDTahoe/new/xns/doc/courier.tbl.me, revision 1.1

1.1     ! root        1: \" $Header: courier.tbl.me,v 1.8 87/03/17 09:32:52 ed Exp $
        !             2: \" $Header: courier.tbl.me,v 1.8 87/03/17 09:32:52 ed Exp $
        !             3: \" $Log:       courier.tbl.me,v $
        !             4: \" Revision 1.8  87/03/17  09:32:52  ed
        !             5: \" Added -I switch to establish search path for DEPENDS UPON files.
        !             6: \" 
        !             7: \" Revision 1.7  86/11/22  07:12:05  jqj
        !             8: \" small changes reflecting differences between 4.3bsd and 4.2 Maryland XNS.
        !             9: \" 
        !            10: \" Revision 1.6  86/05/12  09:02:43  jqj
        !            11: \" minor revisions
        !            12: \" 
        !            13: \" Revision 1.5  85/03/26  06:30:58  jqj
        !            14: \" Revised public alpha-test version, released 26 March 1985
        !            15: \" 
        !            16: \" Revision 1.4  85/03/11  16:46:01  jqj
        !            17: \" Public alpha-test version, released 11 March 1985
        !            18: \" 
        !            19: .lg 0
        !            20: .fo ''%''
        !            21: .(l C
        !            22: .ps +4
        !            23: .b
        !            24: XNS Courier under UNIX (4.3BSD)
        !            25: .ps -4
        !            26: .sp 2
        !            27: .r
        !            28: .he $ XNS Courier $ $Date: 87/03/17 09:32:52 $
        !            29: J.Q. Johnson
        !            30: Computer Science Department
        !            31: Cornell University
        !            32: 405 Upson Hall
        !            33: Ithaca, NY  14853
        !            34: .)l
        !            35: .sh 1 "Introduction"
        !            36: .pp
        !            37: This document describes the implementation and use
        !            38: of the Courier remote procedure call protocol for Berkeley UNIX
        !            39: (version 4.3), with XNS.
        !            40: .sp 1
        !            41: .nf
        !            42: .b
        !            43: .(c
        !            44: ********************************************************
        !            45: Warning:  this document is a DRAFT design specification.
        !            46: It has not yet been fully implemented, and the details of
        !            47: the interface it specifies (particularly with respect to 
        !            48: Courier servers) are subject to change.  Send design
        !            49: comments to [email protected] or [email protected].
        !            50: ********************************************************
        !            51: .)c
        !            52: .r
        !            53: .fi
        !            54: .sp 1
        !            55: .uh "Changes since previous revision:"
        !            56: .np
        !            57: Added discussion of encapsulated protocols.
        !            58: .np
        !            59: Added discussion of Clearinghouse library calls for finding a connection,
        !            60: and relationship of connections to outstanding RPCs.
        !            61: .uh "Earlier changes:"
        !            62: .np
        !            63: Changed names of generated file names to include version number.
        !            64: Added discussion on scope of names vs. DEPENDS UPON modules.
        !            65: .np
        !            66: Users must now link Courier program ``Foo'' with Foo1_support.o.
        !            67: .np
        !            68: Added some discussion of freeing dynamic storage via clear_Foo.
        !            69: .np
        !            70: Changed
        !            71: .i CourierOpen()
        !            72: to take ns_addr structure rather than a string host name.  Routines for
        !            73: converting from name to address (using Clearinghouse) have not yet been
        !            74: designed. 
        !            75: .np
        !            76: Changed ERROR discussion to indicate that an ERROR is mapped to a long
        !            77: with value > 65535.
        !            78: .np
        !            79: Changed description of BDT to reflect use of 
        !            80: .i BDTread()
        !            81: and
        !            82: .i BDTwrite() .
        !            83: .np
        !            84: Added SPPMAXDATA.
        !            85: .np
        !            86: Changed discussion of BulkData constants to indicate that things now
        !            87: work correctly.
        !            88: .sh 2 "General Description"
        !            89: .pp
        !            90: Courier is both a protocol and a specification language.
        !            91: The protocol describes how remote procedures are invoked and
        !            92: how parameters and results of various data types are transmitted.
        !            93: The specification language, somewhat reminiscent of Mesa,
        !            94: provides a simple way of defining the remote interfaces of distributed
        !            95: programs.
        !            96: .pp
        !            97: This implementation is an attempt to allow UNIX C programmers to
        !            98: construct distributed Courier programs to communicate with other
        !            99: Courier implementations layered on XNS.  It thus requires kernel
        !           100: support for XNS Sequenced Packet Protocol sockets.
        !           101: .pp
        !           102: The simplest form of distributed program using Courier
        !           103: consists of three parts.
        !           104: The first is the specification,
        !           105: written in the Courier language.
        !           106: The specification must declare all procedures of
        !           107: the program that will be called remotely (the
        !           108: .i "server procedures" ).
        !           109: The next part is the implementation, in C, of the server procedures.
        !           110: Finally, there is the client portion of the program, also in C, which calls
        !           111: the server procedures.
        !           112: Note that either the client or server portion of the program may be
        !           113: omitted if the UNIX program is designed to act with a non-UNIX
        !           114: partner; for example, a simple printing client under UNIX might send
        !           115: files to a Xerox laser printer using the Printing protocol published
        !           116: and supported by Xerox.
        !           117: .sh 2 "Acknowledgements"
        !           118: .pp
        !           119: Contributors to the current implementation have included
        !           120: JQ Johnson, the primary implementor of the current version of the
        !           121: Courier compiler under UNIX,
        !           122: Eric Cooper, the author of the original UNIX Courier/TCP compiler,
        !           123: Jeff Mogul, the author of the exception signalling mechanism used
        !           124: here to handle abort and reject messages,
        !           125: James O'Toole, Chris Torek, and Mark Weiser,
        !           126: the authors of the 4.2BSD version of UNIX kernel support for XNS,
        !           127: Keith Sklower, the author of the present (4.3BSD) version of UNIX kernel support,
        !           128: Lee Moore, and Bill Nesheim.
        !           129: .pp
        !           130: Some of the software is copyright 1984 by Eric Cooper, and is for
        !           131: research use only; it may be freely distributed but may not be
        !           132: sold as a commercial product.
        !           133: .sh 2 "References"
        !           134: .pp
        !           135: This document assumes familiarity with the Courier language,
        !           136: details of which may be found in
        !           137: the Courier protocol definition,
        !           138: .i "Courier: The Remote Procedure Call Protocol" ,
        !           139: Xerox System Integration Standard 038112,
        !           140: December 1981, and ``Appendix F, Bulk Data Transfer,'' Xerox System
        !           141: Integration Standard 038112 Add. 1, October 1982.
        !           142: However, for reference purposes, a grammar for the Courier language
        !           143: may be found in the appendix to this document.
        !           144: .pp
        !           145: Users of Bulk Data Transfer will need to interact with SPP 
        !           146: (Sequenced Packet Protocol)
        !           147: streams, and may wish to see
        !           148: .i "Internet Transport Protocols" ,
        !           149: Xerox System Integration Standard 028112, December 1981.
        !           150: .pp
        !           151: Those interested in the workings of UNIX networking might consult
        !           152: ``A 4.2bsd Interprocess Communications Primer,''
        !           153: (DRAFT of March 11, 1983), by Samuel J. Leffler, Robert S. Fabry, and
        !           154: William N. Joy, and the 
        !           155: ``4.2BSD System Manual,''
        !           156: (Revised July, 1983), by William Joy 
        !           157: .i "et al" .
        !           158: .pp
        !           159: This document is also supplemented by various UNIX manual pages, notably
        !           160: .i "xnscourier(3) " ,
        !           161: and
        !           162: .i "except(3)" .
        !           163: .sh 2 "Software and Hardware Dependencies"
        !           164: .pp
        !           165: This package requires the following software to operate:
        !           166: .(l
        !           167: 4.3BSD (currently tested only for VAX and Gould versions)
        !           168: PCC, 4.3BSD version (probably\-limited testing has been done with Tartan C)
        !           169: .)l
        !           170: .pp
        !           171: To communicate with Xerox products you will need an Ethernet
        !           172: interface.  Most of the existing example applications depend heavily on
        !           173: the existence of a Xerox Clearinghouse somewhere on the local network.
        !           174: .sh 1 "The Courier Specification Language"
        !           175: .pp
        !           176: The Courier specification language is documented (loosely) in the Xerox
        !           177: ``Courier: The Remote Procedure Call Protocol.''  Our compiler
        !           178: implements a large subset of the specification.  Note that the embedding
        !           179: of Courier constructs in a message or bit stream is usually irrelevant to the
        !           180: applications programmer, who need only be concerned with the semantics
        !           181: of the Courier datatypes.  The courier compiler and runtimes translate
        !           182: Courier datatypes into more easily managed C constructs and handle the
        !           183: protocol of communication with the remote system.
        !           184: .pp
        !           185: This implementation places several restrictions on the specifications
        !           186: it will accept:
        !           187: .np
        !           188: No forward references are permitted in the courier
        !           189: specification.  The user must reorder type specifications so that all
        !           190: types and constants are defined before they are used.
        !           191: Special provisions are made for recursive type declarations of the form
        !           192: typically found in Bulk Data ``StreamOf'' declarations; see the
        !           193: discussion of BDT below for details.  General recursive declarations,
        !           194: e.g. that of the Filing ``Filter,'' are not supported.
        !           195: .np
        !           196: The Courier specification is unclear as to lexical issues, 
        !           197: name scope, and name overloading.
        !           198: In the current compiler, names should contain only upper and lower case
        !           199: letters or digits.  Also, all constants, types, and
        !           200: enumeration tags within a module should have distinct names.  Enumeration
        !           201: tag names are currently global; thus, different enumeration types, even
        !           202: in different modules, may not have the same tags.
        !           203: .np
        !           204: Constants of type CHOICE are not fully supported.  In particular, they
        !           205: are allowed only if the active arm of the choice is a nill record.
        !           206: .np
        !           207: Constants of type SEQUENCE are not supported.  Constants of type ARRAY
        !           208: (which have the same syntax as SEQUENCE constants) are supported, but are
        !           209: not rangechecked; ``Vect: ARRAY 10 OF INTEGER = {1,2,3}'' is accepted,
        !           210: though illegal.
        !           211: .np
        !           212: This implementation supports DEPENDS UPON and
        !           213: referenced types and constants.  However, the semantics of referenced
        !           214: enumeration constants is unclear.  Also, this implementation
        !           215: does not support recursive dependencies.
        !           216: .lp
        !           217: We think we've eliminated all other restrictions, but we're probably wrong.
        !           218: .sh 2 "Predefined types"
        !           219: .pp
        !           220: The following typedefs correspond to predefined Courier types:
        !           221: .(b
        !           222: .TS
        !           223: c c c
        !           224: l l l.
        !           225: Courier type   C typedef       Implemented (on VAX) as
        !           226: 
        !           227: BOOLEAN        Boolean char (0==TRUE, 1==FALSE)
        !           228: CARDINAL       Cardinal        unsigned short
        !           229: LONG CARDINAL  LongCardinal    unsigned long
        !           230: INTEGER        Integer short
        !           231: LONG INTEGER   LongInteger     long
        !           232: STRING String  char*
        !           233: UNSPECIFIED    Unspecified     unsigned short
        !           234: LONG UNSPECIFIED       LongUnspecified unsigned long
        !           235: .TE
        !           236: .)b
        !           237: Note that the representations of these types as seen by the applications
        !           238: programmer are quite different from those sent over a Courier SPP connection.
        !           239: For example, on the VAX a Cardinal's bytes are swapped before being
        !           240: sent out in network byte order.  A UNIX String corresponds to a pointer to
        !           241: a null-terminated array of characters while a Courier serialized STRING
        !           242: contains a count directly followed by an array of characters.  Note
        !           243: in particular that this implementation does
        !           244: not support Courier STRINGs containing the ascii character NUL.
        !           245: .sh 2 "Constructed types"
        !           246: .pp
        !           247: The Courier enumeration, array, and (non-null) record types
        !           248: correspond to C enumerations, arrays, and structures respectively.
        !           249: The Courier null record corresponds to an int.
        !           250: .pp
        !           251: The Courier sequence and choice types
        !           252: pose some problems when they are mapped into C.
        !           253: This is because an object of one of these types
        !           254: must contain run-time information (the length of the sequence
        !           255: or which choice is present)
        !           256: that is implicit in Courier, but must be made explicit
        !           257: in C.
        !           258: Furthermore, the C programmer must bear the responsibility of keeping
        !           259: this information consistent.
        !           260: .pp
        !           261: A sequence type is mapped into a structure consisting
        !           262: of a Cardinal called
        !           263: .i "length" ,
        !           264: and a pointer to the sequence elements called
        !           265: .i "sequence" .
        !           266: The consistency requirement is that
        !           267: .i "length"
        !           268: indicate the number of elements in the array pointed to
        !           269: by
        !           270: .i "sequence" .
        !           271: .pp
        !           272: A choice type is mapped into a structure consisting
        !           273: of an enumeration element called
        !           274: .i "designator" ,
        !           275: and a union of all the possible choices.
        !           276: The designator is of the enumeration type defined
        !           277: (implicitly or explicitly)
        !           278: in the declaration of the choice type.
        !           279: If this enumeration consists of elements
        !           280: .i A ,
        !           281: .i B ,
        !           282: and
        !           283: .i C ,
        !           284: then the choices are accessible as
        !           285: .i A_case ,
        !           286: .i B_case ,
        !           287: and
        !           288: .i C_case .
        !           289: The consistency requirement is that
        !           290: .i "designator"
        !           291: contain the enumeration value corresponding to
        !           292: which choice currently occupies the union.
        !           293: .sh 2 "Constants"
        !           294: .pp
        !           295: The Courier specification is silent on the precise representations of
        !           296: numbers and strings.  In this implementation, numbers may be represented
        !           297: as an optional minus sign followed by a
        !           298: sequence of decimal digits or a sequence of octal digits followed
        !           299: by ``B''.  A constant of type STRING 
        !           300: is represented by a sequence of characters enclosed in
        !           301: double quotation marks; it has the same restrictions as a C literal string (no
        !           302: embedded newlines, use ``\e'' as a prefix character, etc.) except that double
        !           303: double quotes are permitted to indicate quotations inside the string.
        !           304: .sp 1
        !           305: .nf
        !           306: minInt: INTEGER = -2147483648;         -- or 20000000000B --
        !           307: lastCard: CARDINAL = 177777B;          -- or 65535 --
        !           308: quotedName: STRING = "my name is \"jqj""\en";
        !           309: .fi
        !           310: .sp 1
        !           311: .pp
        !           312: Although the Courier language allows constants of
        !           313: any type to be declared,
        !           314: it is difficult to define constants of constructed types
        !           315: in C programs.
        !           316: This implementation normally handles constants by creating
        !           317: static variables initialized to the values of the (possibly structured)
        !           318: constants.  The technique is not in general possible for structured 
        !           319: constants of type CHOICE
        !           320: since they are implemented as C ``unions'', so this implementation does not
        !           321: support constants which contain a non-null CHOICE.  As a special case,
        !           322: constants whose selected variant is a null record are allowed, e.g.
        !           323: .sp 1
        !           324:        myImmediate: BulkData.Immediate = immediate [];
        !           325: .sp 1
        !           326: .sh 2 "Procedures"
        !           327: .pp
        !           328: PROCEDURE constants correspond to C functions plus a numeric jump table
        !           329: index used in serialization of procedure calls on the network.
        !           330: Procedure declarations may include argument lists, result lists, and
        !           331: error lists.  The argument list of the corresponding C function 
        !           332: contains one parameter for each parameter specified in the declaration,
        !           333: but is
        !           334: expanded by the addition of 2 parameters (see below).  The result list
        !           335: specifies a C structure to be returned by the C function containing the
        !           336: results; for a procedure, Foo, this result list is of type FooResults.
        !           337: Thus, a procedure declared
        !           338: .sp 1
        !           339:        Example: PROCEDURE [ ] RETURNS [ a: Foo, b: Baz ] = 0;
        !           340: .sp 1
        !           341: would be implemented by a C function with no arguments
        !           342: returning a structure of type ExampleResults with definition
        !           343: something like
        !           344: .sp 1
        !           345: .nf
        !           346:        typedef struct {
        !           347:                Foo a;
        !           348:                Baz b;
        !           349:        } ExampleResults
        !           350: .fi
        !           351: .sp 1
        !           352: Note that Foo and Baz might be arbitrarily complex structures;
        !           353: see ``Constructed Types'' above.
        !           354: .pp
        !           355: Importing a PROCEDURE from a DEPENDS UPON module imports only its jump
        !           356: table index.
        !           357: .sh 2 "ERROR constants"
        !           358: .pp
        !           359: An ERROR constant corresponds to a defined numeric value, and possibly
        !           360: to a C structure describing its argument list.  For example,
        !           361: .sp 1
        !           362:        OtherError: ERROR [ errorstring: STRING ] = 1;
        !           363: .sp 1
        !           364: corresponds approximately to the C declarations:
        !           365: .sp 1
        !           366: .nf
        !           367:        #define OtherError (1+ERROR_OFFSET)
        !           368:        typedef struct {
        !           369:                String errorstring;
        !           370:        } OtherErrorArgs;
        !           371: .fi
        !           372: .sp 1
        !           373: Note that ERROR values must be specified in the range 0 to 65535, but
        !           374: that they produce a symbol which appears to the C programmer
        !           375: with values of type int
        !           376: in the range 65536 to 131171 (in some contexts, the user may wish to
        !           377: treat C error values in the range 0 to 65535 as Unix error numbers).
        !           378: The structure corresponding to the error
        !           379: may be useful in creating and referencing error arguments
        !           380: in C functions.
        !           381: .sh 2 "Scope of Names"
        !           382: .pp
        !           383: Given a courier program beginning
        !           384: .sp
        !           385:        Example : PROGRAM 537 VERSION 1 =
        !           386: .sp
        !           387: types and constants declared in this program belong to the module ``Example1'';
        !           388: Their full names have prefixed to them the program name and version number.
        !           389: Given the declaration of ``minInt'' above, the XNS Courier compiler actually
        !           390: produces a header file (here
        !           391: .i Example1.h )
        !           392: containing the C declaration:
        !           393: .sp 1
        !           394: .nf
        !           395: static Integer Example1_minInt = {-2147483648};
        !           396: .fi
        !           397: .sp 1
        !           398: .pp
        !           399: This prefixing implies that a C programmer may refer to several different
        !           400: remote Courier programs without risk of name collision.
        !           401: For programmer convenience a second header file (here
        !           402: .i Example1_defs.h )
        !           403: is also created by the compiler containing macro definitions which widen
        !           404: the scope to ``Example1'' and allow the programmer to refer to types and
        !           405: constants without explicit qualification.  The programmer may include
        !           406: whichever header file he prefers.
        !           407: .pp
        !           408: Note that types and constants obtained from a DEPENDS UPON inclusion must
        !           409: be referenced using fully qualified form.  Note also that structure members,
        !           410: procedure arguments, and enum tags are not currently qualified; beware
        !           411: potential name conflicts!
        !           412: .sh 1 "Running the Courier Compiler"
        !           413: .pp
        !           414: The specification file is expected to have the extension
        !           415: .i ".cr" .
        !           416: Consider the following skeletal Courier program definition:
        !           417: .sp
        !           418:        Example : PROGRAM 537 VERSION 1 = BEGIN ... END.
        !           419: .sp
        !           420: The name of this Courier program is ``Example''; it is version 1.
        !           421: By convention, this specification would stored in the file
        !           422: .i "Example1.cr" .
        !           423: The ``537'' in this example is illustrative only;
        !           424: the program number is uniquely allocated from the range of LongCardinals
        !           425: by Xerox.  See Appendix B of the Courier standard for program number
        !           426: assignment procedures.
        !           427: .pp
        !           428: The first step is to use the Courier compiler on the specification.
        !           429: Assuming
        !           430: .i "xnscourier"
        !           431: is in your path (it is 
        !           432: .i "/usr/new/xnscourier"
        !           433: on the Berkeley distribution), type
        !           434: .sp 1
        !           435:        xnscourier Example1.cr
        !           436: .sp 1
        !           437: If there are no errors in compilation,
        !           438: the following files will be produced:
        !           439: .(b
        !           440: .TS
        !           441: c c
        !           442: l l.
        !           443: File   Contents
        !           444: 
        !           445: Example1_defs.h        scope widening macros (includes Example1.h)
        !           446: Example1.h     definitions, typedefs, and constant declarations
        !           447: Example1_support.c     routines to map between C and Courier
        !           448: Example1_server.c      server main program and support routines
        !           449: Example1_client.c      client routines to support applications calling Example
        !           450: .TE
        !           451: .)b
        !           452: .pp
        !           453: The header file
        !           454: .i "Example1_defs.h"
        !           455: should be included via
        !           456: .sp
        !           457:        #include ``Example1_defs.h''
        !           458: .sp
        !           459: in all user-written parts of the Courier program
        !           460: (i.e., the implementations of the client program and server procedures.)
        !           461: .pp
        !           462: The 
        !           463: .i "\fB-I\fIinclude-directory"
        !           464: switch may be used on the command line of
        !           465: .i xnscourier
        !           466: to establish a search path for Courier specification files named in a
        !           467: DEPENDS UPON clause. By default, the compiler looks first in the current
        !           468: directory followed by the courier-description-file named in the 
        !           469: .i /etc/Courierservices
        !           470: file.
        !           471: .sh 2 "Calling Remote Procedures \- Client Implementation"
        !           472: .pp
        !           473: A remote procedure appears to the C programmer to look almost like
        !           474: a local procedure (although it typically makes use of resources not
        !           475: available on the local machine or else runs many times slower
        !           476: than would a local procedure).  A C client program (i.e. the caller
        !           477: of a remote procedure) actually calls a stub function generated by
        !           478: the courier compiler, which in turn executes the code necessary to
        !           479: invoke the corresponding procedure on the remote machine.  
        !           480: .pp
        !           481: Before calling the remote procedure it is necessary to establish a
        !           482: Courier SPP connection (in the process binding a local socket to
        !           483: a remote machine).  This may be done by the calling the function
        !           484: CourierOpen(), passing it as argument a structure identifying the remote
        !           485: host.  This structure
        !           486: is of type ``ns_addr''
        !           487: as defined in the include file
        !           488: .i "#include <netns/ns.h>" .
        !           489: CourierOpen() returns an anonymous pointer
        !           490: (of type CourierConnection*) to a structure containing
        !           491: the socket to be used for this courier call, which should be passed
        !           492: to each remote procedure to be executed on the particular remote host.
        !           493: CourierOpen() returns NULL if there is a problem opening an SPP connection
        !           494: to the host specified.
        !           495: .pp
        !           496: For low-level access to ns_addr structures,
        !           497: the procedure getXNSaddr() is available, taking a string in the form
        !           498: ``network#a1.a2.a3.a4.a5.a6#socket'' (where all numbers are hex) or 
        !           499: ``networkhigh-networklow#d1-d2-d3-d4#socket'' (where all numbers are
        !           500: 3-digit decimal numbers)
        !           501: and returning a pointer to the
        !           502: corresponding ns_addr structure.  This result may then be passed as the
        !           503: argument to CourierOpen().
        !           504: More normally, the user will have a string in the form of an NS address,
        !           505: e.g. ``jqj:computer\ science:cornell-univ''.  This string can be
        !           506: coerced into a Clearinghouse ObjectName, then used in a Clearinghouse-based
        !           507: lookup to find the corresponding address:
        !           508: .sp 1
        !           509: .nf
        !           510:        Clearinghouse2_ObjectName hobj, hdefault;
        !           511:        struct ns_addr * host;
        !           512:        char * string;
        !           513: 
        !           514:        CH_NameDefault(&hdefault);      /* get defaults */
        !           515:        hobj = CH_StringToName(string, &hdefault);
        !           516:        host = CH_LookupAddr(hobj, 0);  
        !           517: .fi
        !           518: .sp 1
        !           519: .pp
        !           520: The SPP connection may be reused by multiple RPCs destined for the same
        !           521: host, even if the procedures are part of different remote programs.  However,
        !           522: the SPP connection may not be reused while an RPC is outstanding; thus,
        !           523: if you need to issue a Courier call during a BDT transfer, you will need
        !           524: to obtain a second CourierConnection.
        !           525: .pp
        !           526: The client then calls the compiler-generated stub function to invoke
        !           527: the remote procedure.  This stub
        !           528: function takes at least 2 arguments:
        !           529: .ip  (1)
        !           530: The pointer returned from CourierOpen().
        !           531: If the SPP connection does not
        !           532: currently exist (i.e. has been closed by the server)
        !           533: it is reopened for this call.
        !           534: .ip (2)
        !           535: A pointer to a user-supplied function which will be used if a Bulk Data
        !           536: Transfer is required (see the section on BDT below); if this remote
        !           537: procedure does not involve Bulk Data Transfer, then the function pointer
        !           538: should be 0 or NULL.
        !           539: .ip (3...)
        !           540: One additional argument corresponding to each parameter in the Courier
        !           541: language description of this procedure.
        !           542: .pp
        !           543: The remote procedure returns in one of 2 ways:  either it returns
        !           544: normally, passing back a structure corresponding to the RETURNS parameters
        !           545: specified in the procedure description, or it signals an error which
        !           546: may be caught using the DURING ... HANDLER mechanism described in
        !           547: .i except(1) .
        !           548: .pp
        !           549: A Courier procedure is allowed to return multiple results, a language feature
        !           550: not easily mapped into C.  To provide this functionality, all UNIX
        !           551: courier remote procedures actually return a structure
        !           552: containing the procedure results.  Thus, for a procedure named
        !           553: Foo, declared
        !           554: .sp 1
        !           555:        Foo: PROCEDURE [ ] RETURNS [str: STRING];
        !           556: .sp 1
        !           557: the C function Foo would return a structure of type FooResults, declared
        !           558: .sp 1
        !           559: .nf
        !           560:        typedef struct {
        !           561:                String str;
        !           562:        } FooResults;
        !           563: .fi
        !           564: .fi
        !           565: .pp
        !           566: Instead of returning a value of type determined by the RESULTS clause
        !           567: of a PROCEDURE declaration, a Courier remote procedure call may return
        !           568: a reject message indicating the remote system's inability to even
        !           569: attempt a remote operation, or an abort message raising a remote error,
        !           570: i.e. reporting the operation's failure.  Reject and abort messages are
        !           571: handled by a signalling mechanism  or if uncaught cause termination of
        !           572: the client program.  For example, a client calling the remote procedure
        !           573: defined by
        !           574: .sp 1
        !           575: .nf
        !           576:        NoSuchFile: ERROR = 0;
        !           577:        OtherError: ERROR [ errorstring: STRING ] = 1;
        !           578:        Example: PROCEDURE [ ] REPORTS [ NoSuchFile, OtherError ];
        !           579: .fi
        !           580: .sp 1
        !           581: might be coded as
        !           582: .sp 1
        !           583: .nf
        !           584:        conn = CourierOpen(destaddr);
        !           585:        DURING Example(conn,NULL)
        !           586:        HANDLER switch(Exception.Code) {
        !           587:        case REJECT_ERROR:
        !           588:                fprintf(stderr,"Remote reject.\en");
        !           589:                exit(1);
        !           590:        case NoSuchFile:
        !           591:                fprintf(stderr,"No such file\en");
        !           592:                break;
        !           593:        case OtherError:
        !           594:                fprintf(stderr,"Remote error %s\en",
        !           595:                        CourierErrArgs(OtherErrorArgs,errorstring) );
        !           596:                break;
        !           597:        case PROTOCOL_VIOLATION:
        !           598:        case INTERNAL_ERROR:
        !           599:                fprintf(stderr,"Local internal error %s\en",
        !           600:                        Exception.Message);
        !           601:                break;
        !           602:        default:
        !           603:                fprintf(stderr,"Unknown error, type %d\en",
        !           604:                        Exception.Code);
        !           605:        }       
        !           606:        END_HANDLER
        !           607: .fi
        !           608: .sp 1
        !           609: The error value, Exception.code, will be one of (1) ``REJECT_ERROR'',
        !           610: which indicates a REJECT message from the remote server, (2) a program-defined
        !           611: ERROR value (offset by ERROR_OFFSET), (3) or ``INTERNAL_ERROR'' 
        !           612: or ``PROTOCOL_VIOLATION''
        !           613: indicating
        !           614: a serious internal error in the Unix courier implementation; in the third
        !           615: case, a string is available as Exception.Message.
        !           616: Both errors and rejections may have arguments, which can be accessed
        !           617: within a handler as a struct pointed to by Exception.Message.  For
        !           618: programmer convenience a macro, CourierErrArgs, is defined, taking
        !           619: as arguments the typedef defining the current error's arguments 
        !           620: and the name of the argument to be accessed; this macro may be used
        !           621: only within an error handler.
        !           622: For rejection messages, the type should be ``rejectionDetails,''
        !           623: as defined on page 25 of the Courier spec.
        !           624: .pp
        !           625: When done with a particular remote connection, the client should call
        !           626: CourierClose() with argument the pointer returned from CourierOpen()
        !           627: to free the socket and cleanly close the connection.
        !           628: .pp
        !           629: The client program should be loaded with 
        !           630: .i "Example1_client.o" ,
        !           631: .i "Example1_support.o" ,
        !           632: and -lcourier (the XNS Courier library)
        !           633: to produce an executable program.
        !           634: .sh 2 "Writing a Courier Server"
        !           635: .pp
        !           636: The Courier protocol specifies a standard for communicating
        !           637: parameters and results which has been adhered to in this
        !           638: implementation.
        !           639: It also specifies an initial connection protocol
        !           640: and a format for messages.
        !           641: .pp
        !           642: There is a single Courier Daemon per UNIX machine whose function
        !           643: is to listen on a well-known XNS port for Courier interface activation
        !           644: requests.
        !           645: A request contains the number of the Courier program
        !           646: whose server is to be activated, and its version number.  These two
        !           647: numbers are used as a key in the file 
        !           648: .i /etc/Courierservices
        !           649: to identify
        !           650: the executable file associated with the particular courier program.
        !           651: .i /etc/Courierservices
        !           652: consists of a sequence of lines of the form
        !           653: .sp 1
        !           654:        program-name  program-number  version  courier-description-file  server-binary-file
        !           655: .sp 1
        !           656: The daemon
        !           657: either spawns the executable file
        !           658: or replies with a reject message.  Note that each incoming remote procedure
        !           659: call may be serviced by a separate UNIX process.
        !           660: This implies that connection-oriented services (services which consist of a
        !           661: sequence of related procedure calls) must be implemented by maintaining
        !           662: the state of connections in a file.
        !           663: .pp
        !           664: The executable file containing the server for a particular remote
        !           665: program consists of a main program generated by the courier compiler
        !           666: which interprets CALL messages from the remote client and calls
        !           667: one or more user-written functions to implement the
        !           668: courier procedures.
        !           669: .pp
        !           670: Given a courier specification for the remote program Example containing
        !           671: remote procedure Example,
        !           672: the server functions (including the C function Example and any support
        !           673: routines needed)
        !           674: should be loaded with 
        !           675: .i "Example1_server.o" ,
        !           676: .i "Example1_support.o" ,
        !           677: and -lcourier
        !           678: to produce the server process that will be invoked whenever
        !           679: an activation request arrives.
        !           680: .pp
        !           681: A server function receives an argument list similar to that passed by
        !           682: a client to a remote procedure.  The first two arguments in the
        !           683: parameter list should be ignored; each succeeding argument corresponds
        !           684: to one parameter in the courier declaration.
        !           685: .pp
        !           686: To return from a server function you must return a structure containing
        !           687: the results of the procedure.  If this structure contains pointers (e.g.
        !           688: Strings) you should insure that the data pointed to is allocated
        !           689: staticly or on the heap rather than on the stack.
        !           690: You must not call exit(3) from within the server function; doing so will
        !           691: cause the client (remote caller) to hang indefinitely waiting for a
        !           692: reply.
        !           693: .pp
        !           694: To abort a server function, returning an error code, use the
        !           695: ``raise(code, msg)''
        !           696: library function, with arguments the error code and either NULL (if the
        !           697: error takes no arguments) or a pointer to a structure containing the
        !           698: arguments to the error.  Raise causes a longjump out of the function
        !           699: directly to the handler, and thence to the remote procedure caller.
        !           700: For example, to return the OtherError message defined above one might code
        !           701: .sp 1
        !           702: .nf
        !           703:        OtherErrorArg randomerr;
        !           704:        . . .
        !           705:        randomerr.errorstring = sys_errlist[errno]);
        !           706:        raise(OtherError,(char*) &randomerr);
        !           707: .fi
        !           708: .sp 1
        !           709: .pp
        !           710: When a server function returns to its main program caller the main()
        !           711: sends the appropriate RETURN or ABORT message to the remote client,
        !           712: and does an exit(0).  The server then holds the connection for up to 90 
        !           713: seconds waiting for another Courier call to arrive on the same SPP
        !           714: stream.
        !           715: .sh 2 "Dynamic Allocation"
        !           716: .pp
        !           717: One additional 
        !           718: .i caveat 
        !           719: is necessary when using Courier remote procedure calls from C:  C does not
        !           720: have garbage collection, so you should free any dynamically allocated storage
        !           721: when you are done with it.  In general, top level Courier objects are
        !           722: always allocated from the stack or statically; however, strings and sequences
        !           723: within an object are generally allocated from the heap using malloc().
        !           724: .pp
        !           725: A client program which calls the remote procedure Foo defined above,
        !           726: returning a STRING,
        !           727: actually has returned to it a C struct containing a pointer to an array
        !           728: of malloced characters.  When done with this string, the program may free
        !           729: it by calling clear_FooResults() with argument the address of the
        !           730: result returned by the call to Foo.  Thus:
        !           731: .sp 1
        !           732: .nf
        !           733:        FooResults result;
        !           734:        . . .
        !           735:        result = Foo(conn,NULL);
        !           736:        . . .
        !           737:        printf("result was %s\n",result.str);
        !           738:        clear_FooResults(&result);
        !           739: .fi
        !           740: .sp 1
        !           741: .sh 1 "Using Bulk Data Transfer"
        !           742: .pp
        !           743: .sh 2 "Overview"
        !           744: .pp
        !           745: When a Courier program needs to transfer an arbitrary amount of
        !           746: information as an argument or result of a Courier procedure, the
        !           747: procedure is usually defined to have an argument of type ``BulkData.Sink''
        !           748: or ``BulkData.Source'' (and a ``DEPENDS UPON BulkData''
        !           749: is included in the program).
        !           750: The argument is a ``source'' if it is information transferred from caller
        !           751: to server (as though a procedure argument), a ``sink'' if it is
        !           752: information transferred from server to caller (as though a procedure
        !           753: result).  In this
        !           754: implementation, a Courier procedure may have at most one such argument,
        !           755: which must have the value null or immediate.
        !           756: In a Courier call, the bulk data is transmitted in a special way,
        !           757: multiplexed into the same data path as control messages
        !           758: between the arguments and the results.
        !           759: .pp
        !           760: The BDT user should include in the 
        !           761: .i ".cr"
        !           762: file a ``DEPENDS UPON BulkData (0) VERSION 1'', then reference these
        !           763: arguments as BulkData.Source and BulkData.Sink in PROCEDURE
        !           764: declarations.
        !           765: The declaration of a Courier PROCEDURE can then indicate a bulk
        !           766: data transfer by including in its formal argument list a variable
        !           767: of type BulkData.Source or BulkData.Sink
        !           768: as appropriate.
        !           769: The corresponding parameter to the C procedure will be of type
        !           770: BulkData1_Descriptor.
        !           771: .sh 2 "Coding a Client"
        !           772: .pp
        !           773: To use Bulk Data in a client program, the Courier definition of the
        !           774: procedure must include one BulkData.Source or BulkData.Sink parameter.
        !           775: As the actual argument to the call, the C client passes a constant,
        !           776: either BulkData1_immediateSource, BulkData1_immediateSink, BulkData1_nullSource, 
        !           777: or BulkData1_nullSink as appropriate.  The client
        !           778: also specifies a pointer to a user-supplied function as the second
        !           779: argument to the remote procedure.
        !           780: Courier sets up the transaction, then calls the supplied function with
        !           781: one argument, the pointer returned by CourierOpen() describing the
        !           782: SPP socket on which to write (if a source argument) or read
        !           783: (if a sink) the bulk data. 
        !           784: .pp
        !           785: To complete the transaction normally, the
        !           786: function should send packets of SPP data terminated by
        !           787: an end-of-message (if writing) or read up to but not beyond an
        !           788: end-of-message (if reading), and should return normally to its caller.
        !           789: To do so, it should call the procedures BDTread(), BDTwrite(), or 
        !           790: BDTclosewrite()
        !           791: as appropriate.  BDTread() and BDTwrite() take arguments analogous to read()
        !           792: and write() except that instead of a file descriptor they accept the SPP
        !           793: descriptor returned by CourierOpen(); their arguments are thus
        !           794: (1) the pointer to the SPP data structure,
        !           795: (2) a pointer to an array of characters (the IO buffer),
        !           796: and (3) a count;
        !           797: they return the number of characters actually read or written.
        !           798: .pp
        !           799: The UNIX program can finish a write transfer by calling BDTclosewrite() or
        !           800: BDTabort(), each with the SPP descriptor as argument.
        !           801: BDTclosewrite() produces an SPP end-of-message, while BDTabort() instructs
        !           802: Courier to discard any buffered data and
        !           803: send a Bulk Data Abort to abort the transaction.  A read transfer may also be
        !           804: prematurely ended by calling BDTabort().
        !           805: .sh 2 "Coding a Server"
        !           806: .pp
        !           807: Writing a server that uses BDT
        !           808: is similar to writing the BDT function in a caller.
        !           809: The server is passed a BulkData.Source or BulkData.Sink (the two are
        !           810: indistinguishable at run time).
        !           811: The server routine should check to make sure that the source or sink
        !           812: is of type null or immediate (active and passive descriptors are not
        !           813: supported in this implementation) by means of code such as:
        !           814: .sp 1
        !           815: .nf
        !           816: Foo(bdtconnection,ignoredarg,..., s, ...)
        !           817:        CourierConnection *bdtconnection;
        !           818:        BulkData1_Sink s;
        !           819:        . . .
        !           820:        switch (s.designator) {
        !           821:        case active:
        !           822:        case passive:
        !           823:                /* can't raise BulkData1_InvalidDescriptor, so */
        !           824:                raise(someerror);
        !           825:        case null:
        !           826:                /* handle null transfer */
        !           827:                break;
        !           828:        case immediate:
        !           829:                /* handle normal, i.e. immediate, transfer */
        !           830:                break;
        !           831:        }
        !           832: .fi
        !           833: .sp 1
        !           834: Note that the BDT error InvalidDescriptor cannot be used unless the
        !           835: procedure declaration explicitly notes that this is a valid REPORTS error
        !           836: type.  Normally, each courier program defines its own error to be reported
        !           837: on BDT problems.
        !           838: .pp
        !           839: Use the first argument to the server procedure as the handle on the
        !           840: BDT connection.
        !           841: As with a client BDTabort() may
        !           842: be used to send abort messages.
        !           843: .sh 2 "Sending Bulk Data"
        !           844: .pp
        !           845: Sending bulk data, either as client or server, is quite
        !           846: straightforward.  Use the supplied procedure BDTwrite(), whose
        !           847: semantics are similar to write().  BDTwrite() will return -1
        !           848: if an error occurs or an abort from the listener is received; in this
        !           849: case, the sender should immediately cease transmitting BDT data, and
        !           850: should instead call BDTabort(). 
        !           851: Typical code to send data on ``bdtconnection'',
        !           852: in this case read from a buffered file open
        !           853: as ``source,'' might appear as:
        !           854: .sp 1
        !           855: .nf
        !           856:        CourierConnection *bdtconnection;
        !           857:        FILE *source;
        !           858:        int count;
        !           859:        char buffer[SPPMAXDATA];
        !           860:        ...
        !           861:        while ( (count = fread(buffer,1,SPPMAXDATA,source)) > 0 &&
        !           862:                BDTwrite(bdtconnection,buffer,count) >= 0 )
        !           863:                ;
        !           864:        if (count <= 0) /* succsfull transfer */
        !           865:                BDTclosewrite(bdtconnection);
        !           866:        else
        !           867:                BDTabort(bdtconnection);
        !           868: .fi
        !           869: .sp 1
        !           870: .pp
        !           871: Each BDTwrite() transmits one packet after checking for an abort message
        !           872: from the receiver.
        !           873: A sender which gets an abort from a remote receiver must immediately
        !           874: stop transferring data and instead must echo an abort via BDTabort()
        !           875: to acknowledge the end of the transfer.
        !           876: .pp
        !           877: Also, for efficiency's
        !           878: sake it is desirable to write packets as near as possible to the
        !           879: nominal maximum length of an SPP packet; this suggests a buffer length of
        !           880: 534 bytes.  For programmer convenience, the file
        !           881: .i courier.h
        !           882: defines the symbol SPPMAXDATA as 534.
        !           883: .sh 2 "Receiving Bulk Data"
        !           884: .pp
        !           885: To make receiving bulk data simple, the routine BDTread() performs checks for
        !           886: various conditions on receipt of each packet.  BDTread() will return the
        !           887: count of the number of bytes actually read in each packet (skipping empty
        !           888: packets), or 0 to indicate end of data.  If an error or BDT abort message
        !           889: occurs, BDTread() will return -1; if this occurs, the receiver should 
        !           890: immediately stop reading BDT data.
        !           891: .pp
        !           892: Typical code for receiving bulk data on the socket described by 
        !           893: ``bdtconnection'' and
        !           894: depositing it in the file opened for buffered ouput as ``destfile''
        !           895: might be:
        !           896: .sp 1
        !           897: .nf
        !           898:        CourierConnection *bdtconnection;       /* BDT source descriptor */
        !           899:        FILE *destfile;                         /* output file */
        !           900:        char sppdata[SPPMAXDATA];
        !           901:        ...
        !           902:                count = BDTread(bdtconnection, sppdata, SPPMAXDATA);
        !           903:                while (count > 0) {     /* actually read data */
        !           904:                        if (fwrite(sppdata, 1, count, destfile) == 0) {
        !           905:                                        /* error while writing */
        !           906:                                BDTabort(bdtconnection);
        !           907:                                break;
        !           908:                        }
        !           909:                        count = BDTread(bdtconnection, sppdata, SPPMAXDATA);
        !           910:                }
        !           911:                if (count == 0) {
        !           912:                        /* successful */
        !           913:                        ...
        !           914: .fi
        !           915: .sp 1
        !           916: .sh 2 "StreamOf declarations"
        !           917: .pp
        !           918: Frequently, data to be sent on a Bulk Data connection will be described
        !           919: by a StreamOfSomething declaration in the courier specification.  For
        !           920: example, in Clearinghouse several routines are documented as returning
        !           921: a StreamOfObjectName; unfortunately, a StreamOf declaration is recursive,
        !           922: and so is not immediately translatable into automatic packing and unpacking
        !           923: routines which need to know in advance how much space to allocate for the
        !           924: result.  As a kludge, we translate such types as if the recursive
        !           925: part of the declaration were a null record.  
        !           926: Consider:
        !           927: .sp 1
        !           928: .nf
        !           929: StreamOfObjectName: TYPE = CHOICE OF {
        !           930:        nextSegment (0) => RECORD [
        !           931:                segment: SEQUENCE OF ObjectName,
        !           932:                restOfStream: StreamOfObjectName ],
        !           933:        lastSegment (1) => SEQUENCE OF ObjectName};
        !           934: .fi
        !           935: .sp 1
        !           936: .lp
        !           937: In the above declaration, the ``restOfStream is effectively ignored.
        !           938: Given this declaration, the following routine may be passed to a
        !           939: remote procedure as the Bulk Data actor.
        !           940: .sp 1
        !           941: .nf
        !           942: #include "Clearinghouse_support.c"     /* get internalize_* routines */
        !           943: #define MAXPACKS 5
        !           944: 
        !           945: GetData(conn)
        !           946:        CourierConnection *conn;
        !           947: {
        !           948:        int count, i;
        !           949:        Unspecified buffer[MAXWORDS*MAXPACKS], *bp, *bufend;
        !           950:        StreamOfObjectName obnames;
        !           951:        
        !           952:        bufend = buffer;
        !           953:        bp = buffer+MAXWORDS*(MAXPACKS-1);    /* end of available space */
        !           954:        while (count = BDTread(conn, (char*)bufend, 
        !           955:                                MAXWORDS*sizeof(Unspecified))
        !           956:                ) {
        !           957:                bufend += count/sizeof(Unspecified);
        !           958:                if (bufend > bp) {
        !           959:                        fprintf(stderr,"BDT read too big to fit\en");
        !           960:                        BDTabort(conn);
        !           961:                        /* should clear out stuff here if we knew how much */
        !           962:                }
        !           963:        }
        !           964:        bp = buffer;
        !           965:        while (bp < bufend) {
        !           966:                bp += internalize_StreamOfObjectName(&obnames,bp);
        !           967:                if (0 == (int) obnames.designator)
        !           968:                   for (i = 0; i < obnames.nextSegment_case.segment.length; i++)
        !           969:                        ProcessObjectName(
        !           970:                                obnames.nextSegment_case.segment.sequence[i]);
        !           971:                else {
        !           972:                   for (i = 0; i < obnames.lastSegment_case.length; i++)
        !           973:                        ProcessObjectName(
        !           974:                                obnames.lastSegment_case.sequence[i]);
        !           975:                   return;
        !           976:                }
        !           977:        }
        !           978: }
        !           979: .fi
        !           980: .sp 1
        !           981: .lp
        !           982: Note that this code is very awkward, and that it requires that the whole
        !           983: bulk data transfer be stored in memory before it is unpacked.  Stay
        !           984: tuned; we intend to modify the compiler to make the handling of such
        !           985: streams much easier, at the cost of incompatibility with the existing
        !           986: scheme, of course!
        !           987: .sh 2 "Encapsulated Protocols"
        !           988: .pp
        !           989: Some courier programs use what might be termed ``encapsulated protocols''
        !           990: as a method of type punning to escape from the restrictions of the Courier
        !           991: language.  For example, in Filing
        !           992: .sp 1
        !           993: .nf
        !           994:        Attribute: RECORD [
        !           995:                type: AttributeType, value: SEQUENCE OF UNSPECIFIED ];
        !           996:        checksum: AttributeType = 0;
        !           997:        Checksum: TYPE = CARDINAL;
        !           998:        createdBy: AttributeType = 1;
        !           999:        CreatedBy: TYPE = User;
        !          1000: .fi
        !          1001: .sp 1
        !          1002: Depending on the value of the type subfield, the data in the value field
        !          1003: is intended to be interpreted as either a CARDINAL or a User name.
        !          1004: .pp
        !          1005: Such encapsulation is in general bad design unless a very compelling
        !          1006: need for it exists.  It is also awkward to support in a C program.  The
        !          1007: best way to handle such types is to write functions to code and decode
        !          1008: the encapsulated value using the low level packing and unpacking routines
        !          1009: provided by the Courier compiler.  For example:
        !          1010: .sp 1
        !          1011: .nf
        !          1012:        Filing4_User
        !          1013:        AttrToUser(attr)
        !          1014:                Filing4_Attribute *attr;
        !          1015:        {
        !          1016:                Unspecified buf[2049], *bp; /* space for biggest attribute */
        !          1017:                Cardinal len;
        !          1018:                Filing4_User retval;
        !          1019: 
        !          1020:                /* useful fact:   Item: TYPE = SEQUENCE OF UNSPECIFIED; */
        !          1021:                externalize_Clearinghouse2_Item(&(attr->value), buf);
        !          1022:                bp = buf;
        !          1023:                bp += internalize_Cardinal(&len, bp);
        !          1024:                bp += internalize_Filing4_User(&retval, bp);  
        !          1025:                return(retval);
        !          1026:        }
        !          1027: 
        !          1028:        UserToAttr(id, attr)
        !          1029:                Filing4_User id;
        !          1030:                Filing4_Attribute *attr;
        !          1031:        {
        !          1032:                Unspecified buf[2049], *bp;
        !          1033:                Cardinal len;
        !          1034: 
        !          1035:                /* don't know length yet, so leave space for it */
        !          1036:                bp = buf + sizeof_Cardinal(len);
        !          1037:                len = externalize_Filing4_User(&id, bp);
        !          1038:                (void) externalize_Cardinal(&len, buf);
        !          1039:                internalize_Clearinghouse2_Item(&(attr->value), buf);
        !          1040:                return;
        !          1041:        }
        !          1042: 
        !          1043: .fi
        !          1044: .sp 1
        !          1045: Since the SEQUENCE OF UNSPECIFIED may have undergone arbitrary
        !          1046: transformations
        !          1047: during deserialization, translating it to a User record consists of first
        !          1048: reserializing it into an array of char, then deserializing it using the
        !          1049: low-level routine appropriate to the encapsulated type.  Translating to
        !          1050: an encapsulated type is an almost precise inverse.
        !          1051: .sh 1 "Example I:  Passwd.cr"
        !          1052: .pp
        !          1053: This section contains a Courier program which implements remote lookup in
        !          1054: .i "/etc/passwd"
        !          1055: (the UNIX database of user names, passwords, home directories, and so
        !          1056: on).
        !          1057: It does not utilize Bulk Data Transfer, but does illustrate most other
        !          1058: features of this courier implementation.
        !          1059: The applications programmer would first write 
        !          1060: .i PasswordLookup.cr ,
        !          1061: the description of the courier interface for the service, then might
        !          1062: write
        !          1063: .i PasswordLookup.c ,
        !          1064: containing the routines needed to implement a server for this courier
        !          1065: program, or might write
        !          1066: .i lookup.c ,
        !          1067: a typical client of this service.
        !          1068: .bp
        !          1069: .sh 2 "The specification (PasswordLookup.cr)"
        !          1070: .sp 1
        !          1071: .nf
        !          1072: PasswordLookup : PROGRAM 754 VERSION 1 =
        !          1073: 
        !          1074: BEGIN
        !          1075: 
        !          1076:     -- This is a translation of the passwd structure in <pwd.h>
        !          1077: 
        !          1078:     Passwd : TYPE = RECORD [
        !          1079:        pw_name, pw_passwd : STRING,
        !          1080:        pw_uid, pw_gid, pw_quota : LONG CARDINAL,
        !          1081:        pw_comment, pw_gecos, pw_dir, pw_shell : STRING
        !          1082:     ];
        !          1083: 
        !          1084:     -- Remote Errors
        !          1085: 
        !          1086:     NoSuchUser : ERROR = 0;
        !          1087:     OtherError : ERROR [ errorstring: STRING ] = 1;
        !          1088: 
        !          1089:     -- Remote entry points.
        !          1090: 
        !          1091:     LookupUid : PROCEDURE [ uid : CARDINAL ] RETURNS [ passwd : Passwd ]
        !          1092:                    REPORTS [ NoSuchUser ]
        !          1093:                    = 0;
        !          1094: 
        !          1095:     LookupUser : PROCEDURE [ user : STRING ]
        !          1096:                    RETURNS [ passwd : Passwd, forward : STRING ]
        !          1097:                    REPORTS [ NoSuchUser, OtherError ]
        !          1098:                    = 1;
        !          1099: 
        !          1100: END.
        !          1101: .fi
        !          1102: .sp 2
        !          1103: .sh 2 "PasswordLookup_defs.h"
        !          1104: .sp 1
        !          1105: .nf
        !          1106: /*
        !          1107:  * Declarations for Courier program PasswordLookup.
        !          1108:  */
        !          1109: #include <courier.h>
        !          1110: #include <except.h>
        !          1111: 
        !          1112: typedef struct {
        !          1113:        String pw_name;
        !          1114:        String pw_passwd;
        !          1115:        LongCardinal pw_uid;
        !          1116:        LongCardinal pw_gid;
        !          1117:        LongCardinal pw_quota;
        !          1118:        String pw_comment;
        !          1119:        String pw_gecos;
        !          1120:        String pw_dir;
        !          1121:        String pw_shell;
        !          1122: } Passwd;
        !          1123: 
        !          1124: #define NoSuchUser 0
        !          1125: 
        !          1126: #define OtherError 1
        !          1127: typedef struct {
        !          1128:        String errorstring;
        !          1129: } OtherErrorArg;
        !          1130: 
        !          1131: typedef struct {
        !          1132:        Passwd passwd;
        !          1133: } LookupUidResult;
        !          1134: 
        !          1135: typedef struct {
        !          1136:        Passwd passwd;
        !          1137:        String forward;
        !          1138: } LookupUserResult;
        !          1139: 
        !          1140: extern LookupUidResult LookupUid();
        !          1141: 
        !          1142: extern LookupUserResult LookupUser();
        !          1143: .fi
        !          1144: .sp 2
        !          1145: .sh 2 "Makefile"
        !          1146: .sp 1
        !          1147: .nf
        !          1148: CFLAGS = -O
        !          1149: USEROBJS = lookup.o PasswordLookup_client.o
        !          1150: SRVROBJS = PasswordLookup.o PasswordLookup_server.o
        !          1151: LIBS = -lcr
        !          1152: DESTDIR = /usr/lib/courier
        !          1153: 
        !          1154: all:   lookup PasswordLookup
        !          1155: 
        !          1156: lookup:        $(USEROBJS)
        !          1157:        cc -o lookup $(USEROBJS) $(LIBS)
        !          1158: 
        !          1159: PasswordLookup:        $(SRVROBJS)
        !          1160:        cc -o PasswordLookup $(SRVROBJS) $(LIBS)
        !          1161: 
        !          1162: $(USEROBJS) $(SRVROBJS):       PasswordLookup_defs.h
        !          1163: 
        !          1164: PasswordLookup_defs.h \e
        !          1165: PasswordLookup_server.c \e
        !          1166: PasswordLookup_client.c:       PasswordLookup.cr
        !          1167:        courier PasswordLookup.cr
        !          1168: 
        !          1169: install:       all
        !          1170:        install -s PasswordLookup $(DESTDIR)
        !          1171: 
        !          1172: clean:
        !          1173:        -rm -f *.o PasswordLookup_*.c PasswordLookup_defs.h
        !          1174: .fi
        !          1175: .bp
        !          1176: .sh 2 "The server procedures (PasswordLookup.c)"
        !          1177: .sp 1
        !          1178: .nf
        !          1179: #include <stdio.h>
        !          1180: #include "PasswordLookup_defs.h"
        !          1181: 
        !          1182: extern Passwd *getpwnam(), *getpwuid();
        !          1183: 
        !          1184: LookupUidResult
        !          1185: LookupUid(CourierConnection,CourierBDTProc,uid)
        !          1186:        CourierConnection *CourierConnection, CourierBDTProc;
        !          1187:        Cardinal uid;
        !          1188: {
        !          1189:        Passwd *pw;
        !          1190: 
        !          1191:        pw = getpwuid(uid);
        !          1192:        if (pw == NULL)
        !          1193:                raise(NoSuchUser,NULL);
        !          1194:        else {
        !          1195:                return (*(LookupUidResult*) &pw);
        !          1196:        }
        !          1197: }
        !          1198: 
        !          1199: LookupUserResult
        !          1200: LookupUser(CourierConnection,CourierBDTProc,user)
        !          1201:        CourierConnection *CourierConnection, CourierBDTProc;
        !          1202:        String user;
        !          1203: {
        !          1204:        LookupUserResult result;
        !          1205:        Passwd *pw;
        !          1206:        FILE *fwdfd;
        !          1207:        static char forward[100];
        !          1208: 
        !          1209:        pw = getpwnam(user);
        !          1210:        if (pw == NULL)
        !          1211:                raise (NoSuchUser,NULL);
        !          1212:        else {
        !          1213:                sprintf(forward,"%s/.forward",pw->pw_dir);
        !          1214:                if ((fwdfd = fopen(forward,"r")) == NULL) {
        !          1215:                        forward[0] = '\e0';
        !          1216:                else {
        !          1217:                        fgets(forward,100,fwdfd);
        !          1218:                        fclose(fwdfd);
        !          1219:                        if (strlen(forward) < 2) (
        !          1220:                                static OtherErrorArg randomerr = {
        !          1221:                                        "invalid forwarding file"};
        !          1222:                                raise(OtherError,&randomerr));
        !          1223:                }
        !          1224:                result.password = *pw;
        !          1225:                result.forward = forward;
        !          1226:                return (result);
        !          1227:        }
        !          1228: }
        !          1229: .fi
        !          1230: .bp
        !          1231: .sh 2 "The user program (lookup.c)"
        !          1232: .sp 1
        !          1233: .nf
        !          1234: /*
        !          1235:  * Sample program to access remote password lookup.
        !          1236:  * Usage: lookup machine username
        !          1237:  */
        !          1238: #include <stdio.h>
        !          1239: #include "PasswordLookup_defs.h"
        !          1240: 
        !          1241: main(argc, argv)
        !          1242:        int argc;  char **argv;
        !          1243: {
        !          1244:        LookupUserResult result;
        !          1245:        Passwd passwd;
        !          1246:        CourierConnection *connection;
        !          1247: 
        !          1248:        if (argc < 3 || (destaddr = getXNSaddr(argv[1])) == NULL)) {
        !          1249:                fprintf(stderr,"Usage:  %s machine file ...\en",argv[0]);
        !          1250:                exit(1);
        !          1251:        }
        !          1252:        if ((connection = CourierOpen(destaddr)) == NULL) {
        !          1253:                fprintf(stderr,"Can't open connection to %s\en",argv[1]);
        !          1254:                exit(1);
        !          1255:        }
        !          1256:        DURING
        !          1257:                result = LookupUser(connection,NULL,argv[2]);
        !          1258:        HANDLER
        !          1259:                switch(Exception.Code) {
        !          1260:        case Courier_reject:
        !          1261:                fprintf("Connection rejected, code = %d\en",
        !          1262:                        CourierErrArgs(rejectionDetails,designator) );
        !          1263:                exit(1);
        !          1264:        case NoSuchUser:
        !          1265:                printf("User %s unknown on %s.\en", argv[2], argv[1]);
        !          1266:                exit(0);
        !          1267:        case OtherError:
        !          1268:                fprintf(stderr,"Remote error %s\en",
        !          1269:                        CourierErrArgs(OtherErrorArg,errorstring) );
        !          1270:                exit(1);
        !          1271:        }
        !          1272:        END_HANDLER;
        !          1273: 
        !          1274:        displaypwd(& result.passwd);
        !          1275:        displayfwd(result.forward);
        !          1276:        CourierClose(connection);
        !          1277: }
        !          1278: 
        !          1279: displaypwd(p)
        !          1280:        Passwd *p;
        !          1281: {
        !          1282:        printf("%s:%s:%d:%d:%s:%s:%s\en",
        !          1283:                p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid,
        !          1284:                p->pw_gecos, p->pw_dir, p->pw_shell);
        !          1285: }
        !          1286: 
        !          1287: displayfwd(s)
        !          1288:        String s;
        !          1289: {
        !          1290:        if (*s) printf("Mail forwarding to %s\en",s);
        !          1291:        else    printf("Mail is not forwarded\en");
        !          1292: }
        !          1293: .fi
        !          1294: .bp
        !          1295: .sh 1 "Example II: PrintFile"
        !          1296: .pp
        !          1297: This example is a very simple application of Bulk Data Transfer.
        !          1298: .sh 2 "PrintFile.cr"
        !          1299: .sp 1
        !          1300: .nf
        !          1301: PrintFile: PROGRAM 756 VERSION 1 =
        !          1302: BEGIN
        !          1303: 
        !          1304:     DEPENDS UPON BulkData (0) VERSION 1;
        !          1305: 
        !          1306:     -- Remote errors.
        !          1307: 
        !          1308:     CantPrint: ERROR = 0;
        !          1309: 
        !          1310:     -- Remote entry points.
        !          1311: 
        !          1312:     RPrint: PROCEDURE [ s: BulkData.Source ]
        !          1313:                REPORTS [ CantPrint ];
        !          1314: 
        !          1315: END.
        !          1316: .fi
        !          1317: .sp 1
        !          1318: .sh 2 "A typical client (remoteprint.c)"
        !          1319: .lp
        !          1320: .sp 1
        !          1321: .nf
        !          1322: /*
        !          1323:  * Sample proram to print a file remotely using trivial remote print
        !          1324:  * protocol.
        !          1325:  * Usage:  remoteprint filename
        !          1326:  *
        !          1327:  */
        !          1328: #include <stdio.h>
        !          1329: #include <sys/types.h>
        !          1330: #include <netns/ns.h>          /* for XNS addresses */
        !          1331: #include "PrintFile_defs.h"
        !          1332: 
        !          1333: static FILE * source;  /* communicate from main to SendSource */
        !          1334: 
        !          1335: main(argc, argv)
        !          1336:        int argc;
        !          1337:        char *argv[];
        !          1338: {
        !          1339:        CourierConnection *connection;
        !          1340:        struct ns_addr *destaddr;
        !          1341: 
        !          1342:        if (argc < 3 || (destaddr = getXNSaddr(argv[1])) == NULL)) {
        !          1343:                fprintf(stderr,"Usage:  %s machine file ...\en",argv[0]);
        !          1344:                exit(1);
        !          1345:        }
        !          1346:        if ((connection = CourierOpen(destaddr)) == NULL) {
        !          1347:                fprintf(stderr,"Can't open connection to %s\en",argv[1]);
        !          1348:                exit(1);
        !          1349:        }
        !          1350:        argv++;
        !          1351:        while (argc-- > 2) {
        !          1352:                argv++;
        !          1353:                if (strcmp(argv[0],"-") == 0) source = stdin;
        !          1354:                else source = fopen(argv[0],"r");
        !          1355:                if (source == NULL)
        !          1356:                        fprintf(stderr,"Can't open %s\en",argv[0]);
        !          1357:                else DURING
        !          1358:                        RPrint(connection,SendSource,immediateSource);
        !          1359:                    HANDLER
        !          1360:                        fprintf(stderr,"Call to RPrint failed.\en");
        !          1361:                    END_HANDLER;
        !          1362:                fclose(source);
        !          1363:        }
        !          1364:        CourierClose(connection);
        !          1365: }
        !          1366: 
        !          1367: SendSource(bdtconnection)
        !          1368: CourierConnection *bdtconnection;
        !          1369: {
        !          1370:        int count;
        !          1371:        char buffer[SPPMAXDATA];
        !          1372: 
        !          1373:        while ( (count = fread(buffer,1,SPPMAXDATA,source)) > 0 &&
        !          1374:                BDTwrite(bdtconnection,buffer,count) >= 0 )
        !          1375:                ;
        !          1376:        if (count <= 0)
        !          1377:                BDTclosewrite(bdtconnection);   /* last packet with EOM set */
        !          1378:        else
        !          1379:                BDTabort(bdtconnection);
        !          1380: }
        !          1381: .fi
        !          1382: .sp 2
        !          1383: .sh 2 "Server (PrintFile.c)"
        !          1384: .sp 1
        !          1385: .nf
        !          1386: #include <stdio.h>
        !          1387: #include <sys/types.h>
        !          1388: #include <netxns/ns.h>
        !          1389: #include "PrintFile_defs.h"
        !          1390: 
        !          1391: RPrintResult
        !          1392: Rprint(source,CourierBDTProc,s)
        !          1393:        CourierConnection *source, CourierBDTProc;
        !          1394:        BulkData1_Source s;
        !          1395: {
        !          1396:        FILE *printpipe;
        !          1397:        char sppdata[SPPMAXDATA];
        !          1398: 
        !          1399:        switch (s.designator)
        !          1400:        case active:
        !          1401:        case passive:
        !          1402:                raise(CantPrint);
        !          1403:                /*NOTREACHED*/
        !          1404:        case null:
        !          1405:                system("print /dev/null");      /* print a null file */
        !          1406:                return;
        !          1407:        case immediate:
        !          1408:                if ((printpipe = popen("print","w")) == NULL) {
        !          1409:                        raise(CantPrint);
        !          1410:                        /*NOTREACHED*/
        !          1411:                }
        !          1412:                count = BDTread(source, sppdata, SPPMAXDATA);
        !          1413:                while (count > 0) {     /* actually read data */
        !          1414:                        if (fwrite(sppdata, 1, count, printpipe) == 0) {
        !          1415:                                BDTabort(source);
        !          1416:                                break;
        !          1417:                        }
        !          1418:                        count = BDTread(source, sppdata, SPPMAXDATA);
        !          1419:                }
        !          1420:                if (pclose(printpipe) == 0 && count == 0)
        !          1421:                        return;
        !          1422:                else raise(CantPrint);
        !          1423: }
        !          1424: .fi
        !          1425: .sh 1 "One Final Example"
        !          1426: .pp
        !          1427: Finally, we present a slightly 
        !          1428: more useful example, a program to print an Interpress
        !          1429: file on a Services-8 printer.  It depends on the standard Xerox Printing
        !          1430: specification, program 4 version 3.  For an even better example, look
        !          1431: at the printing client, xnsprint, provided with the XNS Courier distribution
        !          1432: as
        !          1433: .i "./examples/print/xnsprint.c" .
        !          1434: .sp 2
        !          1435: .nf
        !          1436: #include <stdio.h>
        !          1437: #include <sys/types.h>
        !          1438: #include <netns/ns.h>
        !          1439: #include "Printing_defs.h"
        !          1440: #include <except.h>
        !          1441: 
        !          1442: static FILE *ipfile = NULL;
        !          1443: 
        !          1444: SendSource(bdtconnection)
        !          1445: CourierConnection *bdtconnection;
        !          1446: {
        !          1447:        int count;
        !          1448:        char buffer[SPPMAXDATA];
        !          1449: 
        !          1450:        while ( (count = fread(buffer,1,SPPMAXDATA,ipfile)) > 0 &&
        !          1451:                BDTwrite(bdtconnection,buffer,count) >= 0 )
        !          1452:                ;
        !          1453:        if (count <= 0)
        !          1454:                BDTclosewrite(bdtconnection);   /* last packet with EOM set */
        !          1455:        else
        !          1456:                BDTabort(bdtconnection);
        !          1457: }
        !          1458: 
        !          1459: main(argc, argv)
        !          1460:        int argc;
        !          1461:        char *argv[];
        !          1462: {
        !          1463:        PrintResults result;
        !          1464:        struct ns_addr *destaddr;
        !          1465:        CourierConnection *conn;
        !          1466:        extern struct ns_addr *getXNSaddr();
        !          1467:        PrintAttributes attributes;
        !          1468:        PrintOptions options;
        !          1469: 
        !          1470:        /* use Cornell print server, CornellS1 (slander) */
        !          1471:        destaddr = getXNSaddr("2-273#2-852-159-207");
        !          1472:        attributes.length = 0;
        !          1473:        options.length = 0;
        !          1474:        if (argc != 2 || ((ipfile = fopen(argv[1],"r")) == NULL)) {
        !          1475:                fprintf(stderr,"Usage: %s file\en",argv[0]);
        !          1476:                exit(1);
        !          1477:        }
        !          1478:        if ((conn = CourierOpen(destaddr)) == NULL) {
        !          1479:                fprintf(stderr,"Can't open connection to %s\en",xnshost);
        !          1480:                exit(1);
        !          1481:        }
        !          1482: 
        !          1483:        DURING
        !          1484:                result = Print(conn, SendSource, BulkData1_immediateSource,
        !          1485:                                        attributes, options);
        !          1486:        HANDLER {
        !          1487:                switch (Exception.Code) {
        !          1488:                case Busy:
        !          1489:                        fprintf(stderr,"Busy\en");
        !          1490:                        break;
        !          1491:                case ConnectionError:
        !          1492:                        fprintf(stderr,"Connection error, %d\en",
        !          1493:                                CourierErrArgs(ConnectionErrorArgs,problem));
        !          1494:                        break;
        !          1495:                case InsufficientSpoolSpace:
        !          1496:                case SpoolingQueueFull:
        !          1497:                        fprintf(stderr,"Insufficient spool space\en");
        !          1498:                        break;
        !          1499:                case SpoolingDisabled:
        !          1500:                        fprintf(stderr,"Spooling disabled\en");
        !          1501:                        break;
        !          1502:                case MasterTooLarge:
        !          1503:                case TooManyClients:
        !          1504:                case ServiceUnavailable:
        !          1505:                case SystemError:
        !          1506:                case InvalidPrintParameters:
        !          1507:                case MediumUnavailable:
        !          1508:                case TransferError:
        !          1509:                        fprintf(stderr,"Some Printing error, number %d\en",
        !          1510:                                Exception.code-ERROR_OFFSET);
        !          1511:                        break;
        !          1512:                case Undefined:
        !          1513:                        fprintf(stderr,"Undefined error, number %d\en",
        !          1514:                                CourierErrArgs(UndefinedArgs,problem));
        !          1515:                        break;
        !          1516:                case REJECT_ERROR:
        !          1517:                        fprintf(stderr,"REJECT:  type = %d\en",
        !          1518:                                CourierErrArgs(rejectionDetails, designator));
        !          1519:                        break;
        !          1520:                default:
        !          1521:                        fprintf(stderr,"Some random error, code %d\en",
        !          1522:                                Exception.Code);
        !          1523:                        break;
        !          1524:                }
        !          1525:        exit(1);
        !          1526:        } END_HANDLER;
        !          1527: 
        !          1528:        CourierClose(conn);
        !          1529:        printf("Done.  Request ID %x %x %x %x %x\en",
        !          1530:                result.printRequestID[0], result.printRequestID[1],
        !          1531:                result.printRequestID[2], result.printRequestID[3],
        !          1532:                result.printRequestID[4]);
        !          1533: }
        !          1534: .fi
        !          1535: .bp
        !          1536: .sh 1 "Final Notes"
        !          1537: .pp
        !          1538: The issues of authentication and protection are
        !          1539: difficult.
        !          1540: They are only touched upon in this implementation.
        !          1541: Currently, each Courier program must perform any authentication
        !          1542: (presumably using the Authentication protocol)
        !          1543: if this is desired.  A few routines exist to allow cleints and servers
        !          1544: to perform simple Authentication; no support for strong Authentication
        !          1545: has yet been written.
        !          1546: .pp
        !          1547: This implementation is fairly inefficient, especially in
        !          1548: the implementation of servers.  Courier calls require substantial
        !          1549: extra copying of courier arguments and results.  More significantly,
        !          1550: the requirement that each call spawn a unique process is very expensive
        !          1551: in UNIX, and should be reconsidered.
        !          1552: A sequence of RPCs on the same SPP stream that specifies the same program
        !          1553: and version number uses the same process, but in all other cases a new
        !          1554: process must be spawned for each individual RPC.
        !          1555: .pp
        !          1556: This implementation defines a number of reserved identifiers.
        !          1557: .b ">>>should list them here<<<"
        !          1558: .sh 1 "Appendix"
        !          1559: .pp
        !          1560: This appendix contains the grammar for the Courier language.
        !          1561: It is similar to the YACC specification used
        !          1562: by the Courier compiler.
        !          1563: .sp
        !          1564: .ps -2p
        !          1565: .vs -2p
        !          1566: .nf
        !          1567: .TS
        !          1568: l l l l l.
        !          1569: %token identifier      number  string  
        !          1570:        ARRAY   BEGIN   BOOLEAN CARDINAL
        !          1571:        CHOICE  DEPENDS END     ERROR
        !          1572:        FALSE   INTEGER LONG    OF
        !          1573:        PROCEDURE       PROGRAM RECORD  REPORTS
        !          1574:        RETURNS SEQUENCE        STRING  TRUE
        !          1575:        TYPE    UNSPECIFIED     UPON    VERSION
        !          1576: .TE
        !          1577: %%
        !          1578: 
        !          1579: Program :
        !          1580:                identifier ':' PROGRAM number VERSION number '='
        !          1581:                BEGIN DependencyList DeclarationList END '.'
        !          1582:        |
        !          1583:                identifier ':' PROGRAM '='
        !          1584:                BEGIN DependencyList DeclarationList END '.'
        !          1585:        ;
        !          1586: 
        !          1587: DependencyList :
        !          1588:                /* empty */
        !          1589:        |       DEPENDS UPON ReferencedProgramList ';'
        !          1590:        ;
        !          1591: 
        !          1592: ReferencedProgramList :
        !          1593:                ReferencedProgram
        !          1594:        |       ReferencedProgramList ',' ReferencedProgram
        !          1595:        ;
        !          1596: 
        !          1597: ReferencedProgram :
        !          1598:                identifier '(' number ')' VERSION number
        !          1599:        ;
        !          1600: 
        !          1601: DeclarationList :
        !          1602:                /* empty */
        !          1603:        |       DeclarationList Declaration
        !          1604:        ;
        !          1605: 
        !          1606: Declaration :
        !          1607:                identifier ':' TYPE '=' Type ';'
        !          1608:        |       identifier ':' Type '=' Constant ';'
        !          1609:        ;
        !          1610: 
        !          1611: Type :
        !          1612:                PredefinedType
        !          1613:        |       ConstructedType
        !          1614:        |       ReferencedType
        !          1615:        ;
        !          1616: 
        !          1617: PredefinedType :
        !          1618:                BOOLEAN
        !          1619:        |       CARDINAL
        !          1620:        |       LONG CARDINAL
        !          1621:        |       INTEGER
        !          1622:        |       LONG INTEGER
        !          1623:        |       STRING
        !          1624:        |       UNSPECIFIED
        !          1625:        |       LONG UNSPECIFIED
        !          1626:        ;
        !          1627: 
        !          1628: ConstructedType :
        !          1629:                '{' CorrespondenceList '}'
        !          1630:        |       ARRAY NumericValue OF Type
        !          1631:        |       SEQUENCE MaximumNumber OF Type
        !          1632:        |       RECORD '[' FieldList ']'
        !          1633:        |       RECORD '[' ']'
        !          1634:        |       CHOICE DesignatorType OF '{' CandidateList '}'
        !          1635:        |       PROCEDURE ArgumentList ResultList ErrorList
        !          1636:        |       ERROR ArgumentList
        !          1637:        ;
        !          1638: 
        !          1639: ReferencedType :
        !          1640:                identifier
        !          1641:        |       identifier '.' identifier
        !          1642:        ;
        !          1643: 
        !          1644: CorrespondenceList :
        !          1645:                Correspondence
        !          1646:        |       CorrespondenceList ',' Correspondence
        !          1647:        ;
        !          1648: 
        !          1649: Correspondence :
        !          1650:                identifier '(' NumericValue ')'
        !          1651:        ;
        !          1652: 
        !          1653: MaximumNumber :
        !          1654:                NumericValue
        !          1655:        |       /* empty */
        !          1656:        ;
        !          1657: 
        !          1658: NumericValue :
        !          1659:                number
        !          1660:        |       ReferencedConstant
        !          1661:        ;
        !          1662: 
        !          1663: DesignatorType :
        !          1664:                /* empty */
        !          1665:        |       ReferencedType
        !          1666:        ;
        !          1667: 
        !          1668: CandidateList :
        !          1669:                Candidate
        !          1670:        |       CandidateList ',' Candidate
        !          1671:        ;
        !          1672: 
        !          1673: Candidate :
        !          1674:                DesignatorList '=''>' Type
        !          1675:        ;
        !          1676: 
        !          1677: DesignatorList :
        !          1678:                Designator
        !          1679:        |       DesignatorList ',' Designator
        !          1680:        ;
        !          1681: 
        !          1682: Designator :
        !          1683:                identifier
        !          1684:        |       Correspondence
        !          1685:        ;
        !          1686: 
        !          1687: ArgumentList :
        !          1688:                /* empty */
        !          1689:        |       '[' FieldList ']'
        !          1690:        ;
        !          1691: 
        !          1692: ResultList :
        !          1693:                /* empty */
        !          1694:        |       RETURNS '[' FieldList ']'
        !          1695:        ;
        !          1696: 
        !          1697: ErrorList :
        !          1698:                /* empty */
        !          1699:        |       REPORTS '[' NameList ']'
        !          1700:        ;
        !          1701: 
        !          1702: FieldList :
        !          1703:                Field
        !          1704:        |       FieldList ',' Field
        !          1705:        ;
        !          1706: 
        !          1707: Field :
        !          1708:                NameList ':' Type
        !          1709:        ;
        !          1710: 
        !          1711: Constant :
        !          1712:                PredefinedConstant
        !          1713:        |       ConstructedConstant
        !          1714:        |       ReferencedConstant
        !          1715:        ;
        !          1716: 
        !          1717: PredefinedConstant :
        !          1718:                TRUE
        !          1719:        |       FALSE
        !          1720:        |       number
        !          1721:        |       '-' number
        !          1722:        |       '"' string '"'
        !          1723:        ;
        !          1724: 
        !          1725: ConstructedConstant :
        !          1726:                identifier
        !          1727:        |       '[' ElementList ']'
        !          1728:        |       '[' ComponentList ']'
        !          1729:        |       '['']'
        !          1730:        |       identifier Constant
        !          1731:        |       number
        !          1732:        ;
        !          1733: 
        !          1734: ReferencedConstant :
        !          1735:                identifier
        !          1736:        |       identifier '.' identifier
        !          1737:        ;
        !          1738: 
        !          1739: ElementList :
        !          1740:                Constant
        !          1741:        |       ElementList ',' Constant
        !          1742:        ;
        !          1743: 
        !          1744: ComponentList :
        !          1745:                Component
        !          1746:        |       ComponentList ',' Component
        !          1747:        ;
        !          1748: 
        !          1749: Component :
        !          1750:                NameList ':' Constant
        !          1751:        ;
        !          1752: 
        !          1753: NameList :
        !          1754:                identifier
        !          1755:        |       NameList ',' identifier
        !          1756:        ;
        !          1757: .fi
        !          1758: .vs
        !          1759: .ps

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.