July 31, 1997
               OCS - The E781 Open Constant System
               ___________________________________
                    (valid for version v2_4)

INTRODUCTION

The Open Constant System (OCS) is a set for FORTRAN 77 routines and shell 
scripts which manage the constants files for E781.  All constants files are
kept in a single directory tree.  Most of these are ASCII text files which
can be read and managed with the UNIX tools and editors as well as OCS fortran
routines.

All OCS constants files contain one or more blocks.  A block consists of a
key and the particular set of entries for which correspond to that key. It is 
terminated by '*end' line.  The key identifies the particular version of a 
table.  A table is the basic unit of data managed by OCS.  It is a user 
defined group of constants.  OCS manages the keying so that the correct set of 
constants are identified for any particular key (run number, constant class and
version...).  

OCS is invoked with simple calls in the initialization sections of working 
routines.  It will see to it that the currently correct version of
each constant table is located among the constant files and loaded into the
corresponding constant common block for that table.  It also provides optional
tools to modify and link constant common blocks.  The constant files may be 
standard form text files or files in user specified formats (either text or
binary).  In the latter case user code is required to read in the file and
fill the constant common block.

OCS constant tables are defined by the Fortran include files which describe
the common block which contains the table at run time.  A section of each of
these include files is written in a fixed format as a "DDL" (Data Description
Langauge" description of the structure of the table.  These include files are
read by the OCS routines as text files at initialization time during program
execution.  This technique allows these table to be reorganized and expanded
without requiring modification to all previously existing copies of the table 
in the database.  It also provides a one to one correspondance between the text 
stored for any constant in a table and the Fortran variable name where that 
constant may be found at execution time. 

OCS FILES

OCS standard form text files are free format and are handled automatically
without user code.  The only absolute conventions are that a '#' in column 1
tags a key line, '!' in column 1 tags a comment line and '*' in column 1 tags 
a block terminating line (blocks are data terminated with a line, beginning 
with '*end').  Like in Fortran, end of line comments preceeded with a '!' are
optional.  
Standard form OCS text files have the following elements:

file.ocs                               ! files' name
    !<optional comment lines>
    keyed block                        ! only keyed blocks
    !<optional comment lines>          ! are managed by OCS
      ...                              ! as many in a file as you like
    [eof]

keyed block                            ! standard form of keyed block 
    #key                               !   see format below 
    table data                         !   see format below 
    *end                               !   terminating line required 
    !<comment>                         !   any number of commentary lines 
                                       !   (beginning with !) allowed 
                                       
key                                    ! standard key format
    #table_name first_run last_run mode class version date time author ! comment

    #              !      required key prefix
    table_name     ! c*12 required name of table. table_name//'_cbk.inc' 
                   !      is corresponding common block/DDL
    first_run      ! i*4  first valid run number 
    last_run       ! i*4  last  valid run number (0 - for ALL runs.ge.first_run)
    mode           ! c*8  data mode
         'fill'    !      table data in FILL_TABLE form follows 
                   !      - clear and refill table in memory 
         'mod'     !      table data in FILL_TABLE form follows 
                   !      - modify existing table  in memory 
         'binary'  !      users "binary" (non standatd) format - OCS table has 
                   !      only file name where the table is located
         <other>   !      string available to user to keep table format 
    class          ! c*8  class of constants (must begin with [a-z]) 
                   !      to select group of tables
    version        ! c*8  version of constants (must begin with [a-z]), default=' '
                   !      "Latest" is by alphanumeric order in lower case
    date           ! c*11 constant creation date  format='dd-Mon-yyyy'
    time           ! c*8  constant creation time  format='hh:mm:ss'
    author         ! c*8  constant author identifier
                   !
    !              !      comment delimiter
    comment        ! c*80 Any comment that will fit on the line

table data         ! format of standard form OCS table for 'fill' or 'mod' mode
                   ! all input is free format done using the ARG routine.
    field names    ! column labels for table.  MUST match the variables 
                   ! defined in the include file (DDL) [field_table]
    data values    ! field data for a record.  one line per record.
                   !
    *end           ! terminating line

modes

     fill          ! clear present table common block and fill with the
                   ! records in the order given
     mod           ! search the first field given in the memory for a match 
                   ! with the table data.  replace all other given fields in
                   ! that record with the table data
     binary        ! stores in memory only file name where the table is located
                   !
     <other>       ! for non-standard (user read) text tables may be any non-
                   ! blank string except 'fill' or 'mod'.  Available to user 
                   ! to keep table format
Examples

Defines two standard and one user tables for a range of runs [1111-2222]. 
Changes the spectrometers z positions for runs [2000-2222]. One table (spec_sz) 
is in use for all runs.

#spec_pos  1111  2222 fill mc v03.10 07-Mar-1996 11:50 AK ! spectrometer positions
name     x       y       z
bm      0.0     0.0     0.0000
vx      0.0     0.0     0.0000
! M1 magnet position:
m1      0.0     0.0   199.4043
! M2 magnet position:
m2      0.0     0.0   751.1155
a2      0.0     0.0  2760.0
b2      0.0     0.0  3360.0
c2      0.0     0.0  3960.0
! M3 magnet position:
m3      0.0     0.0  4245.7095
*end

#spec_sz   0  0 fill mc v03.10 07-Mar-1996 11:50 AK !  spectrometer  sizes
name     x          y         z1             z2
bm      200.0     200.0    -200.0000        0.0000
vx      200.0     200.0       0.0000      199.4043
m1      200.0     200.0     199.4043      751.1155
m2      200.0     200.0     751.1155     2760.0
a2      200.0     200.0    2760.         3360.0
b2      200.0     200.0    3360.         3960.0
c2      200.0     200.0    3960.         4245.7095
m3      200.0     200.0    4245.7095    10000.0000
*end

#scaler  3624 3624 text example v02.00 16-JUL-90 17:23 psc ! E761 online scalers
 spill	count	B1	B2	T2.BV	B3	
 0	1	5272774	6813575	0	4470157	
 2	2	4749276	6117738	0	4025935	
 3	3	2948957	3782012	0	2501156	
 1	4	4781003	6181995	0	4051291	
 -1	71.0	3.778E+08	4.878E+08	0.000E+00	3.196E+08
*end

#spec_pos  2000  2222 mod  mc v03.10 15-Apr-1996 14:00 AK ! spectrometer positions
name       z
m1      199.4
m2      751.1
a2     2760.0
b2     3360.0
c2     3960.0
m3     4245.62
*end

#user 2222 2222 binary example v01.00 15-Mar-1993 23:23  psc ! example of a binary file
/home/pcooper/e781/off781/ocs/testing/peter_ocs
*end

#user 3333 3333 binary example v02.00 05-Jun-1996 23:33  rud ! example of a binary file
$CON781_DIR/testing/peter_ocs
*end

 For version v02.00 of 'user' binary table you may see use of environment variable in a
file name.
  
FORTRAN USAGE

OCS subroutines with their arguments and functions are listed below.  All
routines which call OCS routines may have following OCS control include:

#include "ocs_cbk.inc".

OCS kernel routines:

* routine 'ocs_update' 

  - At first call initialises OCS and loads in memory common blocks standard OCS
    form tables, given in 'ocs_fill' routine. At any following calls it reloads
    those tables which don't correspond to current 'ocs_run'.

* logical function ocs_register(ocs_id, routine, owner_list, user_list) 
                                out     in       in          in
                                i4      c12      c*          c*
                                
  - Registers calling routine with OCS  

  Identify all users of constant tables to OCS system.
  - On first entry initialize the OCS system.
  - Identify 'routine' with 'ocs_id' which is the internal index into
    the OCS control tables ('ocs_id' is a local variable defined in ocs_cbk.inc).
  - Book 'routine' as a "owner" or "user" of the table names listed in 
    'owner_list' and 'user_list' respectively.
  - Return '.false.' if an error is detected.
  
  Example - see below APPENDIX 1.
  
* logical variables 'ocs_new_const(ocs_id)' in #include "ocs_cbk.inc" 

  Determine if ANY tables used by the calling routine need to be filled,
  - '.true' if any table user by the calling routine needs to be filled
    (or refilled).  
    
  Example - see below APPENDIX 1.

* logical function ocs_table(ocs_id, mode, l_cur,  line)
                             in      out   out/in  out 
                             i4      log   i4      c*1024
                              
  - Find all tables owned by the calling routine which have to be updated. 
  - Fill standard form table commons automatically.
  - Return .true. to user with file open on Fortran unit 'ocs_unit' and 
    corresponding key in 'line' for each user formatted table.
    - Calling routine should determine which table is open from 'line[1]' and 
      the tables' format from line[4].  (line[4] is the fourth argument parsed 
      from 'line' by the ARG routine.)
    - If format is 'binary' user must read the file name from the next line read
      and open that file (see below APPENDIX 1).
    - Read in that table.
    - Close any files opened
  - Returns '.false.' when done.
  
  Example - see APPENDIX 1 below.

