Processing math: 100%

5 Using GRP Routines

 5.1 Symbolic Constants and Status Values
 5.2 Creating and Deleting Groups
 5.3 Storing Names in a Group
 5.4 Retreiving Names from a Group
 5.5 Retrieving Attributes of Names
 5.6 Deleting Names
 5.7 Changing the Characters Used to Control the Syntax of Group Expressions
 5.8 Creating Associations Between Groups

Many of the points described below are illustrated by the example application in section 1.2. Further details can be found in the subroutine specifications in appendix C.

5.1 Symbolic Constants and Status Values

The GRP package has associated with it various symbolic constants defining such things as the required length of various character variables, an invalid GRP identifier value, etc. These values consist of a name of up to 5 characters prefixed by “GRP__” (note the double underscore), and can be made available to an application by including the line:

  INCLUDE ’GRP_PAR’

Another set of symbolic constants is made available by the statement:

  INCLUDE ’GRP_ERR’

These values have the same format of those contained in GRP_PAR, but define error conditions which can be generated within the GRP package. Applications can compare the STATUS argument with these values to check for specific error conditions. These values are described in appendix D.

5.2 Creating and Deleting Groups

New groups can be created by calling GRP_NEW which returns an identifier for the new group. The group is initially empty but names can be added immediately using any of the methods described in section 5.3. The calling application must provide a “type” for the group when calling GRP_NEW. This type is not used directly by the GRP system, but is provided as a means for applications to differentiate between different types of groups (for instance, groups holding file names and groups holding names of astronomical objects). No restrictions are placed on the strings which can be used for group types. The type of a group can be recalled at any time using routine GRP_GTYPE, and a group can be given a new type by calling GRP_PTYPE.

New groups are also created by the routines GRP_COPY, GRP_REMOV and GRP_PURGE, but they differ from GRP_NEW in that the created group is based on a previously existing group. GRP_COPY creates a new group containing a copy of a subsection of an old group, GRP_REMOV creates a new group containing a copy of the whole of an old group, but excluding all occurrences of a given name, and GRP_PURGE creates a new group containing a purged copy of the whole of an old group (i.e. there are no duplicate entries in the new group). The new groups inherit the type string, control characters and case sensitivity of the old groups, but do not inherit any other attributes (such as owner/slave relationships - see section 5.8).

The routine GRP_DELET deletes a group, thus freeing the GRP identifier and the internal resources used by the group. As well as deleting the group identified by the specified GRP identifier, it also deletes any groups associated with the specified group (by means of an owner-slave relationship as described in section 5.8).

To avoid running out of group identifiers, applications should always delete all groups which they have created (using GRP_DELET) before terminating, even if an error status exists.

5.3 Storing Names in a Group

There are several routines which can be used to store names in a group.

The routine GRP_PUT stores a set of names starting at a given index within a group. Any previous names with the same indices are over-written, and the group is extended as necessary. Note, an application must supply the literal names to be stored, as modification and indirection elements cannot be used with GRP_PUT. For instance, if the string “^LIST.DAT” was stored in a group using GRP_PUT, the ^character would not cause names to be read from the file LIST.DAT. Instead the string would be stored in the group exactly as supplied.

The routine GRP_PUT1 is like GRP_PUT except that it stores just a single name, and so has a simpler interface.

The routine GRP_GROUP obtains a group expression from the environment using a specified parameter and expands it into a list of literal names. These names are appended to the end of a group, which must previously have been created. An existing group may be specified as the basis group for any modification elements included in the group expression. If such a group is not supplied, all elements are stored literally as names. GRP_GROUP returns the total number of names in the group, together with the number of names which it has added. It also returns a logical argument indicating if the group expression was “flagged” (see section 4.6).

The routine GRP_GRPEX performs the same function as GRP_GROUP except that the group expression is supplied as an argument instead of being obtained through the parameter system. This routine can thus be used in stand-alone applications.

The following code fragment shows an example of the use of GRP_GRPEX and GRP_PUT. It is assumed that the text file FRIENDS.LIS exists and contains the single record “FRED,BERT”.

        INTEGER IGRP, STATUS, SIZE, ADDED
        LOGICAL FLAG
        CHARACTER * ( GRP__SZNAM ) NAMES( 3 )
  
        ...
  
        CALL GRP_NEW( ’A test group’, IGRP, STATUS )
  
        CALL GRP_GRPEX( ’^FRIENDS.LIS’, GRP__NOID, IGRP, SIZE, ADDED, FLAG,
       :                STATUS )
  
        CALL GRP_GRPEX( ’HAROLD’, GRP__NOID, IGRP, SIZE, ADDED, FLAG,
       :                STATUS )
        ...
  
        NAMES( 1 ) = ’TOM’
        NAMES( 2 ) = ’DICK’
        NAMES( 3 ) = ’HARRY’
  
        ...
  
        CALL GRP_PUT( IGRP, 3, NAMES, 2, STATUS )

The call to GRP_NEW creates a new, empty group. The first call to GRP_GRPEX read the two names from the file FRIENDS.LIS and stores them with the following indices:

  1 - FRED
  2 - BERT

