hp.com home products and services support and drivers solutions how to buy
cd-rom home
End of Jump to page title
HP OpenVMS systems
documentation

Jump to content


HP OpenVMS Programming Concepts Manual

HP OpenVMS Programming Concepts Manual


Previous Contents Index

33.6.2 Example Using Dialogue Mode (Pascal)

This more complex example can respond to an arbitrary itemset provided in the ACM communications buffer by successive calls to the SYS$ACM[W] system service. In particular, the item list allocated to respond to a given itemset is automatically made large enough to respond to each possible itemset entry if it happens to be an input itemset entry. This differs from the programming tactic used in Section 33.5.3 because variable sizing of automatic (stack) variables is available in Pascal but not in BLISS.

This theoretical example shows support for a fingerprint reader. It is written to demonstrate programming techniques, rather than to correspond to a particular hardware product.
Line Activity Special Notes
22 Function AUTHENTICATE Called by one line at the very end.
180 Function RESPOND Provide input requested by SYS$ACM[W].
239 Function RECURSE_OVER_ITEMS Mandatory specification of attributes. Handle one possible input and many possible output entries.
276 Procedure WRITE_ITEM_PLAIN Write to the terminal.
298 Procedure SET_BUFFER Use input code rather than reading terminal.
321 Fail on non-text other than FINGERPRINT_READIT No ACME should request any other non-text.
358 Read a fingerprint Use the hardware.
438 Synthesize principal name Call SET_BUFFER with the proper string.
496 Prompt the user for other text If any ACME agent requests prompt, it may do so; other ACME agents may request additional information.
587 Fill in the item list Store input text.
608 Process output item set entries Send output text to the terminal.
750 Call SYS$ACM with response When recursion is done, send it.
761 Make the initial call to RECURSE_OVER_ITEMS Initialize for this iteration and start recursion.
775 Learn the ACME_ID of the fingerprint ACME ACME-specific item codes are specific to an ACME.
805 Make an initial SYS$ACMW call Start with invariant information.
828 Loop calling RESPOND So long as the status is ACME$_OPINCOMPL.
863 Close channels Clean-up of open channels.
885 Return status to caller Failures exited earlier.


     2          'sys$Library:PASCAL$LIB_ROUTINES')] 
     3  PROGRAM ACM_SHOPFLOOR(OUTPUT); 
     4      {                                                             } 
     5      { AUTHENTICATE - major subroutine of this module              } 
     6      {                                                             } 
     7      { This function is called with a USER_INDEX, indicating which } 
     8      { of 10 buttons on the shop floor kiosk was pushed, and thus  } 
     9      { which of ten employees is to be authenticated.              } 
    10      {                                                             } 
    11      TYPE PRINCIPAL_INDEX_TYPE = ( 
    12               PRINCIPAL_1, 
    13               PRINCIPAL_2, 
    14               PRINCIPAL_3, 
    15               PRINCIPAL_4, 
    16               PRINCIPAL_5, 
    17               PRINCIPAL_6, 
    18               PRINCIPAL_7, 
    19               PRINCIPAL_8, 
    20               PRINCIPAL_9, 
    21               PRINCIPAL_10 ); 
    22      {                                                             } 
    23      { This subroutine translates each of the 10 possible index    } 
    24      { values into one of ten generic principal names.  To avoid   } 
    25      { changes to this client program, those principal names are   } 
    26      { mapped into the principal names actually corresponding to   } 
    27      { individual names within the ACME Server, so that a single   } 
    28      { data file can be modified by a designated administrator     } 
    29      { without changing the client software.                       } 
    30      {                                                             } 
    31      {                                                             } 
    32      { After the Principal Name has been determined, the user must } 
    33      { be authenticated.  At some kiosks there is a fingerprint    } 
    34      { reader that will be used for authentication, while at the   } 
    35      { spray painting station a keyboard is always used because    } 
    36      { employees are wearing rubber gloves.  For some sensitive    } 
    37      { combinations of Principal Name and kiosk, a fingerprint     } 
    38      { and passwords might both be required.  These variations,    } 
    39      { however, are determined by ACMEs within the ACME Server,    } 
    40      { and this client code merely authenticates using whatever    } 
    41      { method might be specified in the Context Area returned by   } 
    42      { successive SYS$ACM calls.                                   } 
    43      {                                                             } 
    44      CONST 
    45          FINGERPRINT_READIT = 32770; { from the Fingerprint ACME } 
    46      {                                                             } 
    47      { After authentication it is also possible that password      } 
    48      { expirations may need to be handled, in which case even in   } 
    49      { situations where a fingerprint would normally be sufficient,} 
    50      { the user will actually have to engage in typing.  Whether   } 
    51      { users who normally authenticate with a fingerprint even     } 
    52      { have a password is an administrative issue enforced by      } 
    53      { configuration of the ACMEs.  As in the authentication step, } 
    54      { this client software just implements whatever mechanism is  } 
    55      { specified in the Context Area returned by successive        } 
    56      { SYS$ACM calls.                                              } 
    57      {                                                             } 
    58      FUNCTION AUTHENTICATE ( PRINCIPAL_INDEX : PRINCIPAL_INDEX_TYPE ):BOOLEAN; 
    59          TYPE 
    60              ACMECB_PTR = ^ACMECB$TYPE; 
    61              CHANNEL_TYPE = [WORD] 0..65535; 
    62          VAR 
    63              FINGERPRINT_READER_CHANNEL : CHANNEL_TYPE VALUE 0; 
    64              TERMINAL_CHANNEL : CHANNEL_TYPE VALUE 0; 
    65              MY_LOGON_TYPE : INTEGER VALUE ACME$K_LOCAL; 
    66              MY_DIALOGUE_SUPPORT : INTEGER 
    67                  VALUE ACMEDLOGFLG$M_INPUT + ACMEDLOGFLG$M_NOECHO; 
    68              {                                                         } 
    69              { We rely on an initial query to determine the ACME ID    } 
    70              { of the Fingerprint ACME in the current running system.  } 
    71              { We use that ACME ID to compare against ACMECB$L_ACME_ID } 
    72              { in the ACME Communications Buffer to determine whether  } 
    73              { an ACME-specific input item set is one created by the   } 
    74              { Fingerprint ACME, because ACME-specific item codes must } 
    75              { qualified by the originating ACME.                      } 
    76              {                                                         } 
    77              { Field ACMECB$L_ACME_ID.ACMEID$V_ACME_NUM will be the    } 
    78              { actual basis of comparison, because it is sufficient to } 
    79              { identify a particular ACME and the other fields within  } 
    80              { an ACME ID might change between when our query call     } 
    81              { completes and when we make our authenticate call.       } 
    82              {                                                         } 
    83              { We make our query against the reserved ID value of 0,   } 
    84              { to gather information about the ACME Agents. This query } 
    85              { is actually handled by the SYS$ACMW system service.     } 
    86              {                                                         } 
    87              { Data elements for the query for ACME ID                 } 
    88              {                                                         } 
    89              { Addresses of these elements will be set into item list  } 
    90              { ACM_QUERY_ITMLST by procedural code below.              } 
    91              {                                                         } 
    92              SYS$ACM_ACME_ID : INTEGER VALUE 0; 
    93              ACME_QUERY_ACME_NAME : INTEGER VALUE ACME$K_QUERY_ACME_NAME; 
    94              FINGERPRINT_ACME_NAME : STRING(16) VALUE 'FINGERPRINT_ACME'; 
    95              ACME_TARGET_DOI_ID : INTEGER VALUE ACME$K_QUERY_ACME_ID; 
    96              FINGERPRINT_ACME_ID : ACMEID$TYPE; 
    97              {                                                         } 
    98              { Item list for the Query                                 } 
    99              {                                                         } 
   100              ACM_QUERY_ITMLST : ARRAY[0..5] OF ILE3$TYPE 
   101                      VALUE [     0:[ILE3$W_LENGTH:4; 
   102                                      ILE3$W_CODE:ACME$_TARGET_DOI_ID; 
   103                                      ILE3$PS_BUFADDR:0; 
   104                                      ILE3$PS_RETLEN_ADDR:NIL]; 
   105                              1:[ILE3$W_LENGTH:4; 
   106                                      ILE3$W_CODE:ACME$_QUERY_KEY_TYPE; 
   107                                      ILE3$PS_BUFADDR:0; 
   108                                      ILE3$PS_RETLEN_ADDR:NIL]; 
   109                              2:[ILE3$W_LENGTH:16; 
   110                                      ILE3$W_CODE:ACME$_QUERY_KEY_VALUE; 
   111                                      ILE3$PS_BUFADDR:0; 
   112                                      ILE3$PS_RETLEN_ADDR:NIL]; 
   113                              3:[ILE3$W_LENGTH:4; 
   114                                      ILE3$W_CODE:ACME$_QUERY_TYPE; 
   115                                      ILE3$PS_BUFADDR:0; 
   116                                      ILE3$PS_RETLEN_ADDR:NIL]; 
   117                              4:[ILE3$W_LENGTH:4; 
   118                                      ILE3$W_CODE:ACME$_QUERY_DATA; 
   119                                      ILE3$PS_BUFADDR:0; 
   120                                      ILE3$PS_RETLEN_ADDR:NIL]; 
   121                              5:[ILE3$W_LENGTH:0; 
   122                                      ILE3$W_CODE:0; 
   123                                      ILE3$PS_BUFADDR:0; 
   124                                      ILE3$PS_RETLEN_ADDR:NIL]]; 
   125              {                                                         } 
   126              { Item list for initial Authentication call               } 
   127              {                                                         } 
   128              MY_ACM_ITMLST_A : ARRAY[0..2] OF ILE3$TYPE 
   129                      VALUE [     0:[ILE3$W_LENGTH:4; 
   130                                      ILE3$W_CODE:ACME$_LOGON_TYPE; 
   131                                      ILE3$PS_BUFADDR:0; 
   132                                      ILE3$PS_RETLEN_ADDR:NIL]; 
   133                              1:[ILE3$W_LENGTH:4; 
   134                                      ILE3$W_CODE:ACME$_DIALOGUE_SUPPORT; 
   135                                      ILE3$PS_BUFADDR:0; 
   136                                      ILE3$PS_RETLEN_ADDR:NIL]; 
   137                              2:[ILE3$W_LENGTH:0; 
   138                                      ILE3$W_CODE:0; 
   139                                      ILE3$PS_BUFADDR:0; 
   140                                      ILE3$PS_RETLEN_ADDR:NIL]]; 
   141  {                                                         } 
   142  { Variables used both inside and outside Function RESPOND } 
   143  {                                                         } 
   144   MY_ACMESB : ACMESB$TYPE; 
   145   MY_CONTXT : ACMECB_PTR; 
   146   MY_STATUS : UNSIGNED; 
   147   TRASH_STATUS : UNSIGNED; 
   148 {                                             } 
   149 { The ITEMSET array we will read              } 
   150 {                                             } 
   151 TYPE 
   152    {                                                   } 
   153    { A string longer than we will ever see, defined to } 
   154    { avoid exceeding Pascal's 2**16-1 limit on string  } 
   155    { length.                                           } 
   156    {                                                   } 
   157     CHAR_ARRAY_TYPE = PACKED ARRAY [1..65535] 
   158        OF CHAR; 
   159     CHAR_ARRAY_TYPE_POINTER = ^CHAR_ARRAY_TYPE; 
   160    {                                                   } 
   161    { An array longer than we will ever see, defined to } 
   162    { avoid:                                            } 
   163    {                                                   } 
   164    { "%PASCAL-E-SIZGTRMAX, Size exceeds MAXINT bits".  } 
   165    {                                                   } 
   166    ITEMSET_ARRAY_TYPE = 
   167        PACKED ARRAY [1..MAXINT DIV (ACMEIS$K_LENGTH*8)] 
   168            OF ACMEITMSET$TYPE; 
   169    ITEMSET_ARRAY_TYPE_POINTER = ^ITEMSET_ARRAY_TYPE; 
   170 VAR 
   171    ITEMSET_ARRAY : ITEMSET_ARRAY_TYPE_POINTER; 
   172    {                                                 } 
   173    { A special declaration is required in order to   } 
   174    { Synchronize on an ACM Status Block              } 
   175    {                                                 } 
   176    [ASYNCHRONOUS,EXTERNAL(SYS$SYNCH)] FUNCTION $SYNCH_ACMESB ( 
   177        %IMMED EFN : UNSIGNED := %IMMED 0; 
   178        VAR IOSB : [VOLATILE] ACMESB$TYPE := %IMMED 0) 
   179        : INTEGER; EXTERNAL; 
   180    {                                                 } 
   181    { Function to fill in responses to input itemsets } 
   182    {                                                 } 
   183    { Input itemsets will require buffer space, and   } 
   184    { although each input itemset will use no more    } 
   185    { than 65535 bytes, the number of input itemsets  } 
   186    { provided in a single dialogue step is not       } 
   187    { bounded.                                        } 
   188    {                                                 } 
   189    { Therefore we invoke this function recursively   } 
   190    { each time we encounter an input itemset,        } 
   191    { making use of a conformant parameter to         } 
   192    { allocate the appropriate length buffer.  When   } 
   193    { all itemsets have been processed, we make our   } 
   194    { continuation call to $ACM from the deepest      } 
   195    { level of recursion (when all buffers are still  } 
   196    { intact), and then return from function RESPOND  } 
   197    { entirely to wait for completion of the call.    } 
   198    {                                                 } 
   199    { This recursive approach using stack-based       } 
   200    { buffers is fine for operation on the expandable } 
   201    { main VMS user-mode stack, but an application    } 
   202    { operating on non-expandable stacks, such as     } 
   203    { non-initial stack from VAX Ada or DECthreads,   } 
   204    { should obviously use iteration and heap-based   } 
   205    { explicit allocation instead.                    } 
   206    {                                                 } 
   207    FUNCTION RESPOND ( ITEMSET_COUNT : INTEGER ):INTEGER; 
   208        {                                             } 
   209        { The Item List we will write for use on the  } 
   210        { next call to SYS$ACM will never have more   } 
   211        { entries than the Itemset List we received   } 
   212        { in the ACM Communications Buffer from the   } 
   213        { previous call to SYS$ACM, so we choose that } 
   214        { maximum size for our item list.             } 
   215        {                                             } 
   216        TYPE 
   217           ITEM_LIST_TEMPLATE ( UPPER_BOUND : INTEGER ) 
   218           = ARRAY [1..UPPER_BOUND] OF ILE3$TYPE; 
   219        VAR 
   220            ITEM_LIST : ITEM_LIST_TEMPLATE ( ITEMSET_COUNT + 1 ); 
   221            EACH_ITEM : INTEGER VALUE 1; 
   222        {                                             } 
   223        { Each invocation of RECURSE_OVER_ITEMS will  } 
   224        { allocate an automatic (stack-based) buffer. } 
   225        {                                             } 
   226        TYPE 
   227           INPUT_BUFFER_TEMPLATE ( MAX_SIZE : INTEGER ) 
   228           = PACKED ARRAY [1..MAX_SIZE] OF CHAR; 
   229        {                                             } 
   230        { Variables for parsing the Itemset List      } 
   231        {                                             } 
   232        VAR 
   233           CHAR_ARRAY_LENGTH_1 : INTEGER; 
   234           CHAR_ARRAY_POINTER_1 : CHAR_ARRAY_TYPE_POINTER; 
   235           CHAR_ARRAY_LENGTH_2 : INTEGER; 
   236           CHAR_ARRAY_POINTER_2 : CHAR_ARRAY_TYPE_POINTER; 
   237           EACH_ITEMSET : INTEGER VALUE 1; 
   238           INPUT_IOSB, CONFIRM_IOSB : IOSB$TYPE; 
   239        {                                             } 
   240        { RECURSE_OVER_ITEMS                          } 
   241        {                                             } 
   242        { This function gets called:                  } 
   243        {                                             } 
   244        {     1. Once with a parameter of zero at the } 
   245        {        start of processing an Itemset List. } 
   246        {                                             } 
   247        {     2. Recursively as each input itemset is } 
   248        {        encountered in the Itemset List.     } 
   249        {                                             } 
   250        { Multiple output itemsets are processed at a } 
   251        { single recursion level until the end of the } 
   252        { Itemset List or until an input itemset      } 
   253        { is found.                                   } 
   254        FUNCTION RECURSE_OVER_ITEMS ( MAX_SIZE : INTEGER ):INTEGER; 
   255            {                                               } 
   256            { The buffer we will use for this input item    } 
   257            {                                               } 
   258            { The INPUT_BUFFER lifetime needs only be for   } 
   259            { the lifetime of RECURSE_OVER_ITEMS because it } 
   260            { is filled by SYS$QIOW at this recursion       } 
   261            { level and provided as input to SYS$ACM at     } 
   262            { the innermost recursion level.                } 
   263            {                                               } 
   264            VAR 
   265               {                                                 } 
   266               { We use MAX_SIZE+1 to avoid the error:           } 
   267               {                                                 } 
   268               { %PAS-F-LOWGTRHIGH, low-bound exceeds high-bound } 
   269               {                                                 } 
   270               { when MAX_SIZE is 0.                             } 
   271               {                                                 } 
   272               INPUT_BUFFER : INPUT_BUFFER_TEMPLATE ( MAX_SIZE+1 ); 
   273               CONFIRM_BUFFER : INPUT_BUFFER_TEMPLATE ( MAX_SIZE+1 ); 
   274              QIO_FUNC : INTEGER; 
   275            {                                             } 
   276             PROCEDURE WRITE_ITEM_PLAIN; 
   277             BEGIN   {  WRITE_ITEM_PLAIN } 
   278                 IF CHAR_ARRAY_POINTER_1 <> NIL 
   279                 THEN 
   280                    IF CHAR_ARRAY_LENGTH_1 = 0 
   281                    THEN 
   282                       WRITELN 
   283                     ELSE 
   284                       WRITELN ( 
   285                           CHAR_ARRAY_POINTER_1^[1.. 
   286                              CHAR_ARRAY_LENGTH_1] ); 
   287                  IF CHAR_ARRAY_POINTER_2 <> NIL 
   288                  THEN 
   289                     IF CHAR_ARRAY_LENGTH_2 = 0 
   290                     THEN 
   291                         WRITELN 
   292                     ELSE 
   293                         WRITELN ( 
   294                             CHAR_ARRAY_POINTER_2^[1.. 
   295                                CHAR_ARRAY_LENGTH_2] ); 
   296             END;    {  WRITE_ITEM_PLAIN } 
   297             {                                             } 
   298             PROCEDURE SET_BUFFER ( 
   299                    PRINCIPAL_NAME : STRING ); 
   300                 BEGIN   { PROCEDURE SET_BUFFER } 
   301                 INPUT_IOSB.IOSB$W_BCNT := 
   302                     MIN ( SIZE ( PRINCIPAL_NAME ), 
   303                           SIZE ( INPUT_BUFFER ) ); 
   304                 {                                         } 
   305                 { The following line will produce a       } 
   306                 { Pascal run-time error if SYS$ACM does   } 
   307                 { not specify input lengths of at least   } 
   308                 { 12 characters.                          } 
   309                 {                                         } 
   310                  READV ( PRINCIPAL_NAME, INPUT_BUFFER ); 
   311                 {                                         } 
   312                  END;    { PROCEDURE SET_BUFFER } 
   313              {                                             } 
   314               BEGIN       { FUNCTION RECURSE_OVER_ITEMS } 
   315                    {                                         } 
   316                    { Process any initial Input Itemset       } 
   317                    {                                         } 
   318                    IF MAX_SIZE <> 0 
   319                    THEN 
   320                        BEGIN   { process Input Itemset } 
   321                  {                                            } 
   322                  { First we consider non-text ACME-specific   } 
   323                  { item codes, and the only one of those we   } 
   324                  { are prepared to handle is the Fingerprint  } 
   325                  { ACME code FINGERPRINT_READIT.              } 
   326                  {                                            } 
   327                        IF ITEMSET_ARRAY^[EACH_ITEMSET] 
   328                           .ACMEIS$W_ITEM_CODE.ACMEIC$V_ACME_SPECIFIC 
   329                        AND NOT ITEMSET_ARRAY^[EACH_ITEMSET] 
   330                            .ACMEIS$W_ITEM_CODE.ACMEIC$V_UCS 
   331                        THEN 
   332                            BEGIN   { ACME-specific non-text input } 
   333                   {                                        } 
   334                   { Comparing MY_CONTXT^.ACMECB$L_ACME_ID  } 
   335                   { .ACMEID$V_ACME_NUM field against the   } 
   336                   { (previously queried) IDs of ACMEs from } 
   337                   { which this client expects ACME-specific} 
   338                   { input itemsets and also comparing   
   339                   {                                        } 
   340                   { ITEMSET_ARRAY^[EACH_ITEMSET]           } 
   341                   {  .ACMEIS$W_ITEM_CODE.ACMEIC$W_ITEM_CODE} 
   342                   { against the 16-bit values of expected  } 
   343                   { ACME-specific item codes, we get the   } 
   344                   { information to dispatch to handle each } 
   345                   { of the ACME-specific message types that} 
   346                   { this client program knows about.       } 
   347                   {                                        } 
   348                   { In our case, it is only the Fingerprint} 
   349                   { ACME and only code FINGERPRINT_READIT. } 
   350                   {                                        } 
   351                   ASSERT((MY_CONTXT^.ACMECB$L_ACME_ID.ACMEID$V_ACME_NUM 
   352                              = FINGERPRINT_ACME_ID.ACMEID$V_ACME_NUM) 
   353                           AND (ITEMSET_ARRAY^[EACH_ITEMSET] 
   354                                    .ACMEIS$W_ITEM_CODE 
   355                                    .ACMEIC$W_ITEM_CODE 
   356                               = FINGERPRINT_READIT ), 
   357                           'unknown ACME-specific item code'); 
   358                   {                                        } 
   359                   { Exchange Fingerprint Data              } 
   360                   {                                        } 
   361                   { This client contains little knowledge  } 
   362                   { regarding the workings of the          } 
   363                   { Fingerprint Reader.  It knows to call  } 
   364                   { SYS$QIOW using the function code       } 
   365                   { IO$_READPROMPT providing the output    } 
   366                   { "prompt" data and accepting whatever   } 
   367                   { the device provides.  Buffer sizes     } 
   368                   { (within the 65535 limit) and the number} 
   369                   { of exchanges to read a fingerprint     } 
   370                   { are governed by the Fingerprint ACME,  } 
   371                   { which has knowledge of the device      } 
   372                   { characteristics.                       } 
   373                   {                                        } 
   374                   { Perhaps the channel is open from a     } 
   375                   { previous dialogue or recursion step.   } 
   376                   {                                        } 
   377                   IF FINGERPRINT_READER_CHANNEL = 0 
   378                   THEN 
   379                      BEGIN   { a channel must be assigned } 
   380                      MY_STATUS := 
   381                          $ASSIGN ( 
   382                              DEVNAM := 'FPA0:', 
   383                              CHAN := FINGERPRINT_READER_CHANNEL ); 
   384                   {                                    } 
   385                   { If there is no Fingerprint Reader  } 
   386                   { on this machine, the Fingerprint   }  
   387                   { ACME should have figured that out  } 
   388                   { and not requested Fingerprint      } 
   389                   { Reader data.                       } 
   390                   {                                    } 
   391                   IF NOT ODD(MY_STATUS) 
   392                   then 
   393                       RETURN MY_STATUS; 
   394                   END; { A channel must be assigned.} 
   395              {                                        } 
   396              { Exchange Fingerprint data              } 
   397              {                                        } 
   398              MY_STATUS := 
   399                  $QIOW ( 
   400                      EFN := EFN$C_ENF, 
   401                      CHAN := FINGERPRINT_READER_CHANNEL, 
   402                      FUNC := IO$_READPROMPT, 
   403                      IOSB := INPUT_IOSB, 
   404                      P1 := INPUT_BUFFER, 
   405                      P2 := SIZE(INPUT_BUFFER), 
   406                      P5 := IADDRESS(CHAR_ARRAY_POINTER_1^), 
   407                      P6 := CHAR_ARRAY_LENGTH_1 ); 
   408               IF ODD(MY_STATUS) 
   409               THEN 
   410                   MY_STATUS := INPUT_IOSB.IOSB$W_STATUS; 
   411               IF NOT ODD(MY_STATUS) 
   412               THEN 
   413                   RETURN MY_STATUS; 
   414               {                                        } 
   415               END     { ACME-specific non-text input } 
   416           ELSE 
   417               BEGIN   { general or text input itemset } 
   418               {                                       } 
   419               { Pascal does not give us the ability    } 
   420               { that more strongly typed languages do  } 
   421               { to force a compile-time failure in the } 
   422               { case where new message types have been } 
   423               { added to a subsequent release of VMS,  } 
   424               { so we make these run-time checks.      } 
   425               {                                        } 
   426               ASSERT(ACMEMC$K_MIN_GEN_MSG 
   427                         = ACMEMC$K_GENERAL, 
   428                       'ACMEMC$K_MIN_GEN_MSG has shifted'); 
   429               ASSERT(ACMEMC$K_MAX_GEN_MSG 
   430                          = ACMEMC$K_DIALOGUE_ALERT, 
   431                       'ACMEMC$K_MAX_GEN_MSG has shifted'); 
   432               ASSERT(ACMEMC$K_MIN_LOGON_MSG 
   433                          = ACMEMC$K_SYSTEM_IDENTIFICATION, 
   434                       'ACMEMC$K_MIN_LOGON_MSG has shifted'); 
   435               ASSERT(ACMEMC$K_MAX_LOGON_MSG 
   436                          = ACMEMC$K_MAIL_NOTICES, 
   437                       'ACMEMC$K_MAX_LOGON_MSG has shifted'); 
   438               {                                        } 
   439               { The only general item codes we know of } 
   440               { for input itemsets are those that are  } 
   441               { "well known items", and those all      } 
   442               { carry text.  To be flexible for any    } 
   443               { possible future additions, however,    } 
   444               { we choose to handle any text input     } 
   445               { item code, and we can detect those     } 
   446               { by looking at bit ACMEIC$V_UCS in      } 
   447               { the item code.  That bit is simply a   } 
   448               { predefined characteristic of the item  } 
   449               { code and is quite independent of       } 
   450               { whether or not a particular caller     } 
   451               { of SYS$ACM might set the ACME$V_UCS2_4 } 
   452               { function modifier to indicate strings  } 
   453               { are provided in UCS format.            } 
   454               {                                        } 
   455               IF ITEMSET_ARRAY^[EACH_ITEMSET] 
   456                   .ACMEIS$W_ITEM_CODE.ACMEIC$V_UCS 
   457               THEN 
   458                   IF ITEMSET_ARRAY^[EACH_ITEMSET] 
   459                       .ACMEIS$W_ITEM_CODE.ACMEIC$W_ITEM_CODE 
   460                           = ACME$_PRINCIPAL_NAME_IN 
   461                   THEN 
   462                       BEGIN   { ACME$_PRINCIPAL_NAME_IN } 
   463                       {                                } 
   464                       { Choose a canned value.         } 
   465                       {                                } 
   466                       CASE PRINCIPAL_INDEX OF 
   467                       PRINCIPAL_1: 
   468                            SET_BUFFER ( 'KIOSKUSER_1' ); 
   469                       PRINCIPAL_2: 
   470                            SET_BUFFER ( 'KIOSKUSER_2' ); 
   471                       PRINCIPAL_3: 
   472                            SET_BUFFER ( 'KIOSKUSER_3' ); 
   473                       PRINCIPAL_4: 
   474                            SET_BUFFER ( 'KIOSKUSER_4' ); 
   475                       PRINCIPAL_5: 
   476                            SET_BUFFER ( 'KIOSKUSER_5' ); 
   477                       PRINCIPAL_6: 
   478                            SET_BUFFER ( 'KIOSKUSER_6' ); 
   479                       PRINCIPAL_7: 
   480                            SET_BUFFER ( 'KIOSKUSER_7' ); 
   481                       PRINCIPAL_8: 
   482                            SET_BUFFER ( 'KIOSKUSER_8' ); 
   483                       PRINCIPAL_9: 
   484                            SET_BUFFER ( 'KIOSKUSER_9' ); 
   485                       PRINCIPAL_10: 
   486                            SET_BUFFER ( 'KIOSKUSER_10' ); 
   487                       OTHERWISE 
   488                       {                                } 
   489                       { There is a bug in this program.} 
   490                       {                                } 
   491                             RETURN SS$_BUGCHECK; 
   492                       {                               } 
   493                       END;    { CASE PRINCIPAL_INDEX } 
   494                       END     { ACME$_PRINCIPAL_NAME_IN } 
   495                    ELSE    
   496                       BEGIN   { Item Code is for text } 
   497                       {                                } 
   498                       { Perhaps the channel is open    } 
   499                       { from a previous dialogue step. } 
   500                       {                                } 
   501                       IF TERMINAL_CHANNEL = 0 
   502                       THEN 
   503                           BEGIN   { a channel must be assigned } 
   504                           MY_STATUS := 
   505                              $ASSIGN ( 
   506                                  DEVNAM := 'SYS$INPUT', 
   507                                  CHAN := TERMINAL_CHANNEL ); 
   508                           IF NOT ODD(MY_STATUS) 
   509                           then 
   510                                   LIB$SIGNAL(MY_STATUS); 
   511                           END;    { a channel must be assigned } 
   512                        {                              } 
   513                        {We honor SYS$ACM specification of   } 
   514                        {Noecho, but because this client     } 
   515                        { software only has to work with     } 
   516                        { a limited number of hardware       } 
   517                        { configurations, we do not bother   } 
   518                        { to support Local Echo terminals    } 
   519                        { by masking Noecho values the way   } 
   520                        { LOGINOUT does.  If we chose to     } 
   521                        { do that, we could support longer   } 
   522                        { input strings than the limit       } 
   523                        { LOGINOUT imposes because LOGINOUT  } 
   524                        { must fit the prompt and the        } 
   525                        {masking into a 255-character        } 
   526                        { maximum length imposed by RMS,     } 
   527                        { whereas we are using QIO directly. } 
   528                        {                                    } 
   529                        IF ITEMSET_ARRAY^[EACH_ITEMSET] 
   530                           .ACMEIS$L_FLAGS.ACMEDLOGFLG$V_NOECHO 
   531                        THEN 
   532                            QIO_FUNC := IO$_READPROMPT 
   533                                      + IO$M_NOECHO 
   534                        ELSE 
   535                            QIO_FUNC := IO$_READPROMPT; 
   536                        MY_STATUS := 
   537                            $QIOW ( 
   538                                EFN := EFN$C_ENF, 
   539                                CHAN := TERMINAL_CHANNEL, 
   540                                FUNC := QIO_FUNC, 
   541                                IOSB := INPUT_IOSB, 
   542                                P1 := INPUT_BUFFER, 
   543                                P2 := SIZE(INPUT_BUFFER), 
   544                                P5 := IADDRESS(CHAR_ARRAY_POINTER_1^), 
   545                                P6 := CHAR_ARRAY_LENGTH_1 ); 
   546                         IF ODD(MY_STATUS) 
   547                         THEN 
   548                             MY_STATUS := INPUT_IOSB.IOSB$W_STATUS; 
   549                         IF NOT ODD(MY_STATUS) 
   550                         THEN 
   551                             RETURN MY_STATUS; 
   552                         CONFIRM_IOSB.IOSB$W_BCNT := 0; 
   553                         IF CHAR_ARRAY_POINTER_2 <> NIL 
   554                         THEN 
   555                             REPEAT 
   556                             BEGIN   { Confirmation Specified } 
   557                             MY_STATUS := 
   558                                 $QIOW ( 
   559                                     EFN := EFN$C_ENF, 
   560                                     CHAN := TERMINAL_CHANNEL, 
   561                                     FUNC := QIO_FUNC, 
   562                                     IOSB := CONFIRM_IOSB, 
   563                                     P1 := CONFIRM_BUFFER, 
   564                                     P2 := SIZE(CONFIRM_BUFFER), 
   565                                     P5 := IADDRESS(CHAR_ARRAY_POINTER_2^), 
   566                                     P6 := CHAR_ARRAY_LENGTH_2 ); 
   567                             IF ODD(MY_STATUS) 
   568                             THEN 
   569                                 MY_STATUS := INPUT_IOSB.IOSB$W_STATUS; 
   570                             IF NOT ODD(MY_STATUS) 
   571                             THEN 
   572                                 RETURN MY_STATUS; 
   573                                 END     { Confirmation Specified } 
   574                                 UNTIL SUBSTR(CONFIRM_BUFFER,1, 
   575                                      CONFIRM_IOSB.IOSB$W_BCNT) 
   576                                      = SUBSTR(INPUT_BUFFER,1, 
   577                                      INPUT_IOSB.IOSB$W_BCNT); 
   578                              END     { Item Code is for text } 
   579                      ELSE 
   580                          {                                } 
   581                          { Only ACME-specific itemsets    } 
   582                          { can have non-text item codes.  } 
   583                          {                                } 
   584                               RETURN SS$_BUGCHECK; 
   585                          {                                } 
   586                      END;    { general or text input itemset } 
   587                 {                                     } 
   588                 { Fill in the Item List with the     } 
   589                 { input we just gathered.            } 
   590                 {                                    } 
   591                 { Bubble the null terminator up by 1.} 
   592                 {                                    } 
   593                 ITEM_LIST[EACH_ITEM+1] := 
   594                     ITEM_LIST[EACH_ITEM]; 
   595                 {                                    } 
   596                 { Add the new entry.                 } 
   597                 {                                    } 
   598                 ITEM_LIST[EACH_ITEM].ILE3$W_LENGTH := 
   599                      INPUT_IOSB.IOSB$W_BCNT; 
   600                 ITEM_LIST[EACH_ITEM].ILE3$W_CODE::ACMEIC$TYPE := 
   601                      ITEMSET_ARRAY^[EACH_ITEMSET].ACMEIS$W_ITEM_CODE; 
   602                 ITEM_LIST[EACH_ITEM].ILE3$PS_BUFADDR := 
   603                      IADDRESS(INPUT_BUFFER); 
   604                 EACH_ITEM := EACH_ITEM + 1; 
   605                 EACH_ITEMSET := EACH_ITEMSET + 1; 
   606                 {                                    } 
   607                 END;    { process Input Itemset } 
   608                 {                                         } 
   609                 { Process Output Itemsets up to the next  } 
   610                 { Input Itemset.                          } 
   611                 {                                         } 
   612                 WHILE EACH_ITEMSET <= ITEMSET_COUNT DO 
   613                    BEGIN   { process one itemset } 
   614                    CHAR_ARRAY_LENGTH_1 
   615                        := ITEMSET_ARRAY^[EACH_ITEMSET] 
   616                               .acmeis$q_data_1 
   617                               .L0 MOD 65536; 
   618                    CHAR_ARRAY_POINTER_1 
   619                        := ITEMSET_ARRAY^[EACH_ITEMSET] 
   620                               .acmeis$q_data_1 
   621                               .L1::CHAR_ARRAY_TYPE_POINTER; 
   622                    CHAR_ARRAY_LENGTH_2 
   623                        := ITEMSET_ARRAY^[EACH_ITEMSET] 
   624                               .acmeis$q_data_2 
   625                               .L0 MOD 65536; 
   626                    CHAR_ARRAY_POINTER_2 
   627                        := ITEMSET_ARRAY^[EACH_ITEMSET] 
   628                               .acmeis$q_data_2 
   629                               .L1::CHAR_ARRAY_TYPE_POINTER; 
   630                    IF ITEMSET_ARRAY^[EACH_ITEMSET].ACMEIS$L_FLAGS 
   631                        .ACMEDLOGFLG$V_INPUT 
   632                    THEN 
   633                        {                                    } 
   634                        { Recurse to provide an input buffer } 
   635                        { for this input itemset.            } 
   636                        {                                    } 
   637                        RETURN RECURSE_OVER_ITEMS ( 
   638                               ITEMSET_ARRAY^[EACH_ITEMSET] 
   639                                       .ACMEIS$W_MAX_LENGTH ) 
   640                        {                                    } 
   641                     ELSE 
   642                         IF ITEMSET_ARRAY^[EACH_ITEMSET].ACMEIS$W_MSG_TYPE 
   643                             .ACMEMC$V_ACME_SPECIFIC 
   644                         AND NOT ITEMSET_ARRAY^[EACH_ITEMSET] 
   645                             .ACMEIS$W_ITEM_CODE.ACMEIC$V_UCS 
   646                         THEN        { ACME-specific non-text } 
   647                             {                                        } 
   648                             { Comparing MY_CONTXT^.ACMECB$L_ACME_ID  } 
   649                             { .ACMEID$V_ACME_NUM field against the   } 
   650                             { (previously queried) IDs of ACMEs from } 
   651                             { which this client expects ACME-specific} 
   652                             { output itemsets, and also              } 
   653                             {                                        } 
   654                             { comparing ITEMSET_ARRAY^[EACH_ITEMSET] } 
   655                             {    .ACMEIS$W_MSG_TYPE.ACMEMC$W_MSG_CODE} 
   656                             { against the 16-bit values of expected  } 
   657                             { ACME-specific message types, we get the} 
   658                             { information to dispatch to handle each } 
   659                             { of the ACME-specific message types that} 
   660                             { this client program knows about.       } 
   661                             {                                        } 
   662                             { But this client does not know about any} 
   663                             { ACME-specific message types, so an ACME} 
   664                             { that sent a message we cannot handle is} 
   665                             { behaving totally incorrectly, and we   } 
   666                             { give up.                               } 
   667                             {                                        } 
   668                             ASSERT(FALSE, 
   669                                    'unknown ACME-specific message type') 
   670                             {                                        } 
   671                         ELSE 
   672                             BEGIN   { text or general output itemset } 
   673                             {                                        } 
   674                             { Pascal does not give us the ability    } 
   675                             { that more strongly typed languages do  } 
   676                             { to force a compile-time failure in the } 
   677                             { case where new message types have been } 
   678                             { added to a subsequent release of VMS,  } 
   679                             { so we make these run-time checks.      } 
   680                             {                                        } 
   681                              ASSERT(ACMEMC$K_MIN_GEN_MSG 
   682                                       = ACMEMC$K_GENERAL, 
   683                                     'ACMEMC$K_MIN_GEN_MSG has shifted'); 
   684                              ASSERT(ACMEMC$K_MAX_GEN_MSG 
   685                                       = ACMEMC$K_DIALOGUE_ALERT, 
   686                                     'ACMEMC$K_MAX_GEN_MSG has shifted'); 
   687                              ASSERT(ACMEMC$K_MIN_LOGON_MSG 
   688                                       = ACMEMC$K_SYSTEM_IDENTIFICATION, 
   689                                     'ACMEMC$K_MIN_LOGON_MSG has shifted'); 
   690                              ASSERT(ACMEMC$K_MAX_LOGON_MSG 
   691                                       = ACMEMC$K_MAIL_NOTICES, 
   692                                     'ACMEMC$K_MAX_LOGON_MSG has shifted'); 
   693                              {                                        } 
   694                              { All general output itemsets carry text,} 
   695                              { but based on the type of item, it would} 
   696                              { be possible to display them on various } 
   697                              { parts of the screen with distinctive   } 
   698                              { colors and video characteristics.      } 
   699                              {                                        } 
   700                              { That part is left as an exercise for   } 
   701                              { the reader, and in each case we call   } 
   702                              { WRITE_ITEM_PLAIN.                      } 
   703                              {                                        } 
   704                              CASE ITEMSET_ARRAY^[EACH_ITEMSET] 
   705                                       .ACMEIS$W_MSG_TYPE 
   706                                       .ACMEMC$W_MSG_CODE of 
   707                                  ACMEMC$K_GENERAL : 
   708                                      { General text                   } 
   709                                      WRITE_ITEM_PLAIN; 
   710                                  ACMEMC$K_HEADER : 
   711                                      { Header text                    } 
   712                                      WRITE_ITEM_PLAIN; 
   713                                  ACMEMC$K_TRAILER : 
   714                                      { Trailer text                   } 
   715                                      WRITE_ITEM_PLAIN; 
   716                                  ACMEMC$K_SELECTION : 
   717                                      { Acceptable choices             } 
   718                                      WRITE_ITEM_PLAIN; 
   719                                  ACMEMC$K_DIALOGUE_ALERT : 
   720                                      { Alert (advisory)               } 
   721                                      WRITE_ITEM_PLAIN; 
   722                                  ACMEMC$K_SYSTEM_IDENTIFICATION : 
   723                                      { System identification text     } 
   724                                      WRITE_ITEM_PLAIN; 
   725                                  ACMEMC$K_SYSTEM_NOTICES : 
   726                                      { System notices                 } 
   727                                      WRITE_ITEM_PLAIN; 
   728                                  ACMEMC$K_WELCOME_NOTICES : 
   729                                      { Welcome notices,               } 
   730                                      WRITE_ITEM_PLAIN; 
   731                                  ACMEMC$K_LOGON_NOTICES : 
   732                                      { Logon notices                  } 
   733                                      WRITE_ITEM_PLAIN; 
   734                                  ACMEMC$K_PASSWORD_NOTICES : 
   735                                      { Password notices               } 
   736                                      WRITE_ITEM_PLAIN; 
   737                                  ACMEMC$K_MAIL_NOTICES : 
   738                                      { MAIL notices                   } 
   739                                      WRITE_ITEM_PLAIN; 
   740                                  otherwise 
   741                                      {                                } 
   742                                      { Some other output message type.} 
   743                                      {                                } 
   744                                      WRITE_ITEM_PLAIN; 
   745                                      {                                } 
   746                                      END;        { CASE ACMEMC$W_MSG_CODE } 
   747                                      END;    { text or general output itemset } 
   748                              EACH_ITEMSET := EACH_ITEMSET + 1; 
   749                              END;    { process one itemset } 
   750                          {                                         } 
   751                          { We have reached the end, call SYS$ACM.  } 
   752                          {                                         } 
   753                          RECURSE_OVER_ITEMS := $ACM ( 
   754                                  EFN := EFN$C_ENF, 
   755                                  FUNC := ACME$_FC_AUTHENTICATE_PRINCIPAL, 
   756                                  ITMLST := ITEM_LIST, 
   757                                  CONTXT := %IMMED IADDRESS(MY_CONTXT), 
   758                                  ACMSB := MY_ACMESB ); 
   759                  END;    { FUNCTION RECURSE_OVER_ITEMS } 
   760      BEGIN       { FUNCTION RESPOND } 
   761            ITEM_LIST[EACH_ITEM].ILE3$W_LENGTH := 0; 
   762            ITEM_LIST[EACH_ITEM].ILE3$W_CODE := 0; 
   763            ITEM_LIST[EACH_ITEM].ILE3$PS_BUFADDR := 0; 
   764            ITEM_LIST[EACH_ITEM].ILE3$PS_RETLEN_ADDR := NIL; 
   765            {                                                 } 
   766            { We provide 0 as an indication that this is the  } 
   767            { outermost call, rather than one made due to     } 
   768            { encountering an input itemset.                  } 
   769            {                                                 } 
   770            RESPOND := RECURSE_OVER_ITEMS ( 0 ); 
   771            {                                                 } 
   772      END;        { FUNCTION RESPOND } 
   773   BEGIN       { FUNCTION AUTHENTICATE } 
   774   {                                                         } 
   775   { Make an initial query to determine the ACME ID of       } 
   776   { the Fingerprint ACME in the current running system.     } 
   777   {                                                         } 
   778   ACM_QUERY_ITMLST[0].ILE3$PS_BUFADDR := IADDRESS(SYS$ACM_ACME_ID); 
   779   ACM_QUERY_ITMLST[1].ILE3$PS_BUFADDR := IADDRESS(ACME_QUERY_ACME_NAME); 
   780   ACM_QUERY_ITMLST[2].ILE3$PS_BUFADDR := IADDRESS(FINGERPRINT_ACME_NAME); 
   781   ACM_QUERY_ITMLST[3].ILE3$PS_BUFADDR := IADDRESS(ACME_TARGET_DOI_ID); 
   782   ACM_QUERY_ITMLST[4].ILE3$PS_BUFADDR := IADDRESS(FINGERPRINT_ACME_ID); 
   783  MY_STATUS:=1; 
   784  MY_ACMESB.ACMESB$L_STATUS := ACME$_NOSUCHDOI; 
   785  IF not ODD(MY_STATUS) then 
   786              MY_STATUS := $ACMW ( 
   787                      EFN := EFN$C_ENF, 
   788                      FUNC := ACME$_FC_QUERY, 
   789                      ITMLST := ACM_QUERY_ITMLST, 
   790                      ACMSB := MY_ACMESB ); 
   791              IF ODD(MY_STATUS) 
   792              then 
   793                  MY_STATUS := MY_ACMESB.ACMESB$L_STATUS; 
   794              IF NOT ODD(MY_STATUS) 
   795              then 
   796                  {                                                     } 
   797                  { "No Fingerprint ACME present" is a perfectly valid  } 
   798                  { state of affairs, and we record a zero ACME ID.     } 
   799                  {                                                     } 
   800                  IF MY_STATUS = ACME$_NOSUCHDOI 
   801                  THEN 
   802                      FINGERPRINT_ACME_ID := ZERO 
   803                  ELSE 
   804                      LIB$SIGNAL(MY_STATUS); 
   805              {                                                         } 
   806              { Make an initial authentication call.                    } 
   807              {                                                         } 
   808              MY_CONTXT := (-1)::ACMECB_PTR; 
   809              MY_ACM_ITMLST_A[0].ILE3$PS_BUFADDR := IADDRESS(MY_LOGON_TYPE); 
   810              MY_ACM_ITMLST_A[1].ILE3$PS_BUFADDR := IADDRESS(MY_DIALOGUE_SUPPORT); 
   811              MY_STATUS := $ACMW ( 
   812                      EFN := EFN$C_ENF, 
   813                      FUNC := ACME$_FC_AUTHENTICATE_PRINCIPAL, 
   814                      ITMLST := MY_ACM_ITMLST_A, 
   815                      CONTXT := %IMMED IADDRESS(MY_CONTXT), 
   816                      ACMSB := MY_ACMESB ); 
   817              IF ODD(MY_STATUS) 
   818              then 
   819                  MY_STATUS := MY_ACMESB.ACMESB$L_STATUS; 
   820              IF NOT ODD(MY_STATUS) 
   821              then 
   822                  {                                                     } 
   823                  { "Operation Incomplete" is to be expected.           } 
   824                  {                                                     } 
   825                  IF MY_STATUS <> ACME$_OPINCOMPL 
   826                  THEN 
   827                      LIB$SIGNAL(MY_STATUS); 
   828              {                                                         } 
   829              { Respond to successive dialogue steps.                   } 
   830              {                                                         } 
   831              WHILE MY_STATUS = ACME$_OPINCOMPL DO 
   832                      BEGIN 
   833                      ITEMSET_ARRAY := MY_CONTXT^ 
   834                          .acmecb$ps_item_set::ITEMSET_ARRAY_TYPE_POINTER; 
   835                      MY_STATUS 
   836                          := RESPOND ( MY_CONTXT^.acmecb$l_item_set_count ); 
   837                      IF NOT ODD(MY_STATUS) 
   838                      then 
   839                          BEGIN   { Abandon the authentication } 
   840                          MY_ACM_ITMLST_A[0].ILE3$W_LENGTH := 0; 
   841                          MY_ACM_ITMLST_A[0].ILE3$W_CODE := 0; 
   842                          MY_ACM_ITMLST_A[0].ILE3$PS_BUFADDR := 0; 
   843                          MY_ACM_ITMLST_A[0].ILE3$PS_RETLEN_ADDR := NIL; 
   844                          TRASH_STATUS := $ACMW ( 
   845                                  EFN := EFN$C_ENF, 
   846                                  FUNC := ACME$_FC_FREE_CONTEXT, 
   847                                  ITMLST := MY_ACM_ITMLST_A, 
   848                                  CONTXT := %IMMED IADDRESS(MY_CONTXT), 
   849                                  ACMSB := MY_ACMESB ); 
   850                          LIB$SIGNAL(MY_STATUS); 
   851                          END;   { Abandon the authentication } 
   852                      MY_STATUS := $SYNCH_ACMESB ( 
   853                              EFN := EFN$C_ENF, 
   854                              IOSB := MY_ACMESB ); 
   855                      IF ODD(MY_STATUS) 
   856                      then 
   857                          MY_STATUS := MY_ACMESB.ACMESB$L_STATUS; 
   858                      END; 
   859              IF NOT ODD(MY_STATUS) 
   860              then 
   861                  LIB$SIGNAL(MY_STATUS); 
   862              {                                                     } 
   863              IF FINGERPRINT_READER_CHANNEL <> 0 
   864              THEN 
   865                  BEGIN   { a channel was assigned } 
   866                  MY_STATUS := 
   867                      $DASSGN ( 
   868                          CHAN := FINGERPRINT_READER_CHANNEL ); 
   869                  IF NOT ODD(MY_STATUS) 
   870                  then 
   871                      LIB$SIGNAL(MY_STATUS); 
   872                  END;    { a channel was assigned } 
   873              {                                                     } 
   874              IF TERMINAL_CHANNEL <> 0 
   875              THEN 
   876                  BEGIN   { a channel was assigned } 
   877                  MY_STATUS := 
   878                      $DASSGN ( 
   879                          CHAN := TERMINAL_CHANNEL ); 
   880                  IF NOT ODD(MY_STATUS) 
   881                  then 
   882                      LIB$SIGNAL(MY_STATUS); 
   883                  END;    { a channel was assigned } 
   884      {                                                             } 
   885      AUTHENTICATE := TRUE; 
   886      END;        { FUNCTION AUTHENTICATE } 
   887  BEGIN   { PROGRAM ACM_SHOPFLOOR } 
   888      AUTHENTICATE ( PRINCIPAL_10 ); 
   889  END.    { PROGRAM ACM_SHOPFLOOR } 
 


Previous Next Contents Index