* logical function ocs_link_table(table1, l1, n1, table2, l2, n2, link2, c1, c2)
                                  in      in  in  in      in  in  out    in  in
                                  c12     i4  i4  c12     i4  i4  i4     i4  i4
                                  
  - Link one OCS table to another in memory
    - For each record in table2 scan for a character match with table1(c1:c2) 
      ('name' field); l1 is first dimension of table1, n1 is number of filled rows
      and same for table2;
    - store the number of the matched 'table1' record in 'table2' variable 'link2'

  Example - see APPENDIX 1 below.

* logical function ocs_link_tables(table1, name1, table2, name2, link2, c1, c2)
                                   in      in     in      in     out    in  in
                                   c12     c12    c12     c12    i4     i4  i4

  is a new version of previous routine with modified set of arguments,                                   
  where
  
       table1 -  name of table-1, 
       table2 -  name of table-2,
       name1  -  table-1 name fields,
       name2  -  table-2 name fields,
       link2  -  link in table-2 to table-1 (row number),
       c1, c2 -  string region to match: c1 - first symbol, c2 - last symbol

  "Link" means that for each record 'name2' in 'table2' routine scan for a 
  character match with 'name1'(c1:c2) ('name' field) and store the number of the
  matched 'name1' record in 'table2' variable 'link2'.
  
  Example - see APPENDIX 1 below.  
                                  