Both SIZE and ADDED are returned equal to two. The second call to GRP_GRPEX appends the name HAROLD to the group, so that the group becomes:

  1 - FRED
  2 - BERT
  3 - HAROLD

SIZE is returned equal to 3 and ADDED equal to 1. The call to GRP_PUT then stores the name TOM at index 2 (over-writing BERT), and DICK at index 3 (over-writing HAROLD). The group is then extended and HARRY is stored at index 4 so that the group becomes:

  1 - FRED
  2 - TOM
  3 - DICK
  4 - HARRY

5.4 Retreiving Names from a Group

GRP_GET will retrieve a set of names from a group starting at a given index. An error is reported if a range of indices extending beyond the current size of the group is specified. If a character variable which receives a name has a declared length shorter than the stored name, then the name is truncated to the length of the character variable, but no error is reported.

A group can be searched for a specified name by calling GRP_INDEX, which returns the index of the name within the specified group. If the name does not exist within the group, the returned index is set to zero but no error is reported.

The routines GRP_LIST and GRP_LISTF each create a text file containing a subset of the names stored in a specified group. The names are written one per record, and a comment is stored as the first record. The difference between GRP_LIST and GRP_LISTF is that the former obtains the name of the text file to be created through the parameter system, whereas the latter requires the calling application to provide it. These routines are useful for passing groups of names between successive applications. For instance, the first application may create a text file holding a group of names using GRP_LIST, and the second application may then call GRP_GROUP, allowing the file created by the previous application to be specified within an indirection element.

A single name can also be retrieved from a group using routine GRP_INFOC, giving the value “NAME” for the argument ITEM (see below).

5.5 Retrieving Attributes of Names

Each name has associated with it various attributes, which can be retrieved using GRP_INFOC (for attributes which take character values) and GRP_INFOI (for attributes which take integer values). In both cases, the particular attribute required is specified by the argument ITEM. Attributes of a name are carried round with the name if it is copied using GRP_COPY, GRP_REMOV or GRP_PURGE. The following attributes are currently used:

MODGRP
- An integer which has the value GRP__NOID unless the name was specified using a modification element. Otherwise, it is the identifier for the group which was used as the basis for the modification element (see section 4.4).
MODIND
- An integer value which is zero unless the name was specified using a modification element. Otherwise, it is the index of the name within the basis group (i.e. the group identified by the MODGRP attribute) which was modified in order to generate the specified name.
DEPTH
- An integer value which is zero unless the name was specified using an indirection element. Otherwise, it is the number of levels of indirection at which the literal name was specified. For instance, consider the case where a prompt issued by GRP_GROUP is responded to with the group expression

  ^FILE1.DAT

and the file FILE1.DAT contains the single record “NAME1,^FILE2.DAT”, and the file FILE2.DAT contained the single record “NAME2”. The DEPTH attribute would have a value of 1 for name NAME1 indicating that the name was given within a file specified directly within the group expression, and a value of 2 for name NAME2 indicating that the name was given within a file which was itself given within another file.

FILE
- A character value which is returned blank unless the name was specified using an indirection element. Otherwise, it is the name of the file in which the name was literally stored. Thus, in the above example it would be FILE1.DAT for name NAME1 and FILE2.DAT for NAME2.
NAME
- A character value equal to the name itself.

5.6 Deleting Names

Names can be deleted from a group using GRP_REMOV, which deletes all occurrences of a specified name within a group, creating a new group to hold the results (the input group is left unchanged).

Names with a specified range of indices can be deleted from a group using GRP_COPY, as in the following example:

  CALL GRP_COPY( IGRP, 3, 5, .TRUE., ITEMP, STATUS )
  CALL GRP_DELET( IGRP, STATUS )
  IGRP = ITEMP

The call to GRP_COPY creates a copy of the group identified by IGRP, excluding the names with indices between 3 and 5. The copy is stored in a new group, the identifier for which is returned in ITEMP. The names which used to have indices greater than 5 are shuffled down to fill up the gap (name 6 in the old group becomes name 3 in the new group, etc). The above example then deletes the original group, and copies the new group identifier into the variable previously used to store the identifier to the old group.

Names can also be deleted using GRP_SETSZ which reduces the size of a group. Thus, if a group with size of 6 is reduced to a size of 4, then the names with indices 5 and 6 are deleted.

Routine GRP_PURGE deletes duplicate names within a group, creating a new group to hold the results.

5.7 Changing the Characters Used to Control the Syntax of Group Expressions

A set of “control characters” are used to indicate various items of syntax within a group expression. Each group is created with a set of default control characters, but these can be changed by subsequent calls to GRP_SETCC. Each group has its own set of control characters which can be different to those of other groups, and they can be inspected using routine GRP_GETCC. When calling these routines, specific control characters are represented by the following names:

