Program: COLA Purpose: Control language for use with Hermes. Category: UTILITY File: cola.c Author: K.G. Begeman Keywords: NAME= Name of COLA file. The contents of this file will be interpreted by COLA according to the syntax described below. Only the name of the file should be given, the extension of the file should be .col. Note: Hermes knows about COLA files, meaning that you may specify the name of the COLA file directly on Hermes' command line. ** LIST= Make listing [NO] ? If YES the COLA makes a file with the same name as the COLA source file and extension .lis. The source and generated code will be listed. VAR= Value for variable [current value of var.] (see explanation of READ statement below) ** STOP= Stop execution of COLA [NO] ? Description: INTRODUCTION COLA is a small (but powerful) high-level control language for use with Hermes. Using COLA you can write a program to execute a sequence of commands. In such a COLA program you can use a number of facilities, like: - variables (and substitution of their values in commands) - a conditional statement (IF) - repetitive statements (FOR, WHILE, REPEAT) - input/output with Hermes console (READ, WRITE) COLA compiles a source program to some intermediate code, and, if no errors are detected, starts to execute that code. If an error is found, the line containing the error is printed, together with an explanation and compilation is stopped. To introduce the general form and appearance, an example COLA program is presented, which shows a couple of maps on TV, prints them, and copies them to another set. ! example COLA program ! date: Nov 1, 94 integer i ! loop counter integer iret ! error return integer nsubs ! number of subsets iret = RHEAD( "NGC4214", 0, "NAXIS3", nsubs ) IF iret <> 0 THEN ! something wrong WRITE "Something wrong with set NGC4214" HALT CIF READ "Subset numbers [%nsubs] ?" nsubs ! prompt user FOR i = 1 , nsubs ! for all subsets... WRITE "Processing subset %i" ! give message "VIEW INSET=NGC4214 %i CLIP=" ! display "PRINT INSET=NGC4214 %i BOX= FORMAT=" ! print IF i < (nsubs/2) THEN "COPY INSET=NGC4214 %i OUTSET=OUT1 %i" ! first half to OUT1 ELSE "COPY INSET=NGC4214 %i OUTSET=OUT2 %i" ! second half to OUT2 CIF CFOR This example shows some aspects of COLA, all of which will be discussed in detail further on. But a few points can be made immediately: - COLA makes no distinction between upper and lower case letters - layout doesn't affect the meaning of a program - comments can be added (the rest of a line, following ! is skipped, except when the ! occurs in a string) - COLA can read header items The next part of this document contains a detailed description of the features of COLA. CONSTANTS COLA knows 5 datatypes: integer 1, 0, -12, etc. real 3.14, -.01, 1e5, -2.5d-5 etc. double 3.14, -.01, 1e5, -2.5d-5 etc. logical True, False string "This is a sample string" Note that reals are single precision floats and doubles double precision floats. All calculations are done in double precision. To put a double quote (") or a backslash in a string constant put a backslash in front of it; i.e.: "A double quote (\") is obtained by typing \\\"" VARIABLES Variables which are introduced into a program are given "identifiers" by the programmer. An identifier is a name, made up from letters and digits, starting with a letter. The length of a name is physically determined by the length of a source line, although only the first 18 characters are significant. A variable may be an array. Some words are "reserved", and have a special meaning in the language. Therefore they may not be used as names for variables. The reserved words are: ABS ACOS AND ASIN ATAN ATAN2 ATOF ATOI AXUNIT BLANK CANCEL CANCELKEYS CFOR CIF CLOSES COLUMNQ COS COSH COTRANS CREATEC CTYPE CWHILE DATE DEBUG DEG DELETEC DELETEF DELETEH DELETES DELETET DIMENSION DOUBLE ELSE ELSIF ENTERED EQ ERF ERFC EXISTC EXISTF EXISTH EXISTS EXISTT EXP EXTEND FACTOR FALSE FCLOSE FOPEN FOR FREAD FUNNAME FWRITE GE GETBOX GETPOS GETSAS GETSAX GETSSS GT HALT HTYPE IF INTEGER LE LEN LGRID LIST LN LOG LOGICAL LT MOD NCOORDS NE NINT NOT OR RAD RANG RANU RCOLUMN READ REAL REPEAT RFRCOM RHEAD RIMAGE RTABLEC SEED SETDEF SIN SINH SIZEOF SQRT STORYBOARD STRING SUBSTR1 SUBSTR2 SYSTEM TABELQ TAN TANH THEN TRUE UGRID UNTIL WCOLUMN WFRCOM WHEAD WHILE WIMAGE WRITE WTABLEC Before use, variables have to be declared first. Declarations for the different types look like this: integer i,j,nsets,subsets(100) real f,pi,data(10) double d,velo(64) logical quit,okay(100) string message,ngc(100) string*10 keyw The value of a variable can be substituted in a string by prefixing the name with a percent sign (%): "The number is: %num !!" At runtime the sequence '%num' will be replaced by the actual value of the variable num. Default C's format 'g' is used. You can specify the 'f' format using the syntax %10.2\\x. Example: x = 121212.3434 write "%10.2\\x" x = 0.3434 write "%10.2x" x = 0.03734 write "%10.2x" Generates output: 121212.34 0.34 0.037 Remarks: - to put a percent sign in a string, it should be prefixed by another percent sign - to get a variable substitution followed by a letter or digit, separate them with an underscore (_). If an underscore should be printed after a variable substitution, put a second underscore after the variable name. - if a minimum field width is wanted, add the wanted width after the percent sign, i.e: "The number is: %8num !!" - if a precision is wanted, add the wanted precision after a dot (after the percent sign or the wanted minimum width), i.e: "The number is: %.7num !!", or "It is: %10.7num !!" - if an array element is to be printed, you must add an index, which may be an integer constant or a variable, i.e: "The number is: %nums(10) !!", or "It is %nums(n) !!" EXPRESSIONS COLA has three kinds of expressions: arithmetic, logical and string. Arithmetic expressions are made up from integer or real constants, variables and the arithmetic operators: + plus - minus * times / divide (DIV for integer arguments) MOD modulo function ** power For logical expressions the relational operators: = or EQ equal <> or NE not equal < or LT less than <= or LE less or equal > or GT greater than >= or GE greater or equal and the logical operators: AND, OR and NOT can be used. Example expressions: 5 2**10 num 2*(num+3) num < 10 (a LE b) OR (c > 0) ASSIGNMENT An assignment gives the value of an expression to a variable. Example: nr = 3 subset = subset + 1 subsets(1) = 0 message = "Processing set: %iset" quit = ( num > last ) TASK STATEMENT This statement is of course the most important one of COLA. It is used to start a servant task. The statement itself is just a string. Since these strings have to be passed through Hermes, they receive a special treatment: - multiple spaces are replaced by single spaces It is also possible to continue one string over more than one line. Examples: "RESET MEM=1 3 4" "VIEW INSET=%set %subset" "print inset=aurora freq 1 box= format=xxx.xx" READ STATEMENT This statement is used to read a new value for a variable from the console. The user can enter a new value, or keep the current (as default). You also have the option to specify a prompt string for the Task Status Area. Example: The statement sequence INSET = "AURORA" READ "Name of set [%INSET] ? " INSET leads to the following question at the console: - COLA Name of set [AURORA] ? COLA INSET= If you omit the promptstring, COLA will display: "Value for variable: INSET". Of course it is also possible to supply a value when starting up the program with: COLA,NAME=somename,INSET=AURORA CANCEL STATEMENT This statement cancels the previous READ, i.e. the next READ statement will prompt the user again. It is made up of the statement CANCEL followed by a variable. Example: CANCEL pos READ "Next Position" pos WRITE STATEMENT This statement writes a string to the screen (and log file). It is made up of the keyword WRITE and a string. Example: WRITE "Now processing set %set %subset" HALT STATEMENT A HALT statement immediately stops the execution of a COLA program. This can be used to stop a program if a task stops abnormally. IF STATEMENT The IF statement allows a choice between two possible statement paths: IF logical_expression THEN ... ELSIF logical_expression THEN ... ELSIF ... . . ELSE ... CIF The ELSIF's and/or ELSE parts of this statement may be omitted. FOR STATEMENT This statement can be used to execute a body of statements a (fixed) number of times. It takes the following form: FOR cv = lwb , upb , step ... CFOR The variable 'cv' is the control variable, 'lwb' and 'upb' (resp. the initial and final value) and step (the step size) are general (arithmetic) expressions. The last may be omitted, then step defaults to 1. Remarks: - although the control variable shouldn't be changed inside the loop, there's no checking against it. If it is changed, the outcome is not predictable. REPEAT and WHILE STATEMENT These two statements can be used to repeat a body of statements a (in advance unknown) number of times. The statements take the following form: WHILE logical_expression ... CWHILE and REPEAT ... UNTIL logical_expression OPTION STATEMENTS An option statement should appear before any declaration or executable statement. The options statement can be one or more of the following: CANCELKEYS Cancel all keywords that are asked by COLA's READ statement. DEBUG Print out some extra information (like the generated code) useful for debugging. FUNNAME Generate special names for the TASKs to run by COLA. The special names will be: _ STORYBOARD Do not execute the TASK statements but list them on screen and in a file with the name of the cola source file and extension .str. APPENDIX I: syntax of COLA In this syntaxis, the following meta symbols are being used: x OPTION == zero or one x x SEQUENCE == one or more x x CHAIN y == x , ( y , x ) SEQUENCE OPTION program : ( options ; declarations ; statements ) SEQ OPT . options : option SEQ OPT . option : 'CANCELKEYS' ; 'DEBUG' ; 'FUNNAME' ; 'STORYBOARD' declarations : declaration SEQ OPT . declaration : type , variable . type : 'INTEGER' ; 'REAL' ; 'DOUBLE' ; 'STRING' lengthspec OPT ; 'LOGICAL' . lengthspec : '*' , int_const . statements : statement SEQ OPT . statement : assignment ; task ; read_stat ; write_stat ; cancel_stat ; halt_stat ; if_stat ; for_stat ; while_stat ; repeat_stat . assignment : variable , '=' , expression . task : string_const . read_stat : 'READ' , string_const OPT , variable . write_stat : 'WRITE' , string_expr . cancel_stat : 'CANCEL , variable . halt_stat : 'HALT' . if_stat : 'IF' , logical_expr , 'THEN' , statements , ( 'ELSIF' , logical_expr , 'THEN' , statements ) SEQ OPT , ( 'ELSE' , statements ) OPT , 'CIF' . for_stat : 'FOR' , int_var , '=' , integer_expr , ',' , integer_expr , ( ',' , integer_expr ) OPT , statements , 'CFOR' . while_stat : 'WHILE' , logical_expr , statements , 'CWHILE' . repeat_stat : 'REPEAT' , statements , 'UNTIL' , logical_expr . expression : string_expr ; logical_expr ; arith_expr . string_expr : string_const ; string_var ; string_function . logical_expr : logical_term , ( 'OR' , logical_term ) SEQ OPT . logical_term : logical_factor , ( 'AND' , logical_factor ) SEQ OPT . logical_factor : 'NOT' , logical_factor ; '(' , logical_expression , ')' ; arith_expr , rel_op , arith_expr ; logical_var ; 'TRUE' ; 'FALSE' ; logical_function . rel_op : '=' ; '<' ; '<=' ; '<>' ; '>=' ; '>' ; 'EQ' ; 'LT' ; 'LE' ; 'NE' ; 'GE' ; 'GT' . arith_expr : arith_term , ( add_op , arith_term ) SEQ OPT . arith_term : arith_factor , ( mul_op , arith_factor ) SEQ OPT . arith_factor : add_op , arith_factor ; '(' , arith_expr , ')' ; variable ; constant ; arith_function . add_op : '+' ; '-' . mul_op : '*' ; '/' . variable : letter , ( letter ; digit ) SEQ OPT . constant : digit SEQ , ( '.' , digit SEQ OPT , ( 'E' , add_op OPT , digit SEQ ) OPT ) OPT . string_const : '"' , character SEQ OPT , '"' . logical_function : 'FUNCTIONNAME' , ( argument ) SEQ OPT . arith_function : 'FUNCTIONNAME' , ( argument ) SEQ OPT . string_function : 'FUNCTIONNAME' , ( argument ) SEQ OPT . argument : expression ; variable . APPENDIX II: functions in COLA Notation: A1, A2, A... arithmetic expression I1, I2, I... integer expression L1, L2, L... logical expression S1, S2, S... string expression r1, r2, r... real variable d1, d2, d... double precision variable i1, i2, i... integer variable s1, s2, s... string variable x1, x2, x... any variable Mathematical Functions: A = SIN( A1 ) sine of A1. A = COS( A1 ) cosine of A1. A = TAN( A1 ) tangent of A1. A = ASIN( A1 ) inverse sine of A1. A = ACOS( A1 ) inverse cosine of A1. A = ATAN( A1 ) inverse tangent of A1. A = ATAN2( A1, A2 ) inverse tangent of A1/A2. A = SINH( A1) hyperbolic sine of A1. A = COSH( A1 ) hyperbolic cosine of A1. A = TANH( A1 ) hyperbolic tangent of A1. A = EXP( A1 ) exponential function 2.71828...**A1. A = LN( A1 ) natural logarithm of A1. A = LOG( A1 ) base 10 logarithm of A1. A = SQRT( A1 ) square root of A1. A = ABS( A1 ) absolute value of A1. A = ERF( A1 ) error function of A1. A = ERFC( A1 ) complementary error function of A1. A = RAD( A1 ) converts degrees to radians, A1 in degrees. A = DEG( A1 ) converts radians to degrees, A1 in radians. A = NINT( A1 ) nearest integer to A1. A = RANU( A1, A2 ) uniform random number in range A1 - A2. A = RANG( A1, A2 ) gaussian random number with mean A1, dispersion A2. A = SEED( A1 ) sets seed A1 for random number generator. Utility Functions: A = LEN( S1 ) Length of string S1. A = SIZEOF( x1 ) Size of variable x1. A = ENTERED Number of items entered at last read. A = SETDEF( I1 ) Changes default level for read statements, returns the current default level. 0: no default, [ 1: default], 2: hidden, 4: exact number. A = ATOI( S1 ) Converts S1 to integer. A = ATOF( S1 ) Converts S1 to double. A = BLANK Returns BLANK value. S = SUBSTR1( S1, A2 ) Get field A2 from string S1. S = SUBSTR2( S1, A2, A3 ) Get A3 characters from string S1 starting at position A2. S = DATE Returns current time and date. A = FACTOR( S1, S2 ) get conversion factor from units S1 to units S2. A = LIST( I1 ) sets/gets Hermes' character output device state. The following device (I1) states are possible: 0 both terminal and log file off; 1 terminal on; 2 log file on; 3 both terminal and log file on; If an negative device state is given, the function only reports the current device state. A = SYSTEM( S1 ) issue the shell command S1. File handling: L = EXISTF( S1 ) Checks whether file S1 exists. L = DELETEF( S1 ) Deletes file S1. I = FOPEN( S1, S2 ) Opens file S1 with mode S2. Legal values for S2 are: "r" open file for reading. "w" create file for writing; discard previous contents if any. "a" append; open or create file for writing at end of file. "r+" open file for update (i.e. reading and writing). "w+" create file for update; discard previous contents if any. "a+" append; open or create file for update, writing at end. I is the file id which is non-negative and should be used for further reference to the file (i.e. in FREAD and FWRITE). If I is negative, an error occurred: -1 No more open files. -2 File could not be opened. I = FCLOSE( i1 ) Closes file with id i1. If I is non-negative, an error occurred: -1 Invalid id (i1). -2 File not open. -3 Error closing file. I = FREAD( i1, s2 ) Reads next line from file with id i1 into s2. I contains the number of characters read (excluding the new-line) or a negative error code: -1: Invalid id (i1). -2: File not open. -3: Cannot read from file. I = FWRITE( i1, S2 ) Writes a line with contents S2 to file with id i1. I contains the number of characters written (excluding the new-line) or a negative error code: -1: Invalid id (i1). -2: File not open. -3: Cannot write to file. Set, box and position parsing: A = GETSAS( S1, s2, i3 ) parses string S1 into set name (s2) and subsets coordinate words (i3). It returns the number of subsets or a negative value in case of error. See Appendix III for more information about subset coordinate words. A = GETBOX( S1, S2, A3, i4, i5 ) parses string S1 for set S2 and subset coordinate word A3 into lower and upper box grids. The results depend on the return value of GETBOX: <0 an error occurred; 0 no position found in string S1; 1 one position found, returned in i4; 2 only size of box found, returned in i5; 3 a complete box found, lower grids returned in i4, upper grids returned in i5; A = GETPOS( S1, S2, A3, d4 ) parses string S1 for set S2 and subset coordinate word A3 into grid positions returned in d4. GETPOS returns negative on error, else the number of positions decoded are returned. A = GETSSS( S1, i2, A3, s4 ) encodes set S1 and A3 subsets from i2 into string s4. GETSSS returns non-zero on error. Set and subset handling functions: L = DELETES( S1 ) Deletes set S1. L = EXISTS( S1 ) Checks for existence of set S1. A = DIMENSION( S1, A2 ) Returns dimension of subset coordinate word A2 in set S1. A = LGRID( S1, A2 ) Returns lower grid on axis number A2 of set S1. A = UGRID( S1, A2 ) Returns upper grid on axis number A2 of set S1. A = EXTEND( S1, S2, A3, A4 ) Creates or extends axis S2 of set S1 with origin A3 and size A4. A = GETSAX( S1, A2, i3 ) Gets the axis numbers in i3 from subset coordinate word A2 of set S1. A = CLOSES( S1 ) Closes set S1. Descriptor Handling Functions: A = DELETEH( S1, I2, S3 ) Removes descriptor S3 from subset level I2 of set S1. A = EXISTH( S1, I2, S3 ) Checks existence of descriptor item S3 on subset level I2 of set S1. If present, the subset level on which S3 is found is returned. A = HTYPE( S1, I2, S3, s4 ) Returns type of descriptor S3 on subset level I2 of set S1 in s4. Type can be: CHAR, INT, LOG, REAL, DBLE. A = RHEAD( S1, I2, S3, x4 ) Reads descriptor S3 on subset level I2 of set S1 into x4. A = WHEAD( S1, I2, S3, x4 ) Writes descriptor S3 on subset level I2 of set S1 from x4. A = WFRCOM( S1, I2, S3, S4 ) Writes comments from S3 to FITS descriptor S3 on subset level I2 of set S1. A = RFRCOM( S1, I2, S3, S4 ) Reads comments in S3 from FITS descriptor S3 on subset level I2 of set S1. Image Handling Functions: A = RIMAGE( S1, I2, i3, i4, r5 ) Reads image data from subset level I2 of set S1 from lower grids i3 to upper grids i4 into r5. A = WIMAGE( S1, I2, i3, i4, r5 ) Writes image data to subset level I2 of set S1 from lower grids i3 to upper grids i4 from r5. Table handling Functions: A = DELETET( S1, I2, S3 ) Removes table S3 on subset level I2 of set S1. A = EXISTT( S1, I2, S3 ) Checks existence of table S3 on subset level I2 of set S1. Returns number of columns in table S3. A = TABLEQ( S1, I2, S3, s4 ) Stores all columns of table S3 on subset level I2 of set S1 in s4. It returns the number of columns found. A = RTABLEC( S1, I2, S3, s4 ) Reads comments into s4 from table S3 on subset level I2 of set S1. A = WTABLEC( S1, I2, S3, s4 ) Writes comments from s4 to table S3 on subset level I2 of set S1. A = DELETEC( S1, I2, S3, S4 ) Removes column S4 from table S3 on subset level I2 of set S1. A = EXISTC( S1, I2, S3, S4 ) Returns number of rows in Column S4, Table S3, at subset level I2 of set S1. A = CREATEC( S1, I2, S3, S4, S5, S6, S7 ) Creates column S4 in table S3 on subset level I2 of set S1 with type S5, comments S6 and units S7. See gdsa_crecol.dc2. A = CTYPE( S1, I2, S3, S4, s5 ) Returns type of column S4 of table S3 on subset level I2 of set S1 in s5. Type can be: CHAR*I, INT, LOG, REAL, DBLE. A = COLUMNQ( S1, I2, S3, S4, s5, s6, s7 ) Gets info about column S4 of table S3 on subset level I2 of set S1. In returns the number of rows in the column, and in s5 the column type, in s6 the comments and in s7 the column units. See gdsa_colinq.dc2. A = RCOLUMN( S1, I2, S3, S4, x5, A6, A7 ) Reads data into x5 from column S4 of table S3 on subset level I2 of set S1. Start row is in A6, number of rows in A7. A = WCOLUMN( S1, I2, S3, S4, x5, A6, A7 ) Writes data from x5 to column S4 of table S3 on subset level I2 of set S1. Start row is in A6, number of rows in A7. If A6 is zero, appends data to the end of the column. Coordinate Handling Functions: A = COTRANS( S1, I2, d3, d4, I5 ) Does a transformation from grid coordinates to physical coordinates and vice versa. If I5 is zero d3 should contain the physical coordinates on subset level I2 of set S1, and d4 will contain the grids. If I5 is not zero d3 should contain the grid coordinates on subset level I2 of set S1, and d4 will contain the physical coordinates. Note that d4 contains all coordinates (i.e. including the subset coordinates and hidden coordinates). The first coordinate in d4 refers to the first axis, the second to the second axis etc. Use NCOORDS to find how many coordinates are returned in d4. See cotrans.dc2. A = AXUNIT( S1, A2, s3 ) Returns in s3 the type of physical units used by COTRANS for axis number A2 of set S1. See axunit.dc2. A = NCOORDS( S1 ) Returns the number of coordinates returned by COTRANS when used on set S1. See ncoords.dc2. APPENDIX III: Subset coordinate words. A subset coordinate word is a non-negative integer number which denotes a unique sub-structure of a GIPSY data set, a so called subset. It can be any pixel, any line or plane or the whole structure (set). The whole set is designated by a coordinate word which is zero. The following example should explain the use of subset coordinate words: ! example COLA program ! date: Nov 2, 94 string inset, set, file, line, bunit integer subsets(1000), nsub, n, ier, fd, dstat, nblank real datamax(1000), datamin(1000) real amax, amin logical plot ! message for the user write "Cola script to create a table out of the minimum and maximum of subsets" ! get the input set and subsets from the user repeat read "Enter Set and subsets" inset ! decode into gds subset levels nsub = getsas( inset, set, subsets ) if ( nsub < 0 ) then write "something wrong with your input set and subsets" ! cancel inset to be asked again cancel inset cif until ( nsub > 0 ) ! get the units of the data if ( rhead( set, 0, "BUNIT", bunit ) <> 0 ) then bunit = "?" cif ! set to blank amin = blank amax = blank ! loop over all subsets to get DATAMIN and DATAMAX for n = 1, nsub ier = rhead( set, subsets(n), "NBLANK", nblank ) if ( ier <> subsets(n) ) then ier = getsss( set, subsets(n), 1, inset ) dstat = list( 0 ) "mnmx inset=%inset" dstat = list( dstat ) cif ier = rhead( set, subsets(n), "DATAMIN", datamin(n) ) if ( ier < 0 ) then datamin(n) = blank else ier = rhead( set, subsets(n), "DATAMAX", datamax(n) ) if ( ier < 0 ) then datamax(n) = blank cif cif if ( amin = blank and datamin(n) <> blank ) then amin = datamin(n) amax = datamax(n) elsif ( datamin(n) <> blank ) then if ( amin > datamin(n) ) then amin = datamin(n) cif if ( amax < datamax(n) ) then amax = datamax(n) cif cif cfor write "%inset Minimum = %amin Maximum = %amax" if ( existt( set, 0, "EXTREMA" ) > 0 ) then ! write "Removing table EXTREMA" ier = deletet( set, 0, "EXTREMA" ) write "table EXTREMA already present, so we deleted it" cif ier = wtablec( set, 0, "EXTREMA", "DATAMIN and DATAMAX for %inset" ) ier = createc( set, 0, "EXTREMA", "DATAMIN", "REAL", bunit, "minimum" ) ier = wcolumn( set, 0, "EXTREMA", "DATAMIN", datamin, 1, nsub ) ier = createc( set, 0, "EXTREMA", "DATAMAX", "REAL", bunit, "maximum" ) ier = wcolumn( set, 0, "EXTREMA", "DATAMAX", datamax, 1, nsub ) plot = true read "Plot datamin [%plot]?" plot cancel plot if ( plot ) then dstat = list( 0 ) "table inset=%set option=6;1 grdevice=x11 ytabcol=EXTREMA DATAMIN; xtabcol= xvalues=1:%nsub connect=N eytabcol= header= comment=" dstat = list( dstat ) cif read "Plot datamax [%plot]?" plot cancel plot if ( plot ) then dstat = list( 0 ) "table inset=%set option=6;1 grdevice=x11 ytabcol=EXTREMA DATAMAX; xtabcol= xvalues=1:%nsub connect=N eytabcol= header= comment=" dstat = list( dstat ) cif ! The End Updates: Feb 26, 1991: KGB Document created. Nov 4, 1991: KGB RPK Bug repaired. Sep 1, 1993: KGB Bug in type conversion repaired. Jan 21, 1994: KGB New code, many enhancements. Nov 3, 1994: KGB Version 3.0. Dec 23, 1994: KGB Function setdef implemented. May 4, 1995: KGB Bug in prompting for characters strings repaired. Jun 16, 1995: KGB Added an undocumented feature. Nov 15, 1995: KGB Bug in DELETES destroyed. Dec 8, 1995: KGB Dynamical allocation of all internal tables. Dec 21, 1995: KGB Added functions RFRCOM and WFRCOM. Jan 11, 1996: KGB Added function CLOSES. Feb 6, 2006: JPT Bug in PopReal repaired.