* routine ocs_nafrel(l2, n2, link2, ind1, ind2, c1, c2)
                     in  in  in     in    out   out out
                     i4  i4  i4     i4    i4    i4  i4  
   
  - Link a standard E781 DB common block table to another one by ADAMO like 
    NAFREL way (in MC, for example);

  where  
       l2    - length  of table-2 records
       n2    - number of entries in table-2
       link2 - array  of  links  in table-2 to table-1
       ind1  - current position in the table-1
       ind2  - current position in the table-2
       c1    - string region to match - first
       c2    - string region to match - last

* routine ocs_load_table(table,class,version,f_run,l_run,ierr)
                         in    in    in      in    in    out
                         c12   c8    c8      i4    i4    i4    

  - Load new version of the table in memory.

       table   - TABLE name
       class   - input string with CLASS   name
       version - input string with VERSION name
       f_run   - intput FIRST-RUN number
       l_run   - intput  LAST-RUN number
       ierr    - ERROR flag: = 0 if OK,
                             = 1 if requested table not found
    
* routine ocs_load_version(class, version, ierr)
                           in     in       out
                           c8     c8       i4
                          
  - Load new version of all tables. For given 'class' those versions will be taken
    which are not "older" than 'version'.
    
       class   - CLASS   name
       version - VERSION name
       ierr    - ERROR flag: = 0 if OK,
                             = 1 if NO tables was found
    
* integer function ocs_ntab_reload(key, tables, classes, versions, f_runs, l_runs)
                   out             in   out     out      out       out     out
                   i4              i4   c12(*)  c8(*)    c8(*)     i4(*)   i4(*)
             
                                            * = 'mftab' in 'ocs_update'

  - Function to search for table versions of the same class as current is, which 
    should be refilled for new RUN number. Gives number of those tables as 
    output value.

  Arguments:
  
       key      - flag: 1 - reload of all found tables
                	0 - NO reload, list only
       tabs     - output array with TABLE   names
       classes  - output array with CLASS   names
       versions - output array with VERSION names
       f_runs   - output array with first-run numbers
       l_runs   - output array with  last-run numbers

* routine ocs_bin_table(table, bin_file, ierr)
                        in     out       out
                        c12    c*        i4

  - Get file name for the binary table.
  
  Arguments: 
   
       table    - TABLE name
       bin_file - output string with file name for the table
       ierr     - ERROR flag: = 0 if OK,
                              = 1 if requested table not found

* routine ocs_tab_version(table,class,version,f_run,l_run, modes, n_out)
                          in    out   out     out   out    out    out
                          c12   c8    c8      i4    i4     c8(20) i4
    ........
#include "ocs_tab_version.inc" ! for 'class,version,f_run,l_run,modes,n_out'

  - Get info about the active table.
  
  Arguments:
    
       table   - TABLE name
       class   - output string with CLASS   name
       version - output string with VERSION name
       f_run   - first RUN for the table 
       l_run   - last  RUN for the table
       modes   - modes  for the table
       n_out   - number of table parts found:
                                             =0 in error case,
                                             =1 if only 'fill' part found,
                                             >1 if 'mod' part(s) found
   
OCS printing routines:
  
* routine ocs_prin_table(table, nrow, xtab, calling) 
                         in     in    in    in
                         c12    i4    r4    c*

  - Print onto unit 'ocs_prt_unit' (see 'ocs_cbk.inc') standard OCS table 'table' 
    stored in array 'xtab' having 'nrow' filled rows heading by 'calling' with 
    the name of calling routine & comment.
  
  Example - see routine 'test_ocs.F' below.

* routine ocs_prin_all(table, calling)
                       in     in
                       c*     c*
  
  - Print ALL standard OCS tables filled by 'ocs_fill.F' if first argument is 
    empty (' '). Prints only given 'table' if it was specified.
    
    To generate routine 'ocs_prin_all.F' type command 
    
      ocs_prin_all_maker
      
    or (better!) type
    
      ocs_init
         
    Example - see routine 'event_analysis.F' in $OCS781_EXA.
   
* routine 'ocs_dump' -  Dump OCS commons.    
  