INDIRECTION
- The character used to indicate that the string which follows is not a literal name but is the name of a text file from which further names are to be read. The INDIRECTION control character defaults to “^”.
COMMENT
- The character used to indicate that the remainder of the group expression (or record within a text file) is a comment and is to be ignored. The COMMENT control character defaults to “#”.
DELIMITER
- The character used to delimit elements within a group expression. The DELIMITER control character defaults to “,”.
NAME_TOKEN
- The character used within a modification element as a token for each name in the basis group. The NAME_TOKEN character defaults to “”.
SEPARATOR
- The character used to separate old and new substitution strings when editing a group of names. The SEPARATOR character defaults to “”.
OPEN_NEST
- The character used to open a nest within a group expression, within which any DELIMITER characters are ignored. The OPEN_NEST character defaults to “(”.
CLOSE_NEST
- The character used to close a nest within a group expression, within which any DELIMITER characters are ignored. The CLOSE_NEST character defaults to “)”.
FLAG
- The character which can be used to flag selected group expressions by appending it to the end of the group expression. The presence of the flag is communicated to the calling application by means of the FLAG argument of routines GRP_GROUP and GRP_GRPEX. The FLAG character defaults to “-”.
OPEN_KERNEL
- The character used to open a kernel within an element. The OPEN_KERNEL character defaults to “{”.
CLOSE_KERNEL
- The character used to close a kernel within an element. The CLOSE_KERNEL character defaults to “}”.
NULL
- This is a character which can be assigned to any other control character to suppress use of that control character (see below). The NULL character defaults to “%”.
ESCAPE
- This is a character which can be used to “escape” another control character within a group expression. If a GRP control character is encountered within a group expression, its special meaning is ignored and it is treated literally if it is preceeded by an escape character. Such escape characters are removed before storing the corresponding strings in the returned group (escape character which do not preceed another control character are not removed). Escape characters can themselves be escaped. By default, the escape character is equal to the null character (i.e. escaping is not available by default).
5.7.1 Suppressing the Use of Selected Control Characters

Applications may sometimes want to suppress the use of certain control characters. For instance, an application which stores lines of text within a group may want to suppress the division of each group expression into separate elements, so that the whole group expression is stored as a single name within the group. One way to do this would be to set the DELIMITER control character to a character which is guaranteed not to occur within the supplied text. However, such a character may not exist. A more flexible approach is to assign the value of the NULL control character to the DELIMITER control character. For instance, if the NULL and DELIMITER characters are both set to “%”, then the group expression is not split up into elements delimited by “%” signs. Note, any “%” signs in the group expression will be included in the name stored in the group without any change. The NULL character can be assigned to any of the other control characters to suppress the use of those control character. The only restriction is that if one of the two control characters OPEN_NEST and CLOSE_NEST is assigned the NULL value, then the other one must also be assigned the NULL value. The same restriction applies to the OPEN_KERNEL and CLOSE_KERNEL control characters.

5.8 Creating Associations Between Groups

Each group may be “owned” by another group. This idea of ownership may be used to establish associations between groups. Many groups may be associated together into a sort of “super-group”, or group of groups, by making one group the owner of another, which in turn is the owner of another, and so on. These associations are provided for the benefit of the calling application, which can impose any meaning it likes on the associations. The only significance of these associations within the GRP package is that when a group is deleted (using GRP_DELET), all other groups in the same “super-group” (i.e. all groups which have an association, either directly, or indirectly, with the specified group) are also deleted.

An example of the use of these associations is to provide a means of attaching supplemental information to a group describing the global properties of the group. For instance, consider an application which uses groups to store information about spiral galaxies. Group A may contain the names 1232, 628, 5364, and group B may contain 81, 31. It may be necessary to store supplemental information describing each of these groups, for instance the catalogue to which the numbers in groups A and B refer, the morphological type, and so. A new group C could be created holding the names NGC and ScI, giving the catalogue and type of the galaxies in group A. This group could then be associated with group A. Similarly a group D could be created holding the names Messier and Sb, and associated with group B.

Four routines are included in the GRP package for handling these associations; GRP_SOWN establishes one group as owner of another, GRP_OWN returns the identifier of the group which owns a specified group, GRP_SLAVE returns the identifier of the group which is owned by (i.e. is a slave of) a specified group, and GRP_HEAD returns the group at the head of an owner-slave chain. Groups are “free” (i.e. are not owned by any other group) when they are created. A group which has had an owner established for it can be made free again by calling GRP_SOWN giving a null group (GRP__NOID) as the “owner”. A group may not be owned by more than one group, and may itself own a maximum of one group. It is thus possible to establish “chains” of owner-slave associations (but not “tree structures”). All the groups in such a chain form a “super-group” of inter-dependant groups. When an attempt is made to delete any group within such a super-group, all the other groups in the same super-group are also deleted whether higher or lower in the chain.

The owner-slave relationship is strictly one way; a group cannot own its own owner, or equivalently be a slave of its own slave. If an attempt is made to set up such an association using GRP_SOWN, then an error is reported, and STATUS is returned set to GRP__SLAVE. Thus if group A is owned by group B, and group B is owned by group C, group C may not be owned by either group A or group B.