* OCS table include files

  To generate include files for standard OCS tables use script 'ocs_incl_maker'.  
  It creates .inc file of OCS and writes it as a file 'table_name.inc_Z'.  
  If all is OK with it,  you may rename  it to        'table_name.inc'
  
  Arguments:
            $1 - table_name 
            $2 - string of USER variable names ( short names like in ddl );
                 first field of table by OCS requirements is 'name', c*12
                                                - it defines INTERNALLY
            $3 - string of variable types for each variable name  
                 (only c12, c8, c4, r4 and i4 can be used)            
            $4 - maximum number of rows in the table (OPTIONAL parameter, 20 is 
                                                                   preset value)
            $5 - string of linking tables names      (OPTIONAL parameter)
            
  Short variable names from string will be transformed to long form like:
  volume_table_name, shape_table_name, x_table_name, k_table_name.

  Short linking names (l-name) will be transformed by following concatenation:
                      'l-name' -> 'lk//l-name//_table_name'      

  ATTENTION: You MUST edit the '<table_name>.inc' file to add table description 
             and to add comments to each variable.
  Example:

  ocs_incl_maker rich_def 'volume shape x  k '  \
                          'c8     c4    r4 i4' 25 'glob_pos'
                                     
  To generate 'ocs_fill' routine by 'ocs_fill_maker' (to include filling of your and
  system tables) type command 
  
    ocs_fill_maker
      
    or (better!) type
    
      ocs_init
    
  in directory, where your <ocs>.inc includes and <your_name>.ocs files are located.
  
CALLING ORDER

  <Slava - we need a description of the phasing of OCS calls and the
   associated functions - FIRST, INIT1 INIT2, ...>

EXAMPLE OF FORTRAN USAGE - see APPENDIX 1

      
OCS INITIALIZATION

At OCS initialization stage information about all input tables (allowed by class 
selection) is stored in OCS metatables. Some of them are marked as active tables 
in accord with class/version/run selection. Usually reload procedure changes 
active tables and refills them.

OCS has an internally controled parameter described in 'ocs_cbk.inc':

  - ocs_error_level (i4)- ERROR level: 0 - no errors
                                       1 - OCS warning
                                       2 - fatal OCS error
  
OCS has some user controled internal parameters described in 'ocs_cbk.inc':

  - ocs_prt_unit   (i4) - fortran unit for table printing (default value = 6);
  - ocs_prt_line   (i4) - length of printing lines used in 'ocs_prin_table' for 
                          line splitting during the table printing (default=130);
  - ocs_debug_level(i4) - debugging level: 0 - no    debug (default),
                                           1 - short debug,
                                           2 - full  debug;
  - ocs_class_main (c8) - class of the highest priority for table selection;
  - ocs_class_out (c80) - list of initially rejecting classes;
  - ocs_class_get (c80) - list of classes which should be accepted;
  - ocs_sets_out  (c80) - list of table sets which should be ignored.

Class treatment. Three mentioned string variables in 'ocs_cbk.inc' 'ocs_class_out',
'ocs_class_get', and 'ocs_class_main' should be defined by user just before the 
first 'call ocs_update'.    
      
  - 'ocs_class_out' tells OCS what classes should be hardly rejected at initial 
    stage. It means that user in this run will NEVER use these classes (default 
    value is ' ' - NO selection). Example:

    ocs_class_out='aa test'   ! c8 each
                           
  - 'ocs_class_get' tells OCS tables of what classes should be initialized. 
    It means that user in this run will use exactly these classes (default value
    is ' ' - NO selection, ALL tables in use). Example:

    ocs_class_get='mc anal' ! c8 each

    User is free to choose between these two options, or don't use both of them.
    It's clear that in the case of clash between '_out' and '_get' requests, 
    '_out' will have priority over '_get'.

    It's clear too that you will have problem with class selection if you have 
    more then 2 classes on input. To resolve this problem use 'ocs_class_main'.

  - 'ocs_class_main' gives OCS the class with highest priority at automatic
    table selection stage (important in the case of class clashes; default 
    value is ' ' - NONE). Example:
    
    ocs_class_main='anal' ! c8

  Class selection works as follows:
    
   - if 'ocs_class_out' is not empty, then at initial stage tables of listed
     classes will be hardly rejected;
     
   - from tables which were left after this rejection will be taken only those 
     tables which are listed in 'ocs_class_get' if it's not empty (if 
     'ocs_class_get'=' ' then ALL classes will be taken - default);
       
   - at any time when class/version/run selection is doing in the case of more
     then one class for the table a classes clash will be resolved using 
     'ocs_class_main' which defines the class with highest priority.

  If you don't wish to load tables for some particulary detectors, you may ignore
  them listing all these subsets in a string variable 'ocs_sets_out':   
  
  ocs_sets_out = 'rich_ adc_ phot_'  ! SETS to deselect 

  In this case all tables those names match one of substring will be ignored 
  by OCS.
  
After automatic table selection stage in 'ocs_update' user is free to load any 
available table with any class/version/run by 'call ocs_load_table(...)'.

OCS TABLES INFO

At any time (after first 'call ocs_update') user may get info about current status
of any active table using 'ocs_tab_version':
     
#include "ocs_tab_version.inc" ! for 'class,version,f_run,l_run,modes,n_out'
    ........

    call ocs_tab_version(table,class,version,f_run,l_run,modes, n_out)
                         in    out   out     out   out   out    out
                         c12   c8    c8      i4    i4    c8(20) i4

In 'class' user will get class of currently active 'table', in 'version' - its 
version, in 'f_run' - first run for the table, in 'l_run' - last run for the 
table, 'modes' will have list of used modes for the table. 
                                                                  
n_out > 1 if 'mod's were used in addition to 'fill' for the table and it will be
the number of found table parts.

File name for 'binary' table can be taken by routine 'ocs_bin_table': 

    call ocs_bin_table(table, bin_file, ierr)
                       in     out       out
                       c12    c*        i4
    
where 'bin_file' for given 'table' has file name with full path.
In error case ierr=1.
    
TABLES REFILLING

Table refilling can be done by user for any particulary table or by OCS itself 
in data driven mode if run number ('ocs_run') was changed.

To refill particulary table user needs to call:

    call ocs_load_table(table, class, version, first_run, last_run, ierr)
                        in     in     in       in         in        out
                        c12    c8     c8       i4         i4        i4     

This routine will force OCS to load requested version of table (its 'fill'
and 'mod' parts), if 'class' is allowed by previous requests, 'table' available
with requested 'version', and 'first_run', and 'last_run' (ierr=1 if requested
table not found).
   
In data driven mode user should use second call to 'ocs_update':
  
    ocs_run = <new run number>
    call ocs_update
  
to reload those tables which are not valid for new RUN number.
  
At first call 'ocs_update' creates internal metatables and loads all tables in 
accord with automatic class/version/run selection procedure.
    
At any next calls 'ocs_update' will reload those tables which are not valid for 
current 'ocs_run' value. Actually in this case 'ocs_update' calls function 
'ocs_ntab_reload' with key=1 (user may use it directly):
  
    n_tables=ocs_ntab_reload(key, tables,  classes, versions, first_runs, 
   +                                                           last_runs)
             out             in   out      out      out       out
             i4              i4   c12(*)   c8(*)    c8(*)     i4(*)

                             * - max number of tables (= mftab in 'ocs_update')
                             
In the case of key=0 and for given run number 'ocs_run' (see 'ocs_cbk.inc')
this function will produce list of tables (in 'tables' array) with those
classes, versions and first/last-run numbers (in 'classes', 'versions',
'first_runs', and 'last_runs' arrays) which need to be refilled ('n_tables' -
number of refilling tables; if 'n_tables = 0' - NO tables for refilling).
After that user is free to change class/version for each table before its
reloading by yourself with 'ocs_load_table'.

In the case of key=1 'ocs_ntab_reload' internally calls 'ocs_load_table'
for each table to load new version of a table (with the same class) using
standard OCS version selection procedure.
    
Also user may (re)load all tables of given class choising versions which are 
.le.'version' in OCS sense by call to 'ocs_load_version':
    
    call ocs_load_version(class, version, ierr)
                          in     in       out 
                          c8     c8       i4
       
For each table those version will be taken which is mostly close to 'version' but
not "older" of it. ierr=1 if no one table found for reload.
                      
OCS allowes user to have NO own OCS tables (own '*.ocs' files) at both code and 
script levels.
    
KEYING

OCS keys define an instance of the data for a table.  The keyed block of a 
table chosen to be loaded into memory common blocks is determined by comparing 
all the keys for that table with the present value of the run number in a 
program and other, user set, criteria.  By default the keyed block with the
highest version for which the present run number is between the key's run number
limits is selected.  Highest version is determined in an alphabetical sense (in
lower case representation);V2 is after V1 and ZEBRA is after both.  If the user 
selects a specific version for a particular table then that version, if found, is 
selected.

There should never be two keyed blocks with identical keys.  If, by error, there
are then the last one will be used.

For OCS standard form tables MOD mode keyed blocks are permitted.  These 
modify the contents of a table already loaded into memory.  There is an 
additional rule for the selection of MOD mode keyed block.  A FILL mode keyed
block selected after (in the sense of order in the file) a MOD de-selects that 
MOD.  In the following example

#detv 1111 2222 fill example V01.00 15-Mar-1993 23:23  psc ! detector volumes
#detv 2000 2222 mod  example V01.00 15-Mar-1993 23:23  psc ! detector volumes
#detv 1111 2222 fill example V02.00 21-DEC-1993 00:00  psc ! detector volumes

the V02.00 keyed block would be selected for run 2100 and the MOD V01.00 would 
NOT be applied. Had the MOD appeared in the file after the FILL V02.00 block it 
would be applied. DB Manager beware! User OCS files are scanned after all standard
files are scanned.  Therefore, the user can always supersede that standard
constants either by refilling a table and/or modifying it.

OCS INTERNALS

The above section allows the user to use OCS as a black box.  This section 
explains how it does what it does.  There are two related parts, the OCS 
internal routines and the OCS files.  

OCS FILES

All standard OCS files are kept in a single directory tree with the exception
of user file(s).  Nothing in the system requires this.  Common sense demands it.
There are two classes of files, data and special.  The data files were described
above.  There are two special files which are produced from the whole set of
data files: 'master' and 'tables'.  'master' is the index to the standard
files.  It contains the key line from each keyed block along with the name 
of the file and line number where that key line can be found.  'tables' is a
list of every unique table name found.

Every computer system running E781 codes which reference OCS does not have to 
have the same set of OCS files present.  The 'master' and 'tables' files are 
required to reflect those files which are present.

Users keep their own sets of data and special files.  These are scanned after
the corresponding standard files.  There are tool to generate the special files
for use by both users and the database manager described in the DB Management 
section below.

OCS INTERNAL ROUTINES

When 'ocs_register' is first called the 'tables' file is read in.  All table names 
are compared to this list.  If an unknown table is requested by an owner a
fatal error is generated.

When 'ocs_scan_master' is first called the 'master' file is scanned and the keyed 
blocks selected for each known table are determined.  This scan is redone only 
when the run number or user version selections (the "key") are changed.  Tables 
kept in standard OCS files are filled immediately and all routines which own 
or use those tables have their 'ocs_new_const' flags set.  User mode key blocks 
are opened,found and passed back to the calling routine to be read.

After a key change if the first 'ocs_table' call is not from the owner of a
table an error is reported.  A table must be filled completely by its owner
before a user can correctly reference it.

The OCS system is smart enough to recognize that a particular table has not 
changed.  Only tables which change are opened and read.  Only routines which
own or use changed tables have their 'ocs_new_const' flags set.  

* logical variable 'ocs_key_changed' in #include "ocs_cbk.inc" 
  
  - user's variable to handle information about tables changings.

* subroutine ocs_fill_table(table, l_fields, max_records, n_records, 
                           xtable, mode,     line_current)
         
  Arguments:
  
       table        - table name
       l_fields     - number of fields in table
       max_records  - maximum number of table records
       n_records    - present number of table records (rows)
       xtable       - table array
       mode         - modify flag: TRUE  for 'mod',
                    		   FALSE for 'fill'
       line_current - current line number in opened file                           
       
                           
 Fill a OCS database common block for standard table from an external file. 
 The INCLUDE file for the common block is used as a DDL to describe the table's 
 format.
 
 Example - see any 'ocs_fill.F'
 
* logical function ocs_class_check(class,iflag)
                                   in    in
                                   c8    i4
                           
  - Function to check CLASS of a table before loading, sets .TRUE. if the CLASS
    was requested at initialization stage:
    
        if(ocs_class_check(class,iflag)) <'class' was requested>   
         
  Arguments:
  
       class - input string with CLASS name
       iflag - flag: 0 - check 'class' in 'ocs_class_out'
                     1 - check 'class' in 'ocs_class_get'
      
* logical function 'ocs_llt' (entry 'ocs_lle')
 
        if(ocs_llt(str1, str2)) <str2 is "older" then str1> 
           out     in    in
           logical c*    c*
           
  - Two strings comparison (fortran function 'llt' analog adopted to our version
    comparison requirements):
  
    = .TRUE.  if str2 is "older" then str1
    = .FALSE. if not
  
OCS TABLE VERSIONS and CLASSES

Looking at version selection for each table, it is clear that versions must be 
taken like date-time values going strongly in chronological order (in the OCS
string comparison sence). We need such kind of agreement to have automatic 
version selection - see example below and explanations after it.
  
  step  f_run  l_run  ver    comment
  1)    0      0      v1     first version
* 2)    0      0      v2     improved version
  3)    5     10      v3     improved at ANAL stage version
* 4)    5      7      v4     improved at ANAL stage version
* 5)    8     10      v4     improved at ANAL stage version
  ________________________
  6)	0      4      v2
  7)	5      7      v4
  8)	8     10      v4
  9)   11      0      v2
  
  OCS in automatic table selection regime will load tables given as a set 
2) - 5) by steps 6) - 9) for each run numbers region.


DB MANAGEMENT

The OCS files are to be maintained by the E781 database manager.  A set of
procedures written using the UNIX tools are available to help with this task.

ocs_make_master - create 'master' and 'tables' special files by searching 
                  all files ending in '.ocs' using 'ls' or 'find'.
                  NO  arguments - local use in current directory with 'ls'.
                  
   Flags: Arguments:

      -d <dir>       - main working directory (if not current)
      -a <dir1:dir2> - list of additional directories separated by ':'
      -f	     - use 'find' instead of 'ls' flag
      -e	     - env. variable substitution flag
      -h             - print help 

Examples: 

  ocs_make_master		   # looks for *.ocs files in running directory

  ocs_make_master -d $CON781_DIR   # looks for *.ocs files in $CON781_DIR
  								directory
  ocs_make_master -d $CON781_DIR -a $CON781_DIR/devel	 # looks for *.ocs files
                                                           in $CON781_DIR and in
                                                           $CON781_DIR/devel 
                                                           directories
  ocs_make_master -d $CON781_DIR -a $CON781_DIR/devel -e # same as above but 
                                                           replace part of path
  							   by env. variable 
  							   '$CON781_DIR'
  ocs_make_master -d `pwd`  -a `pwd`/devel -e # same as above but you run script
  					        in $CON781_DIR directory
  ocs_make_master -d `pwd`  -f -e	      # you run script in $CON781_DIR 
                                                directory and it looks into all 
                                                underlying directories

 
ocs_select - script with key block selection algorithm.  Lists keys of selected
             key blocks for each table a given class, version, and run. 
             It looks in both local 'tables/master' files and in remote files
             '$OCS781_MASTER/$OCS781_TABLES'.
             It has four flagged OPTIONAL arguments to select table, class, 
             version, and run number. It has also four option flags:

   Flags: Arguments:

      -c <class>   - for CLASS name
      -r <run>     - for RUN number
      -t <table>   - for TABLE name
      -v <version> - for VERSION (optionally includes +- signs)
      -l	   - local    running flag
      -nl	   - nonlocal running flag
      -s	   - sort  flag
      -p	   - print flag
      -h           - print help
             
  If flag '-s' given, then output will be sorted by table names.
 
  If flag '-l' given, then script will look only into local 'tables/master' 
  files. This option will be switched on automatically if environment variable 
  'OCS781_TABLES' not defined.
  
  If flag '-nl' given, then 'find' will be used instead of 'ls'.
  
  If flag '-p' given, then selected tables will be printed.
             

Examples:

  ocs_select -s			   - prints all content of 'master' files
  				     what gives info about all available
  				     tables (sorted by table names)

  ocs_select -c mc -s		   - finds all tables with 'mc' class,
                                     output will be sorted by table names
 
  ocs_select -t  pwc_pos	   - finds all available versions for
                        	     the table 'pwc_pos'

  ocs_select -t pwc_pos -r 2222    - finds for selected table all available
                                     versions with run numbers which match
  				     following conditions:
  				     
  				     First_run  Last_run <- run region
  				     
  				     0  	0	- selects always
  				     'F-run'	0	- selects tables with
  				   			  'F-run' <= 2222
  				     'F-run'	'L-run' - select tables with
  				   			  'F-run' <= 2222 .AND.
  				   			  'L-run' >= 2222

  ocs_select -v v01.01	           - finds all tables with versions='v01.01'

  ocs_select -t pwc_pos -v v01.01  - finds version 'v01.01' of selected table
  			             if it exists

You may select REGION of versions using +/- signes:

  ocs_select -t pwc_pos -v +v01.01     - finds all versions >= 'v01.01'
  					 for selected table

  ocs_select -t pwc_pos -v -v01.01     - finds all versions <= 'v01.01'
  					 for selected table

ocs_assempble_table - script to assemble whole OCS table from its 'fill' & 'mod' 
                      parts. It writes assembled table to the standard output.
 
 Usage:  
  
     ocs_assempble_table  <files> <inc-dir> <table> <class> <vers> <frun> 

     <files>   - list of all files separated by ':'
 
     <inc-dir> - directory for include file
 
     <table>   - table name
 
     <class>   - table class
 
     <vers>    - table version
 
     <frun>    - first run for the table
          
 Example: 
 
     ocs_assempble_table $CON781_DIR/pwc.ocs:$CON781_DIR/devel/pwc.ocs \
                         $OFF781_INC pwc_sz   anal  v00.01 0 


DB PROTECTION

Database protection in OCS is guaranteed by READONLY mode for all DB files and 
limited access to main DB directory. Some additional tools should be developed 
to check out the DB under running.

KNOWN PROBLEMS

MOD precedence.  Should MOD precedence be determined by "only equal 
                 version" rather than file order?

AUTHORS

OCS was developed by the E781 database working group - Peter Cooper (Fermilab), 
Alexander Kushnirenko (Carnegie Mellon), Antonio Morelos (San Luis Potosi), 
Mike Procario (Carnegie Mellon) and Slava Rud (Moscow State University).

VERSIONS

     V0     1-Sep-1994    Peter S. Cooper
     V1     1-Sep-1995    Slava I. Rud
     v1_2  16-Nov-1995    Slava I. Rud
     v1_3     Feb-1996    Slava I. Rud
     v1_4  15-May-1996    A. Kushnirenko   
     v2_0  06-Jun-1996    Slava I. Rud
     v2_1  06-Aug-1996    Slava I. Rud
     v2_2  16-Oct-1996    Slava I. Rud
     v2_3  05-Apr-1997    Slava I. Rud
     v2_4  31-Jul-1997    Slava I. Rud
     
________________________________________________________________________________

                          APPENDIX 1

                  EXAMPLE OF FORTRAN USAGE

Assume for this example the example tables given above.

      subroutine test_ocs(func, error_code)
c                         in    out 
c
c     Example of OCS usage
c
c     22-Jul-1996   rud   last correction
c
      Implicit NONE
*
*-------OCS  includes:
*
#include "ftabbk.inc"			! 
#include "ocs_cbk.inc"                  ! for use with OCS routines
#include "ocsbk.inc"                    !
*
*-------User includes:
*
#include "scaler_cbk.inc"               ! a constant table I own
#include "ssd_pos_cbk.inc"              ! a constant table I own
#include "mag_pos_cbk.inc"              ! a constant table I own
#include "spec_pos_cbk.inc"             ! a constant table I use

      logical   
     *          mode                    ! table mode
      
      character
     *          file*80                ,! binary  file name
     *          file_name*80           ,! scratch file name
     *          func*8                 ,! request function
     *          line*1024               ! space for key
      integer*4
     *          i,j                    ,! dummy do loop indices
     *          error_code             ,! global event error code
     *          l_cur                  ,! current line no. in open file 
     *          ifopen                 ,! file open key
     *          exp_env                 ! function of MP

      real*4
     *          A(10,10)                ! binary table array
     
      IF(func .eq. 'first') THEN        ! initialization section
        if( ocs_register(ocs_id ,       ! the id OCS will give me
     *       'test_ocs'         ,       ! my name
     *       'scaler user ssd_pos mag_pos',! the tables I own
     *       'spec_pos ') )  then       ! the tables I use but don't own
*Stop:
         print *, 'TEST_OCS: bad OCS_REGISTER result for routine '//
     *            'test_ocs. Stop'
         STOP
        endif
        
      if(ocs_debug_level.gt.0) then  !  short debug            
       print '(a18,i2)','test_ocs: ocs_id=',ocs_id
       print *,'test_ocs: ocs_new_const(ocs_id)=',ocs_new_const(ocs_id)
      end if
       
      call ocs_prin_table('spec_pos',nspec_pos,spec_pos,'test_ocs')
              
      ELSE IF(func .eq. 'init1') THEN
            if(ocs_new_const(ocs_id)) then    ! constant update section
                                        ! OCS will set this flag .true.
                                        ! whenever a table I own needs to
                                        ! be updated or a table I use
                                        ! has been updated
               do while (ocs_table(ocs_id, mode, l_cur, line))
                                        ! the OCS system will update all of
                                        ! my standard form tables and return
                                        ! to me for any user format tables
                                        ! which require updating
                   if(line(2:8).eq.'scaler') then  ! handle my 'scaler' table
                     read(ocs_unit,'(6a8)') (name_scaler(j),j=1,6) ! read in scaler names
                     l_cur = l_cur + 1      ! count each read in line 

                      i = 0
                      do while(i.gt.-1)
                         read (ocs_unit,'(i2,(a))') i,line 
                         l_cur = l_cur + 1      ! count each read in line 
                         if(i.ge.0) then
                            read(line,*)(value_scaler(j,i),j=1,5)
                         else
                            read(line,*)(summed_scaler(j),j=1,5)
                         end if
                      end do
                      
                      read (ocs_unit,*)      ! skip blank line
                      l_cur = l_cur + 1      ! count each read in line 

                   else if(line(2:5).eq.'user')then ! handle my binary 
                                                    ! 'user' table
                      read(ocs_unit,'(a)') file     ! read file's name
*     Extract file name from env. variable by function 'exp_env':                      
                      ifopen = exp_env(file,file_name) ! file --> file_name
                      if(ifopen.ne.0) then
                      print *, 'TEST_OCS: cannot get correct path to '//
     *                          file//'  file from exp_env. Return.'
                        RETURN
                      end if
	              open(unit=2, file=file_name,  ! open file
     *                     status='OLD',form='unformatted')
                      read(2)                       ! readin your data
                      read(2) A
                      close(unit=2)                 ! close file
                   end if
               end do   !  enddo 'while'
                ocs_new_const(ocs_id) = .false. ! constant update done
            end if                              ! end constant update section
         
      ELSE IF(func .eq. 'init2') THEN
                                        ! at this point all tables I own or 
                                        ! use are up to date. do all work
                                        ! require to my tables.        
        if(ocs_new_const(ocs_id)) then  ! constant update section
                                        ! OCS will set this flag .true.
                                        ! whenever a table I own needs to
                                        ! be updated or a table I use
                                        ! has been updated
*    Old linking:

          call                          ! link ssd_pos -> spec_pos:
     *         ocs_link_table(  name_spec_pos, lspec_pos, nspec_pos,
     *                           name_ssd_pos,  lssd_pos,  nssd_pos,
     *                         lkspec_ssd_pos, 1, 2)

          call ocs_prin_all('ssd_pos','test2_ocs') ! print table 'ssd_pos'

*    New linking:

          call                          ! link 'mag_pos' -> 'spec_pos'
     &        ocs_link_tables('spec_pos', name_spec_pos,
     &                         'mag_pos', name_mag_pos,
     &                   lkspec_mag_pos,  1, 2)

          call ocs_prin_all('mag_pos','test2_ocs') ! print table 'mag_pos'

          ocs_new_const(ocs_id) = .false. ! constant update done
        end if ! end constant update section

      END IF  ! function
      
      RETURN
      end
      
_______________________________________X________________________________________