The Mumps Compiler Programmer's Guide
Kevin C.
O'Kane, Ph.D.
Computer Science Department
University of Northern Iowa
Cedar
Falls, IA 50614
okane@cs.uni.edu
http://www.cs.uni.edu/~okane
(319) 273
7322
October 15, 2000
Mumps Compiler Manual Kevin C. O'Kane This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
This document describes the implementation of the Mumps compiler for use with Windows 95/98, Windows NT, Solaris and Linux. Solaris is a trademark of Sun Microsystems. Windows 95/98 and Windows NT are trademarks of Microsoft Corporation; The material herein is preliminary in nature and subject to change. Use of the software described here is at the user's risk. Please send error reports the the e-mail address above.
Contents |
|
The purpose of this document is to present an overview of the Mumps Compiler implementation of the Mumps language. This package consists of compilers and support libraries for various operating systems for a dialect of the Mumps language. Usage of the Mumps compilers is entirely at the user's risk. The Mumps software packages described herein are not warranted in any manner whatsoever. The author disclaims responsibility for any and all damages, either direct or consequential, which may arise through use of these packages.
Copy the compiler program to a directory in your working path. Copy the cgi.h and fcns.h files to the directory from which you will compile. To compile a program, type:
mumps.compiler progname.mps
where mumps.compiler is the name of the compiler program. You may rename the compiler, for example, compiler.exe for DOS based systems. The Mumps compiler translates Mumps source code to C source code. The output of the compiler will be a program named progname.c. You may compile this with any C compiler. Your C compiler will need access to cgi.h and fcns.h when compiling the translated Mumps program In some cases, DOS C compilers lack some service functions found in all Unix and most other DOS environments. You will need to supply these if they are missing.
Before compiling, you should check the preprocessor defined variables in fcns.h. These need to be set for your operating system and for other options. The file fcns.h contains operating system and environment specific settings in preprocessor defined variables located near the beginning of the file. Each contains a comment as to its optional settings. These should be set before compiling the program.
For Linux, the following script is useful:
mumps.compiler $1.mps if [ $? = 0 ] then echo Compiling generated C code... gcc -o $1.cgi $1.c -lm -lpq echo ----------------------------- else echo C compilation surpressed. echo ------------------------- fi |
The script assumes that your Mumps source program has a .mps extension. The -lm option is always required. The -lpq and -lpwdb are needed only if you want PostgreSQL support (-lpwdb may not be required with more recent distributions of PostgreSQL). Note that you do NOT use the .mps file extension when invoking this script.
Global arrays can be either permanent or temporary. If temporary, they are created when a translated Mumps program first accesses them and destroyed upon program termination. The global arrays are temporary if the NEW_TREE preprocessor variable is set to 1 in fcns.h. Temporary global arrays are useful if your data are loaded each time you run your program or if your permanent data base storage resides on server. Tmporary global file names are constructed with a value baesd on the program's process id.
Permanent global arrays continue to exist after a program terminates. Global arrays will be permanent if the NEW_TREE preprocessor variable in fcns.h is set to 0.
Global arrays reside in two files. The names of these files can be set at compile time or run time by altering the values in the C variables cfgdata and cfgkey. The filenames in these, including path names, will be used to open the global array files. The default names are are data.dat and key.dat. The default names are in fcns.h in the defined variables UDAT and UKEY.
The following is an example of how to change the file names at run time:
Zmain
Zd
strcpy(cfgdata,"mydata.dat")
Zd
strcpy(cfgkey,"mykey.dat")
Set ^a(1)="test"
.
.
.
The C function strcpy is used to change the values in the C variables cfgdata and cfgkey. It must be used before the first reference to a global array.
For Mumps programs, the global arrays are initialized only if they do not exist and you either explicitly open them or attempt to access them for the first time. Compiled programs that neither reference nor open global arrays will not create or initialize them.
Global array references may contain either string or non-negative numeric subscripts. Only printable ASCII characters are permitted as values for global array subscripts. No subscript may consist of a negative number. The global array files will grow in size as elements are added. These files may be copied for backups. Numeric subscripts are stored in character collating sequence order unless the ZNumeric command is given first (in which case, leading blanks are prepended to all numbers to form a field of length eight). See below for complete details. The $Order function does not work at present, only the older $Next.
To use a Mumps program with a web server, compile the Mumps program with the cgi.h header file. To do this, place the following line after the ZMain:
Zd #include "cgi.h"
If cgi.h is located in some other directory than the one in which you are compiling, include appropriate path information. The zd command passes the "#include" command to the C program being constructed. The header file contains the code necessary to access the Web server QUERY_STRING environment variable.
Place the Mumps program in the server's cgi-bin (or other appropriate directory). From HTML documents, you reference Mumps programs with lines of the form:
<A HREF="/cgi-bin/yourprogram.cgi?var1=11111&var2=123"> test </A>
Here, the name of the compiled (binary) Mumps program is taken to be yourprogram.cgi but in some systems the file extension may need to be different. Check your web server documentation to determine if executable files require a specific extension other than .cgi. An executable Mumps program must be given adequate permissions to be executed by the web server. Also, the web server must have permission to read and write files in the directory in which the global arrays are placed. Incorrect permissions and file ownership will result in failure. Temporary global arrays are placed in the directory that contains the program being executed. The web server must have permission to write files in this directory, too.
Upon initialization of the Mumps compiled program (if the cgi.h file was included during compilation), the variables appearing in the HREF (var1 and var2 in the above) will exist in the Mumps symbol table and have the values provided by the browser (11111 and 123, in this case). The web server extracts the parameters from the HREF and places them in an operating system environment variable named QUERY_STRING. The software in cgi.h looks for QUERY_STRING and decodes it (non alphabetic and non- numeric values are encoded). It takes each parm=value expression and creates a Mumps variable named parm and sets its value to value. If cgi.h is omitted, you will not have access to web server passed values. The total length of the parameter string in QUERY_STRING may not exceed 1024 bytes. The full value of QUERY_STRING is also contained in the Mumps variable %QS during execution. The executing Mumps program may determine that is was invoked by the web server by testing for the existence of %QS. However, you may invoke a Mumps program in simulated Web server mode by calling it from a shell script such as the following (for the Linux Bash Shell):
#!/bin/bash QUERY_STRING="&var1=value1&var2=value2" export QUERY_STRING yourprogram.cgi unset QUERY_STRING Here, the Mumps program will execute with parameters var1 and var2 instantiated as variables upon entry into execution.
If the values of the variables passed through QUERY_STRING need to contain blanks or special characters, they must be encoded in the manner prescribed by the HTML standard. From a Mumps program, the Mumps built-in function $zhtml can be used for this purpose (see below).
Be certain to Halt at the end of a program in order to terminate the program. If you do not correctly terminate the Mumps program, the web server may hang.
When using temporary global arrays, the global array names are constructed from the process id and are therefore unique. For programs with permanent globals, Mumps compiled programs insure that only one program is running at any given time on any given database files. Mumps programs open the database files for exclusive access. Thus, Mumps programs should be short, transaction oriented jobs that do not delay the system. When a program attempts to execute and the files it wants are in use, it waits. Note: since only one copy of the Mumps is ever running for a given database, all file accesses are also exclusive and thus the Lock command has no meaning.
When running with a web server, it is critical that your Mumps program have sufficient file access permissions to be executed by the web server. Further, the globalarray files must also be read/write accessible to the web server. If either your program or your files do not have correct file access permissions, it will fail.
If you halt a compiled program with a control-C or other external Kill command, the global array data base may be corrupted. For temporary files, this means that the temporary files may not be correctly deleted. For permanent globals, the files may be corrupt. Back-up copies of critical data should be maintained. A dump function exists that copies the global arrays to an ASCII text file which can be used to reload the data base. Generally speaking, programs that do not access the globals or access them only to read their contents, do not corrupt the global arrays if forcibly terminated.
Mumps programs may be created with any standard system editor which does not introduce embedded control codes into the program. Be careful here. Your programs must be ordinary ASCII files with the following mandatory conventions:
If a command line does not contain a label, the first character is a TAB character. Important: Mumps command lines must use the tab character. If a line begins with a pound sign (#), the line is treated as a comment. For the Mumps compiler, if a line begins with a percent sign (%), the line is treated as inline C code and passed as-is to the C compiler. If the line begins with any other character and has no TAB character, it is assumed to be HTML code and it is written to standard output after expansion at run time of any embedded Mumps expressions.
If the line contains a label, the label is followed immediately by a TAB character followed by the program text. Labels may exist alone on lines but the MUST have a TAB character.
Each line is terminated by a line feed.
No line may exceed 255 characters in length.
The program should not exceed the available user Mumps buffer space presently set at 32,000 bytes. That is, the source code for the program should not, at present, exceed 32,000 bytes.
If a line contains no TAB characters, it will be determined to be HTML code and written directly to the standard out, after substitution of Mumps expressions and new line characters. (see below)
If a line determined to be HTML code contains an ampersand followed immediately by an exclamation point, a line feed will be inserted into the output line in its place.
If a line determined to HTML code contains an ampersand followed immediately by a tilde (~) followed by a valid Mumps expression, followed by a tilde character, the enclosed expression will be evaluated and executed at run time and the resulting value of the expression will be substituted in the output line. Please Note: the closing tilde is required or erratic results will occur as the compiler attempts to parse the HTML code. You have been warned.
If a line begins with a percent sign, the code is sent to the C program output as inline C code. See compiler notes.
The default IO unit number is 5. Unit 5 refers to the console terminal (usually a CRT) for both input and output. Units 1 through 10 may be used by the user for sequential file structures. Unit 5 may not be opened. An attempt to do so will result in an error message. Units 1 through 10 require a file name after the unit number expression in the Open statement. The unit number expression must be followed by a colon and the file name must follow the colon. The file name may be represented as a variable or literal constant. Literal constant file names are enclosed in quotation marks ("). The file name must be followed, either within the quotes if given as a literal or as part of the value if given as a variable, by either ",OLD" or ",NEW". This parameter indicates whether the file is to be created (output implied) or read (input implied). For example:
OPEN 1:"MYFILE.DAT,NEW"
OPEN
2:"TABLE.DAT,OLD"
In the first of these, the file will
be created and only output (WRITE) operations are permitted. In the
second, the file is presumed to exist and only input operations will
be permitted. If the NEW option is given, any previous generation of
the file is deleted.
During READ operations to units
one through ten, the end of file condition is signaled by the return
of a null string and the setting of $TEST to zero when the input is
exhausted. A READ operation which is successful sets the $TEST
variable to one.
The ASCII NUL (0) character may
not be used. Usage will result in parser error. No ASCII character
of value greater than 200 may be generated or used.
No string, program line or
intermediate result may exceed 255 characters in length. An attempt
to create a string of length greater than 255 may result in
truncation without an error message. The user should also note that
when functions are evaluated, the length of the function is the
length of its name, parentheses, commas and arguments after
evaluation. Global array references (keys) can be up to 255
characters long.
The name of the variable used in a
"setpiece" function must be 63 or fewer characters in
length.
Numbers may not contain more than
20 characters including leading zeros, plus and minus signs, and
decimal points. Numeric quantities are stored internally as double
variables. Some round off may occur in certain operations in the
usual manner.
The $ORDER function is not
implemented. $NEXT presents numeric subscripts in character, not
numeric, collating sequence except if you use the ZNumber command
(see below).
The naked indicator is not implemented.
1 Multiple adjacent operators
2 Unmatched quotes
3 Global
not found
4 Missing comma
5 Argument not permitted
6 Bad
character after post-conditional
7 Invalid quote
8 Label not
found
9 Too many/few fcn arguments
10 Invalid number
11
Missing operator
12 Unrecognized operator
13 Keyword
14
Argument list
15 Divide by zero
16 Invalid expression
17
Variable not found
18 Invalid reference
19 Logical table
space overflow
23 Symbol table full
24 Function argument
error
25 Global not permitted
26 File error
27 $N error
29 <break> at line:
30 Function not found
31
Program space exceeded
32 Stack overflow
Compiler Implementation Overview
This compiler does not implement the full 1995 Mumps (M) standard. It implements many of the features of the 1995 standard with both omissions and extensions. Work is on-going so the list of features will grow.
The compiled modules execute between 1.5 and 6 times faster than the same code executing on an interpreter. The lower multiplier reflects benchmark programs which are very global array intensive while the higher multiple is for programs that are less global array intensive.
The C code generated by the compiler has comments which are the original Mumps program's line and line number. The compiler also interjects its own comments in places to explain what it is doing. The C code should be fairly easy to follow. Naturally, you may alter the C code to add your own features.
This effort arose out of work to use Mumps as a server-side web scripting language. For this purpose, it is amazingly well suited (see the patient record system examples). To work in in the web server environment, changes and extensions were made on an interpreter written by this author in the early 80's. One set of changes involved the ability to extract from the server environment data passed from browser forms. These data, in the form of name=value pairs, partly encoded, are automatically translated by the interpreter to Mumps variables with initialized values. Also, the need was recognized to be able to embed HTML code in Mumps programs and have the interpreter scan the HTML and substitute any expressions found in it with their results. Example: < title > &~^patient(ptid)~ < /title >
Here, the global array reference is replaced by its value. The &~ and ~ are delimiters. This works very well.
The key to this was the old Mumps interpreter which was very small (about 60,000 bytes under Linux) and quite efficient. It was a single user environment and had no major O/S issues inhibiting its interface. Since web transactions are very brief, serial locking of the data base works very well. A data base stand-alone daemon was written which took queries from a web server invoked database-less version of interpreter but it was actually slower and had many more integrity and security issues. The serial lock version works better. Tests show that a small, quick transaction oriented interpreter works very well . Code was also added to permit global arrays to be stored and loaded from a SQL server. This uses the ODBC interface and has been checked out with a Sybase SQL server under WinXXXX. This frees Mumps from one of its current liabilities - the inability to interface with enterprise data base servers.
In order to improve the performance even more, it was decided in November of 1998 to try compiling Mumps by modifying the Mumps interpreter written by this author to generate C code and then compiling the C code to binary.
One advantage of full compilation is inter-operability with other languages and with the host operating system. This is achieved in by compiling to C which has full access to all system features.
New documentation is being added as the project develops. In the meantime, see the file checkout.mps in Appendix A for many examples. These should be self explanatory and a temporary substitute for documentation.
Be very careful of required syntax in Z commands. See the examples below and in checkout.mps. Error messages derive from both the Mumps compiler and the C compiler. Error messages, sometimes many, from the C compiler are not be helpful and can cause considerable frustration. You can, however, look in the C code to see what original line of Mumps may have generated the error.
The following lists some important changes that have been made. Mainly these center around added commands to adjust to the C environment. Mainly, these take the form of program and function structuring commands and data declaration.
A line with a pound sign in column one is taken as a comment. Do not include /* and */ in your Mumps comments. Do not include the TAB character in your comment.
Line execution level codes may be inserted in a line as the first item following the TAB character. The line level codes are from one to five decimal points followed by exactly one blank. Lines with line levels greater than the current line exception level are skipped. Only five levels are permitted.
A normal Mumps code line begins with a TAB character, followed by the first command of the line. A line with a label begins with the label, followed by the TAB character followed by the first command of the line. Multiple TAB characters may be used to indent.
YOU MUST USE TAB CHARACTERS. BLANKS WILL NOT WORK (most of my email involves repeating this).
If a line does not begin with a pound sign and contains no TAB characters, it is assumed to be HTML. HTML lines are first scanned by the compiler for embedded Mumps expressions which are evaluated (i.e., C code is generated to evaluate them) and the results substituted into the HTML line during execution. Then the HTML line is written to the C code output as the argument to a print statement to standard output. This allows you to embed HTML in your Mumps program.
All main programs must begin with a ZMain command. This is necessary so that the compiler will generate the required prologue/epilogue blocks. There is also a ZFunction command.
The compiler includes a run-time symbol table to store Mumps variables. It is located in the fcns.h file under the name sym_. Mumps variables are created and destroyed during execution. Previous versions of the compiler used C variables. The current system is more efficient.
New handling is different than the standard. The New command creates a new symbol table. Arguments, however, to the New are not permitted at this time. If a New is used in a block invoked by an argumentless Do, the New will be undone upon exit from the block in which it is contained.
On entry into a function called with arguments, A New is done and then the arguments are copied into the new symbol table. On exit, the old symbol table is restored. Thus, the only way to insert values into the symbol table of a function called with arguments is through the parameter list.
On entry into a function not called with arguments, the New is not done. All variables know to the invoking program are known to the function. Work is on-going on this problem.
ZMain takes no arguments and should be the only command on the line. The line begins with a TAB. No label is permitted. The command generates a C main program prologue. The prologue contains many definitions required by the generated C code. When a main program ends, the epilogue automatically closes the global arrays. On the other hand, global arrays are not automatically closed on conclusion of a sub-function.
The scope of a main program ends at the end of file for the program or upon encountering a ZFunction statement. There is no explicit logical program end statement.
A ZFunction command begins a new function and, if appropriate, ends a preceding function. The format is:
zFunction FunctionName
where FunctionName is the name of the function. These are NOT mumps functions but, rather, C functions.
Operators
The modulo operator follows the C language conventions and the Mumps expression x#y is implemented as the C code x%y. Mumps and C calculate modulo differently for reasons unknown to me. This affects the results when one of the operands is negative and the other is positive.
The pattern match operator does not support alternation.
The
binary sorts after (]]) operator is not supported.
Indirection.
Indirection is permitted in the IF, Kill, and SET commands at present. Do not use in other commands or in post-conditionals.
Functions may either be inline parts of the file containing the Mumps program or separately compiled C functions, possibly residing in separate object files. Functions are compiled in one of two ways:
If the Mumps function name begins with a circumflex (^), the
Mumps function becomes a C function separate from the C program main
function. If the Mumps function name does not begin with a
circumflex, the Mumps function becomes an inline section of code
within the C function in which it appears.
Normally, your
Mumps program becomes the C program main()
function. If a Mumps function becomes an inline function, it is
really just a part of the main()
program. You may want your Mumps function to be a separate C
function, however. If your Mumps function begins with the
circumflex character (^), it will be compiled as a separate C
function with the name you give it. A spearately compiled C
function can be called from other languages. A separately compiled
C function expects its arguments to be char *
and it returns the same. See the section below regarding compiling
separate functions.
Creating Mumps Functions as Separate C Functions
All Mumps functions that are to become separately compiled C functions must begin with a function header consisting of:
a circumflex (^) if the function is to become a separate C function,
the name of the function (must follow C function naming conventions),
a parenthisized list of arguments (if there are no arguments, the open and close parentheses must still be present),
a TAB character,
and an optional comment beginning with a semi-colon followed by at least one blank.
If a function header follows the above format, it will be created
as a separate C function. A new namespace is created on entry into a
function of this form and deleted upon exit. Variables passed to the
function are copied to the new namespace but they are not copied
back to the restored namespace on exit. The function may return a
value to the calling program through the QUIT
command.
Creating Mumps Functions and Inline
Functions
Use the same
format as above but omit the circumflex (^). The Mumps function will
become an inline section of the current function. Inline functions
do not require an argument list. If an inline function is invoked
with arguments, a new namesape is created on entry and deleted on
exit. Variables passed to the function become part of the new
namespace but their contents are not returned to the restored
namespace. If a function header has no arguments, a new namespace is
not created on entry and any variables created or altered are are
altered upon return. You must return to the calling program with a
QUIT command.
The following give examples.
An extrinsic function that is part of the main function:
zmain set i="hello" write $$test(i),! halt test(x) ; entry point write x," " quit "there"</b>
An extrinsic function that is a separate C function:
^test(x) ; entry write x," " quit "there" ZMain set i="hello" write $$^test(i),! halt
Note the circumflex (^). test() is, consequently, a separate C function. Labels in separate functions need to be unique only with regard to other labels in the function. You may not branch back to the main function with a GOTO. Labels in inline functions must be unique with regard to the entire program in which they appear. Separate functions should appear before they are used unless they are declared with code such as:
Zd char * test(char *);
prior to the ZMain command. Each argument must be declared
as "char *".
Linking Multiple Mumps
Object Files Together
Mumps functions that are separate C
functions (i.e., their names begin with circumflex), may be
separately compiled and linked together from object code modules. In
order to do this, however, a macro definintion must be passed at
compile time in order to prevent duplicate data definitions. For
example, if you have a main Mumps function in a file named main.mps
that calls upon a function contained in the file test.mps,
you may first translate the functions to C and then compile them to
object code, then link them as follows:
mumps.compiler main.mps
mumps.compiler test.mps
gcc -c
main.c
gcc -c test.c -DSUB
gcc main.o test.o -lm
This assumes that the main function is in main.c which was created by the mumps compiler. Likewise, test.c contains the subroutine also created by the mumps compiler. The -DSUB causes the C compiler to avoid redeclaration of certain globals values in the subroutine that are normally declared in the main routine. If you compile the subroutine without this switch, the link edit will generate multiple error messages.
Linking to Functions Written in C from Mumps Functions (Mumps
calls C)
You may
link to your own routines written in C. If the mumps invocation is:
set i=$$^MyFunction(a,b,c)
where MyFunction is the name of the function written in C. Your function header will be (in the C program):
char * MyFunction(char * a, char * b, char * c) { ...
the values of the arguments will be passed as pointer to string. You must return a pointer to string. The value you return will be stored, in this example, in i.
You will need a function header at the beginning of the Mumps
program of the form:
Zd char * MyFunction(char *, char * char
*);
Be careful not to use the underscore
character in function names. In mumps this means concatenate. Be
careful not to use the names of functions already known to the C
environment, including those names found in fcns.h.
Linking
to Functions Written in Mumps from C Programs (C calls Mumps)
In a separate file, write your Mumps function giving it a
name preceeded by a circumflex (^). Compile the function but do not
use the -DSUB parameter (if
you have multiple Mumps files, -DSUB
should be used on all but one file). Compile and link the C and
Mumps programs. Note that the Mumps program returns and receives
char * arguments.
Example:
In you C file main.c:
#include <stdio.h>
main() { char * subFunction(char *, char *, char *);
printf("result:
%s\n",subFunction("1","2","3"));
}
In your Mumps file test.mps:
^subFunction(a,b,c)
quit a+b+c
mumps.compiler test.mps
gcc -c test.c
gcc
-o main main.c test.o -lm
main
"result: 6" will
be printed.
Automatic opening of global arrays
The global arrays are automatically opened the first time they are used. If they are already in use by another Mumps program, your Mumps program will wait. If you do not use the global arrays, they will not be opened. If your program has a main routine, the globals will be automatically closed at termination. Otherwise, you must explicitly close them with a Zclose command. Failure to close the globals will result in data loss.
The permanent global arrays occupy two files. By default these are key.dat and data.dat. If you want to use other files, you need to change these reserved system variables cfgdata and cfgkey. These are reserved variable names and must not be used for any other purpose. If you want to change these from the default settings, you must do so prior to any global array reference. They may be set with ordinary Mumps commands such as:
Zd
strcpy(cfgdata,"/usr/mydirectory/data.dat");
Zd
strcpy(cfgkey,"/usr/mydirectory/key.dat");
You may select any names supported by your system. You must include these lines prior to the first usage of the global arrays. The global array handler uses the contents of these variables when it opens the files.
You may want to place the files on different disks to improve performance. One (cfgkey) contains the btree and the other (cfgdata) contains data stored at nodes. From WinXXXX, you may include disk name info. Be certain that your compiled program has read/write permissions the directory in which you place the files.
Initializing permanent global arrays
If you want to reinitialize the permanent global arrays, delete the old files. The system will create new ones. Be certain to delete both files - otherwise the system will improperly create the new globals. If your Mumps program terminates without properly closing the global arrays, they are corrupt and must be recreated.
Do not use the C language comment sequence /* ... */ or // anywhere.
The DO command in the compiler may reference function names and it may pass parameters. Any local function may be called. See the example in checkout.mps. The DO command may not contain offsets.
The command to compile, link and execute the demo program (under Linux) is:
mumps.compiler checkout.mps
gcc -o checkout checkout.c -lm
where mumps.compiler is the translator from Mumps to C. The second line compiles the result and links the library functions. Adjust the preprocessor variables in fcns.h for other operating systems and compiler options. Add -lpq and -lpwdb if you are using the PostgreSQL interface (-lpwdb may not be required on more recent versions of PostgreSQL).
Some C compilers check for error/warning conditions that others do not. The code generated by this compiler compiles and executes correctly on the Watcom 10.6 and Linux C compilers. If you have problems, please send me mail giving the type of compiler and the error. Some DOS compilers do not provide all required library functions. If this is so, you will need to write the missing functions. See any standard Unix or Linux system for any needed definitions. The Watcom compiler has all required function libraries.
Break Break exits a block rather than pause a program. For example,
for i=1:1:10 do
. write i,!
. if i=5 break
The program will print the numbers 1 through 5.
$Text(...) - not presently
supported.
Xecute - not presently
supported.
The sorts-after operator (]]) is
not supported.
Alternation is not permitted in
the pattern match operator.
Lock - Not supported. File
access is synchronized, however. Only one instance of your program
may access a given set of globals and any other instance waits
until it finishes. This may seem strange but it works just fine in
a web server transaction oriented environment.
Multiple arguments to the for
command. Only ascending iterative for commands at present.
Goto argument must be a
local label with no offset. If the label is not found, the error
message will be from the C compiler, not the Mumps compiler.
Do arguments may not be
post-conditionalized (the Do
itself, however, may) and arguments may not contain offsets. Only
local labels or separately compiled functions may be arguments.
the form: Do ^abc where abc
is the name of a file on disk containing Mumps code is not
permitted. The for Do ^abc
means to call upon a seprateley compiled function.
Timeout in read; direct
input in read.
Newer functions: $fn, $g, $na,
$o, $ql, $qs, $q, $re, $st, $tr.
Newer special variables: $d, $ec, $es, $et, $k, $p, $q, $st, $sy, $tl, $tr.
The Merge,
TCommit, TREstart,
TROllback, and
TStart commands are
not supported.
Many of these will be supported in the near future.
C style block structuring for for and if
The if and for commands may be followed by an open curly bracket ({) which will begin a block. The user must ultimately provide the close curly bracket (}) to terminate the block. This may not be used within the scope of a single line scoped if or for. See checkout.mps for examples. This gives you C type block structuring. You may declare variables within blocks (zd) for local usage.
The Mumps source program should not exceed the available buffer space (presently 30,000 bytes). Note: this is source program length, not executable program length. This corresponds to about 1,000 lines. The limit will be raised shortly.
Substitute global array processor
You may substitute your own global array processor. The routine global(...) in fcns.h is the global array handler. Details below. At present, if you want to run with no global arrays, substitute a dummy routine for global(...).
If a line begins with a percent sign (%) in column 1, the remainder of the line is treated as an inline C statement.
If the program crashes after opening and updating the global array, the global array files are corrupt and must be deleted.
SQL Server based global arrays
To compile programs so that they can access a data base server, set the SQL or POSTGRES defined symbol to 1 in the fcns.h file. Do not engage this option unless you need it as it will introduce significant additional overhead into the resulting module. You must also provide access to the odbc32.lib for DOS and the PostgreSQL libraries for Unix/Linux. (At present, the SQL interface is not well tested)
What was once the main feature of Mumps, its native, hierarchical global array data base, is increasingly seen as a major drawback in today s standardized, multi-facility networked health care provider environment. Instead the rapidly growing popularity of standardized, networked , SQL based, relational data base management server systems (RDBMS) has undercut the demand for the Mumps hierarchical data base model. While the hierarchical model remains very adept at servicing the needs of individual patient care applications, it is less well suited for the needs of client-server, networked , enterprise wide applications involving finance, quality of care audit and reporting.
You may access an SQL server through the $Zodbc function. It take five arguments: the name of the data base, the user name, password, the SQL command and the name of the Mumps array to receive the results. For retrieval, the value returned is the number of rows retrieved.
The SQL command may be any standard command. Rows retrieved are stored in the global array name (with out ^ or parentheses) given as the last argument. The rows are stored in the array with ascending numeric indices beginning at one. Thus, if the global array name is tmp and 5 rows are retrieved, they will be in ^tmp(1), ^tmp(2),... ^tmp(5). Each retrieved row will be stored as a string in the global array. The column values will be separated from one another by a backslash character (\). This may be set to another value with the DELIM preporcessor variable in fcns.h. If DELIM is set to \x01, the columns will be successive indices in the named array.
Introduction
The purpose of this section is to provide you with an introduction to the Mumps language in general and the Mumps compiler. The Mumps language originated in the mid-60's at the Massachusetts General Hospital. The acronym stands for "Massachusetts General Hospital Utility Multi-Programming System". It is a language which is similar in some respects to BASIC but it contains many additional features not found in BASIC, or for that matter, in most other languages. In its full form, Mumps is an interpretive language. In fact, parts of the language specification require that it can never fully become a "compiled" language such as FORTRAN, COBOL or PL/I. In the compiler, some features, therefore, mainly those involving run time interpretation and symbol table binding, are removed. See above for details.
Among the features which make Mumps attractive for both bio-medical and general scientific applications are:
Hierarchical data base facility. Mumps data sets are not only organized along traditional sequential and direct access methods, but also as hierarchical trees whose data nodes are addressed as path descriptions in a manner which is easy for a programmer to master in a relatively short time.
Flexible and powerful string manipulation facilities. Mumps built-in string manipulation operators and functions provide programmer's with access to efficient means to effect complex string manipulation and pattern matching operations.
Transportability to widely different systems Mumps presently runs under a large number of operating systems on many machine architectures. These systems range in size from small home micro-computers to the largest central time sharing systems. Through efforts that have taken place by the Mumps Development Committee over the years, a well organized language definition has been written and formally published. This standard provides for a far tighter specification for system performance and linguistic definition than is normally the case. As a result, programs written under a Mumps system can be moved with relatively little effort from one system to another.
Full numeric data handling facilities Mumps provides, in addition to string handling facilities, a full range of fixed and floating point computational facilities.
Basically, Mumps has only one data type: string, although it does allow integer and floating point computations as well as logical expressions. A string variable is restricted to 255 characters in length or less (20 characters or less if it is being used as a number. Note: this is a restriction of this implementation of Mumps; see above for a detailed list of such restrictions). The values in a string may be any ASCII code from 1 to 128 (decimal) inclusive with the exception that an ASCII 1 may not be used in a string index to a global array. Mumps does not permit usage of the ASCII zero character (<NULL>). Ordinarily, strings will contain ASCII printable characters. String constants are enclosed in double quote marks ("). A double quote mark can be included in the usual manner with two adjacent quote marks (""). A constant containing only numerics (and, optionally, plus, minus and decimal point), need not be enclosed by quotes (although doing so has no effect). The following are examples of valid Mumps character string constants:
"THE SEAS DIVIDE AND MANY A TIDE"
"123.45"
"BRIDGET O'SHAUNESSEY? YOU'RE NOT MAKING THAT UP?"
"""THE TIME HAS COME,"" THE WALRUS SAID"
When a string is being used as a number (e.g., in addition), the numeric portion must be 20 characters or less in length. Numeric constants are restricted to integer or decimal values (positive or negative). "E-type" notation is not permitted. If a string begins with a number but ends with non-numeric characters, only the numeric leading portion will participate in operations requiring numeric operands (e.g., add, subtract, etc.); the trailing non-numeric portion is lost. On the other hand, if a string begins with non-numeric characters, its value will be interpreted as 0. The following are examples:
1+2 will be evaluated as 3.
"ABC"+2 will be
evaluated as 2.
"1AB"+2 will be evaluated as 3.
"AA1"+2 will be evaluated as 2.
"1"+"2"
will be evaluated as 3.
Although "string" is the basic data type, Mumps converts strings internally to floating point values for calculations. Consequently, numbers are of approximately 7 digit precision. A number may range in magnitude from 10**-19 to 10**19.
Logical values in Mumps are special cases of the numerics. A numeric value of zero is interpreted as false while a non-zero value is interpreted as true. Logical operators yield either zero or one and their results can be treated like any other numeric. Similarly, the numeric result of any numeric operator can be used as a logical operand. The results of string operators are interpreted either as zero (leading characters non-numeric) or some value (leading characters numeric). Strings and the results of string operations can therefore participate as the operands of logical operators.
Variables are named in Mumps in much the same manner they are named in other languages. A Mumps variable name must begin with a letter (A through Z) or percent sign (%) and may be followed by either letters or numbers. In general, variable names should be nine or fewer characters in length (the maximum variable name length is 255 characters). Unlike most languages, Mumps variables are not automatically data typed by their first letter. Mumps, in effect has only one data type so any variable name may be any value. All Mumps variables are varying length strings (length may range from 0 to 255 characters).
In Mumps there are no data declaration statements. Variables come into existence through assignment statements (SET) or the "READ" command and pass from existence through the "KILL" command. In the compiler, variables must be declared.
In the Mumps, there are two kinds of arrays: internal arrays and global arrays. The following pertains to internal arrays: arrays are not dimensioned. A name used as an array variable may also, at the same time, be used as a scalar. Array values are created by assignment or appearance in a "READ" statement. If you create an element of an array, let us say element 10, it does not mean that Mumps has created any other elements: that is, it does not imply that there exist elements 1 through 9. You may specifically create these or not. Array indices may be positive or negative numbers or character strings. Arrays in Mumps may have multiple dimensions. The following are some examples of arrays:
SET A(1,2,3)="ARRAY"
READ TEST(22)
WRITE
TEST(22)
SET I=10 SET A(I)=10
SET A("TEST")=100
SET I="TESTING" SET A(I)=1001
SET
A("MUMPS","USERS'","GROUP")="MUG"
Global arrays are unique to Mumps. As a programmer, you will work with them as though they were arrays. The system, however, interprets them as tree path descriptions for the system's external data files. A global array is distinguished by beginning with the circumflex character (^). The remainder of the specification is the same as an internal array. global arrays are not dimensioned and they may appear anywhere an ordinary variable may appear (except in certain forms of the "KILL" command). A typical global array specification consists of the array name followed by some number of indices (indices may be constants, variables [including internal or global arrays] or expressions of string, numeric or mixed type). For example:
SET ^A(1,43,5,99)="TEST"
SET ^SHIP("1ST
FLEET","BOSTON","FLAG")="CONSTITUTION"
SET ^CAPTAIN(^SHIP("1ST FLEET","BOSTON","FLAG"))="JONES"
SET ^HOME(^CAPTAIN(^SHIP("1ST FLEET","BOSTON","FLAG")))=
... "PORTSMOUTH"
WRITE ^SHIP("1ST
FLEET","BOSTON","FLAG")
... CONSTITUTION
WRITE ^CAPTAIN("CONSTITUTION")
... JONES
WRITE
^HOME("JONES")
... PORTSMOUTH
WRITE
^HOME(^CAPTAIN("CONSTITUTION"))
... PORTSMOUTH
The system files are viewed as trees. Each global array name ("A", "SHIP", "CAPTAIN", and "HOME" in the above) is the root of a tree. The indices are thought of as path descriptions to leaves. For example, out of the root "A" there may be many branches, the above specifies to take the branch labeled "1" (note: this does not mean the "first" branch out of the node - it means the branch with label "1"). At the second level the specification says to take the branch labeled "43" (note: this does not imply that branches 1 through 42 necessarily exist). The path description is followed (or, possibly, created if the global array specification appears on the left hand side of an assignment statement or in a "READ" statement) to a final node. The value at the node is either retrieved or a new value stored depending upon the context in which the global array specification was used. The indices of global arrays may be numeric or character strings. The second sequence of examples above illustrates this usage.
Both string and character indices may be mixed in the same path description.
A value may be stored at any position in the tree. For example:
SET ^A(1,43,5)=22
SET ^A(1,43)="TEST MIDDLE LEVEL"
Arithmetic unary operators: + -
The arithmetic unary operators are: + and -. The plus operator (+) has no effect other than to force the expression to its right to be interpreted as numeric. The minus operator forces numeric interpretation and negates the result. For example:
SET I="123 ELM STREET"
WRITE +I yields 123
WRITE -I yields -123
Arithmetic binary operators: + - * / \ **
The addition (+), subtraction (-), multiplication (*) and exponentiation (**) operators perform in the normal manner. Operands are given a numeric interpretation if necessary. Operands may be either expressions, constants, variables or array references. Results are computed in floating point if appropriate. Mumps has two division operators: full division (/) and integer division (\). Full division give results which may have fractional parts. Integer division truncates the answer to an integer.
The modulo operator (#) gives the left operand modulo the right operand. The following are examples:
2+3 yields 5
2.31+1 yields 3.31
3-5 yields -2
7/4 yields 1.75
7\4 yields 1
11#3
yields 2 (please see compiler notes)
3**2 yields 9
Arithmetic relational operators: > <
The greater than (>) and less than (<) relational operators compare numbers. If the operands are not numbers, they are given a numeric interpretation. The result is either zero for FALSE or one for TRUE. For example:
1 > 2 yields 0
2 > 1 yields 1
1 < 2 yields 1
2
< 1 yields 0
Both operators may be negated producing not greater than ('>) and not less than ('<) (note: the single quote mark is the negating operator). There is no "less than or equals" or "greater than or equals" operators as such. For example:
1' > 2 yields 1
2' > 1 yields 0
1' < 2 yields 0
2' < 1 yields 1
The only binary string operator is concatenation (_) represented by an underscore character. The following are examples:
"ABC"_"XYZ" yields
"ABCXYZ"
"ABC"_123 yields "ABC123"
123_456 yields 123456
String relational operators: = [ ] ?
The equals relational operator (=) tests for equality as in the following example:
IF "ABC"="ABC" WRITE "EQUALS"
We would expect that "EQUALS" would be written to the terminal. The not-equals operator if formed by the single quote mark and the equals sign. The equals and not-equals operator may be used with strings or numbers.
The contains operator ([) determines if the right hand operand is contained in the left hand argument. For example:
SET A="NOW IS THE TIME"
IF A["THE" WRITE
"YES"
The word "YES" would by printed on the terminal.
The follows operator (]) is used to test if the left hand operand follows the right hand operand in the collating sequence. For example:
SET A="ABC"
IF A]"AAA" WRITE "YES"
The word "YES" would be printed at the terminal.
The pattern matching operator (?) is used to determine if a string conforms to a certain pattern. Alternation is not supported. The patterns are:
A for the entire upper and lower case alphabet.
C for the 33
control characters.
E for any of the 128 ASCII characters.
L
for the 26 lower case letters.
N for the numerics
P for the
33 punctuation characters.
U for the 26 upper case characters.
A literal string.
A pattern code is made up of one or more of the above, each preceded by a count specifier. The count specifier indicates how many of the named item must be present. Alternatively, an indefinite specifier - a decimal point - may be used to indicate any count (including zero). For example:
SET A="032-34-6304"
IF
A?3N1"-"2N1"-"4N WRITE "OK"
SET
A="JONES, J. L."
IF A?.A1",".A WRITE "OK"
The logical operators AND (&), OR (!) and NOT (') may be applied in the usual manner. The user should note, however, that since Mumps has stric t left-to-right precedence, the results can sometimes be odd:
1 & 1 yields 1
2 & 1 yields 1
1 & 0 yields 0
1!1 yields 1
1!0 yields 1
0!0 yields 0
2!0 yields 1
1 & 0 < 1 yields 0
1 & (0 < 1) yields 1
The "NOT" operator may be used in conjunction with other operators to form compound operators. The resulting compound operators are:
'< not less than
'> not greater than
'= not equal
'[ not contains
'] not follows
'? not pattern
Each statement in Mumps begins with a unique command word. Often the command word is abbreviated to a single character. The single character abbreviations are unique for all commands except those which begin with the letter "Z". For commands not beginning with the letter "Z", Mumps does not check the spelling of the command word if more than one character of the spelling is given. The first letter is used to determine the command. Thus "WRITE", "W", and "WRIGHT" all have the same meaning.
The format of a command normally consists the command word or letter followed (optionally) by a post-conditional, followed by exactly one blank followed by the arguments to the command. Most commands can have multiple arguments. Multiple arguments are delimited by commas. If a line is to have more than one command, the first command is delimited by exactly one blank and the next command word or letter follows immediately. Blanks are very significant in Mumps.
As noted above, most commands may be "post-conditionalized". A post-conditional is a logical expression which is used to determine if the command (and all its arguments) should be executed. It is like a small "IF" statement. A post-conditional appears as a colon followed by an expression. If the expression evaluates to 0 (false), the command (or argument) is not executed. If the expression evaluates non-zero, the command or argument is executed.
The following are examples of the above:
an ordinary assignment statement:
SET I=10*5
same as above with command word abbreviation:
S I=10*5
an assignment statement with multiple arguments:
S I=10*5,J=5,K=I+J (K will equal 55)
an assignment statement post-conditionalized:
S:I=10 J=0 (set J to zero if I equals 10)
a multiple command line:
S I=10*5 S j=5 S S=I+J (same as above)
Table of Commands
Mumps usage is non-standard. It is used to exit an argumentless Do group. For example:
For i=1:1:10 Do . Write i,! . If i>5 Break
The CLOSE command closes a unit number and makes it available for other uses. It also frees the system buffers for other uses. The argument must evaluate to a number which corresponds to an open unit. In Mumps this must be in the range of 1 to 4 (other values will terminate your program). An output file must be closed explicitly. Failure to do so may result in loss of some or all of the file. The close command may be post-conditionalized and it may have multiple arguments.
C 4
C I
C:K=J 1,2
The DO command causes the program to branch to the label specified and continue execution beginning at that label. Execution proceeds until the end of the program is reached or a QUIT command is encountered or the block ends. When any of these terminating conditions is achieved, the program returns to the original DO command and executes subsequent arguments or commands on that line and following lines. DO commands may have multiple arguments. They specify multiple routines to be executed. The DO command may be post-conditionalized but its arguments may not be post-conditionalized at this time.
The argumentless DO causes the code on the immediately following line to be executed. This line must be the beginning of a block with a level of dotted indent greater than that of the line containing the DO. This block is terminated by a QUIT or by encountering a line with a lower level of dotted indent. An argumentless DO must be followed by two blanks (unless it is the last command on a line). It may be Post-Conditionalized. For example:
For i=1:1:10 Do Write "Line ",i . Write "This is a Do group",i,! . If i=5 Do .. Write "This is a sub block",! . Quit The BREAK command may be used to exit from a block and not return to the line containing the invoking DO. For example: For i=1:1:10 Do . Write i,! . If i=5 Break The above code will print the numbers 1 through 5.
Other examples:
DO LAB1
DO LAB1
DO:I=J LAB1
DO
DO Label(parm1,parm2...parmN)
DO ^FCNNAME(parm1,parm2,...,parmN)
DO:I=J ^FCNNAME(parm1,parm2,...,parmN)
If the label following the DO command
begins with a circumflex (^), a separately compiled C function is
invoked. The function may be declared in the current file or in a
separate file. Functions from seprate files may be pre-compiled and
linked. See the section on Functions
The ELSE command tests the value of the system wide built-in variable $TEST. If $TEST (abbreviated as $T) is zero, the remainder of the line on which the ELSE appears is executed. If $T is not zero, the remainder of the line is not executed. $TEST is set, among other ways, by the IF statement. Since ELSE does not take arguments, it must be followed by two blanks.
For example:
ELSE Set I=10
In the compiler, only ascending
numeric indexed operations are permitted at present. See notes
above.
The GOTO command causes unconditional
transfer of control. In the compiler, only local program labels may
be used. These may not be post-conditionalized (although the GOTO
itself may be). Variables are not permitted.
The HALT command terminates execution of the Mumps program and returns you to the operating system or web server. It may be post-conditionalized. For example:
H:I=3
The HANG instruction suspends execution of your program for a specified period of time (in seconds). It takes as an argument the number of seconds to wait. It may be post-conditionalized. The HANG instruction differs from the HALT instruction only in the argument: a HANG without an argument is a HALT instruction. For example:
H:I=J 2*K
The IF command permits conditional execution.
In the compiler, only one expression is permitted and the argumentless form is not supported. In the compiler, however, a { may follow the expression introducing a block which ultimately ends with a } character. There should be no other operations on the line after the { character. An IF beginning a block should not be on the same line as an IF or FOR which have single line scope (i.e., that do not begin blocks themselves).
Note also that expressions are evaluated left to right. This sometimes causes problems for people used to dealing with FORTRAN or BASIC. For example, the expression:
I=0&J<0
is always false since it is parsed as:
(((I=0)&J)<0)
if I is zero, the first expression is true (value of 1); if J is less than zero, then J is interpreted as true giving, as a result of the AND operation (&), a value of 1 which is not less than zero - therefore false. If I is not zero then, regardless of the value of J, the AND operation results in false (value of zero) which is not less than zero - therefore false. The expression should have been written as:
(I=0)&(J<0)
The JOB command performs a fork().
The KILL command is used to prune the symbol table and to delete parts of the global arrays.
There are three forms of the KILL command: the first deletes all entries in the local (i.e., non-global) symbol table; the second deletes specific elements from the local symbol table or specific elements from the global arrays; and the third is used to delete all elements from the local symbol table except for certain named symbols. All forms may be post-conditionalized. The first form - delete the entire local symbol table - is denoted by the KILL command alone:
KILL
The second form appears as a list of references (note: indirection for the names is permitted):
KILL A,B,^G(1,2,33)
The above would delete variables A, B and the global array node ^G(1,2,33). Note: if the global array node ^G(1,2,33) has descendants, they are also deleted. Also, if a local array node is deleted, any of its descendants are also deleted.
The final form of the KILL may be used to delete all elements from the local symbol table except for certain protected elements. It has the following format:
KILL (A,B(1,1),K)
In the above, the local symbol table
will be deleted except for variables A, B(1,1) and K. All other
variables will be lost. Global array nodes may not be used in this
form of the KILL statement. Indirection is permitted.
The LOCK command gains exclusive
access to a portion of the data base for an individual user. A LOCK
with no arguments frees all prior LOCK's. The LOCK is not supported
in the compiler.
The NEW command creates a new symbol table or namespace and pushes the old namespace onto a stack.
If NEW is used with no arguments, a completely new namespace is created and any previous namespaces are pushed-down. Upon exit from the block in which the NEW was executed, the old namespace is restored and the contents of the new namespace are lost. A block is determined to be exited if you execute a QUIT from a section of code entered by a DO or you revert to a lower level of dotted indent.
If you include an argument consisting of a parenthesized list of variable names, a new symbol table will be created that contains only these variables and the values they had in the prior namespace. Upon exit from the block (see above), the values of these variables from the new namespace will be copied back to the restored namespace. Any other variables created in the new namespace are discarded.
If you include one or more variable names as an argument, not enclosed in parentheses, a new namespace will be created which contains all the variables from the previous name space except for the variables given in the parenthesized list. Variables named in the parenthesized list will be newly created variables and they will not have the values they may have had in the old namespace. Upon exit from the block, the values of original variables from the old namespace, except the ones named in the list, will be copied to the restored namespace. Any variables newly created in the new namespace will be lost.
Upon entry into a separately compiled C function, a NEW is always performed. Any parameters passed to the function are instantiated. Upon exit, the old symbol table is restored.
Examples:
set i=1,j=2,k=3
write "before ",i," ",j,"
",k,!
do
. new
. set i=100,j=200,k=300
. write "in
block ",i," ",j," ",k,!
write "after
",i," ",j," ",k,!
halt
The above
will write:
before 1 2 3
in block 100 200 300
after 1 2
3
--------------------------------------------
set
i=1,j=2,k=3
write "before ",i," ",j,"
",k,!
do
. new (j,k)
. write "data(i)=",$data(i),"
j=",j," k=",k,!
. set i=100,j=200,k=300
. write
"in block ",i," ",j," ",k,!
write
"after ",i," ",j," ",k,!
halt
The
above will write:
before 1 2 3
data(i)=0 j=2 k=3
in
block 100 200 300
after 1 200
300
--------------------------------------------
set
i=1,j=2,k=3
write "before ",i," ",j,"
",k,!
do
. new j,k
. write "i=",i,"
j=",j," k=",k,!
. set i=100,j=200,k=300
. write
"in block ",i," ",j," ",k,!
write
"after ",i," ",j," ",k,!
halt
The
above will write:
before 1 2 3
i=1 j= k=
in block 100
200 300
after 100 2 3
The OPEN command opens sequential files and associates them with unit numbers. Opened files may be read or written. This implementation permits unit numbers 1, 2, 3, and 4 to be used by the programmer. Unit 5 is reserved for the normal user console and unit 6 is reserved for Unix full screen console I/O. The open command takes a device number (either a number or an expression which evaluates to a number in the range of 1 to 4) and a file name. The file name must either be a variable name or a quoted literal. The file name consists of a valid file name followed by either /NEW /APPEND or /OLD (for the compiler, dow not use the "/" character, use a comma instead).
If followed by /NEW, the program
assumes you are opening the file for output: any previous files with
this name are lost You may only write to this file. If you open the
file with the /OLD option, the program assumes the file exists and
opens it for input only (reads). If you specify /APPEND, the file is
opened for outout with all new data written to the file appearing
after the previously existing data. If an error takes place, $test
is set to zero and the remainder of the command is not inspected. If
no error takes place, $test will be one. The user should not attempt
to reference units for which the OPEN command returned a $test value
of 0. For example:
OPEN 1:"data,old"
OPEN 1:"data,new"
OPEN 1:"data,append"
You should be careful to CLOSE any file you have opened for output in order not to loose any of the file's contents. You must CLOSE an open unit number before re-using the unit number in another OPEN command.
In cases where the file name contains
"/" characters, a comma may replace the "/"
prior to the NEW, OLD, or APPEND.
The QUIT command provides an exit
point for a FOR, DO commands. It may be post-conditionalized. It
takes no arguments (therefore, you need two spaces after it if there
are any commands following it on the same line). In the FOR case,
the QUIT terminates the nearest loop. In the DO case, the QUIT
returns to the most recently invoking command. In the XECUTE case,
execution of the XECUTE text is terminated and control is returned
to the original command line.
The READ command reads data into variables. It may be post-conditionalized. Ordinarily the READ command reds from the user's terminal (unit number 5). The READ command can be redirected to other devices and files, however, by use of the USE command (see below). It is a common source of error - sometimes quite destructive errors - for the user to READ or WRITE to the wrong device. Many Mumps programmers explicitly place the USE command immediately prior to the READ or WRITE commands. Ordinarily, the READ command takes one or more arguments which may be local scalar variables, local array elements, or global array elements. Each of these is read successively from the input device. When more than one argument is present, a carriage return / line feed is taken as the delimiter between the successive input values. For example:
READ A,B,^A(1,3,99)
If the input is derived from the user's terminal, the user might type the following sequence in response to the above:
22
38
NOW IS THE TIME
The READ command may also write before it reads. This mode is only permitted at the user's console. The options permitted for "write before read" are: a literal constant in quotes; and a tab, new line, or new page operation. A tab operation is specified by a question mark followed by an expression which is interpreted as arithmetic. The effect is to cause the cursor to move to the named column on the page or screen. The new line operation is caused by typing an exclamation point (!). The program will generate a carriage return / line feed pair. A new page is induced by the pound-sign (#) character. For example, if you wish to read name, social security number and password with user input from column 20, the following might be appropriate:
R "NAME",?20,N,"SSN",?20,S,"PASS",?20,P
After the above, the variable N will contain the name, S the social security number and P the password. The READ command also permits single character input. That is, a read operation will be satisfied as soon as the user strikes any character on the keyboard: no carriage return is required. The variable will contain a number which is the equivalent of the ASCII character struck. This mode is denoted by preceding the variable to be read by an asterisk. For example:
READ "ENTER A LETTER ",*A
The READ is satisfied when any character is struck. There is also another form of the READ: that which contains "time-outs". Time-outs permit the programmer to specify a maximum interval of time which the program should wait for the user to reply to the READ operation. The time-out may be used with either the regular or character by character mode. The time-out is specified by placing a colon after the variable followed by an expression which will be interpreted as numeric. The value of the expression is the amount of time in seconds to wait for a user response to this operation. If the user fails to respond in the required interval, the $TEST built-in variable is set false (0) and the variable contains nothing. If the user does respond in time, $TEST is set true (1) and the variable contains the user's reply. For non-character by character mode input, the user must type the carriage return for the input to be valid. If the time-out expires before the user type the carriage return, all input is lost. For example:
AGAIN READ
"ENTER NAME ",N:20 IF '$TEST GOTO AGAIN
SET (abbreviation: S)
The SET command is the assignment command for Mumps. The expression to the right hand side of the equals sign is evaluated and placed in the storage associated with the variable on the left hand side. Global variables may be used on the left hand side. The SET command may be post-conditionalized. The $Piece function may be used on the left hand side of an expression. For example, if the variable a contains the value "aaa.bbb.ccc", the command:
$P(a,".",2)="xxx"
will result in the value of a
becomming "aaa.xxx.ccc"
The USE command tells the program which device (unit) number to use for input output operations (READs and WRITEs). The unit designated as the input/output device remains in effect until changed by the USE again or an error occurs. If the program detects an error it always resets the the current device number to 5 - the number associated with the user's console terminal. The valid range of unit numbers in Mumps is 1 through 5. The current unit number can be determined from the $IO (abbreviation: $I) built-in variable. The command is specified as the letter U or the word USE followed by an expression which is interpreted as numeric. For example:
USE 1
The VIEW command is not used in this
implementation.
The WRITE command transmits data to an output device. Normally output is directed to the user's console terminal. By the USE command, however, data may be directed to another unit number and its associated file. A unit number must be opened prior to writing to it. The WRITE command takes as arguments either literals in quotes, numeric constants, variable names: both local and global, and control codes and expressions for tab, new line and new page operations. The control operations are the same as those discussed above for the READ operation. Output is stream oriented: that is, each WRITE does not begin on a new line: it begins where the last line left off. Wrap-around occurs at your terminal depending upon the current terminal monitor level width setting. You may specify single character output by an asterisk followed by a decimal number. The system will send the ASCII character associated with the number you specify (e.g., *7 will send the BELL character). For example:
WRITE "NAME OF PATIENT",?25,NAME
WRITE !,"AGE",?20,AGE
The Z in ANSI Mumps permits the
implementor to add special commands.
ZASCII - Form this point
on, store global array numeric subscripts according to the ASCII
collating sequence (This is the startup default).
ZE - The ZE command may be
used to terminate a compiled program. It may be executed either
from direct or program mode. The ZE command causes immediate
termination. It should be the only command on a line. The Halt
command may also be used to terminate execution. When executing
with a web server, it is important that your program ultimately
terminate a program. Simply ending a program (Quit) and returning
to interactive mode will cause your web server to crash.
ZNUMERIC - From this point
on, store global array subscripts according to numeric collating
sequence. Only works with positive integer subscripts of 8 or fewer
digits. This is not the startup default. When in effect, numeric
subscripts are padded to the right to a field width of 8.
ZMAIN - The ZMAIN command
is used to introduce the main program to the compiler. It is
discussed in detail below. It and the remainder of the line on
which it appears are ignored by Mumps programs.
ZFUNCTION - The ZFUNCTION
command is used to begin a subroutine in compiled programs. It is
discussed in detail below. It and the remainder of the line on
which it appears are ignored by the Mumps programs.
ZRETURN - The ZRETURN
command is used to return from a subroutine in compiled programs.
Its use is discussed in detail below. It and the remainder of the
line on which it appears are ignored by the Mumps programs.
ZD - The ZD command is
used to pass arguments directly to the C compiler in compiled
programs. Its use is discussed in detail below. It and the
remainder of the line on which it appears are ignored by the
prgram.
ZSEEK - ZSeek address repositions the currently open file system to the byte address given by address. This is equivalent to the C language lseek() function.
$ASCII returns the numeric value of an ASCII character. The string is specified in e1. If no i2 is specified, the first character of e1 is used. If i2 is specified, the i2'th character of e1 is chosen. For example:
$ASCII("ABC") YIELDS 65
$ASCII("ABC",1)
YIELDS 65
$ASCII("ABC,2) YIELDS 66
$ASCII("")
YIELDS -1
Like $Next except it gives the previous value of the last global array index. (non-standard function).
$CHAR(i1) or $CHAR(i1,i2) or $CHAR(i1,i2,...)
$CHAR translates numeric arguments to ASCII character strings. Numeric values greater that 128 will generate errors. For example:
$CHAR(65) yields "A"
$CHAR(65,66) yields "AB"
For the compiler, $DATA only works for global arrays.
$DATA returns an integer which indicates whether the variable vn is defined. The value returned is 0 if vn is undefined, 1 if vn is defined and has no associated array descendants; 10 if vn is defined but has no associated value (but does have descendants); and 11 is vn is defined and has descendants. The argument vn may be either a local or global variable. For example:
$DATA(A)
$DATA(A(1,1))
$EXTRACT(e1,i2) or $EXTRACT(e1,i2,i3)
$EXTRACT returns a substring of the first argument. The substring begins at the position noted by the second operand. If the third operand is omitted, the substring consists only of the i2'th character of e1. If the third argument is present, the substring begins at position i2 and ends at position i3. Note that this differs from the usual SUBSTR function in PL/I. If only "e1" is given, the function returns the first character of the string "e1". If i3 specifies a position beyond the end of e1, the substring ends at the end of e1. For example:
$EXTRACT("ABC",2) YIELDS "B"
$EXTRACT("ABCDEF",3,5) YIELDS "CDE"
$FIND(e1,e2) or $FIND(e1,e2,i3)
$FIND searches the first argument for an occurrence of the second argument. If one is found, the value returned is one greater than the end position of the second argument in the first argument. If i3 is specified, the search begins at position i3 in argument 1. If the second argument is not found, the value returned is 0. For example:
$FIND("ABC","B") YIELDS 3
$FIND("ABCABC","A",3) YIELDS 5
$JUSTIFY(e1,i2) or $JUSTIFY(e1,i2,i3)
$JUSTIFY right justifies the first argument in a string field whose length is given by the second argument. In the two operand form, the first argument is interpreted as a string. In the three argument form, the first argument is right justified in a filed whose length is given by the second argument with i3 decimal places. The three argument form imposes a numeric interpretation upon the first argument. For example:
$JUSTIFY(39,3) YIELDS " 39"
$JUSTIFY("TEST",7)
YIELDS " TEST"
$JUSTIFY(39,4,1) YIELDS "39.0"
The $LEN function returns the string length of its argument. For example:
$LEN("ABC") YIELDS 3
$LEN(22.5) YIELDS 4 .DE If a
second argument is given, the function returns the number of
non-overlapping occurrences of "e2" in "e1" plus
1.
The $NEXT function gives the next higher value for the last index in an array (local or global). If there is no higher value, $NEXT returns -1. You may find the first value for the last index by invoking the function with -1 in the last position. Remember, Mumps arrays are sparse and not all index values necessarily exist. For example:
$NEXT(A(1,1)) yields 3 if A(1,3) exists and A(1,2) does not.
$NEXT(A(1,1)) yields 99 if A(1,99) exists and no node exists between A(1,1) and A(1,99).
$NEXT(A(-1)) yields 1 if A(1) is the first index value for the first index level.
See details above under "Implementation Notes" concerning the collating sequence. Numeric subscripts are presented in alphabetic, not numeric, collating sequence.
Not presently implemented.
$PIECE(e1,e2,i3) or $PIECE(e1,e2,i3,i4)
The $PIECE function returns a substring of the first argument delimited by the instances of the second argument. The substring returned in the three argument case is that substring of the first argument that lies between the i3'th minus one and i3'th occurrence of the second argument. In the four argument form, the string returned is that substring of the first argument delimited by the i3'th minus one instance of the second argument and the i4'th instance of the second argument. If only two arguments are given, i3 is assummed to be 1. For example:
$PIECE("A.BX.Y",".",2) YIELDS "BX"
$PIECE("A.BX.Y",",",1) YIELDS "A"
$PIECE("A.BX.Y",".",2,3) YIELDS "BX"
$P can be used on the left hand side of a SET command or as an argument in a READ command. In these cases, the first argument must be a local or global variable. The contents of this variable are altered to the value of the right hand side of the SET statement or the value read by the READ statement. The entire contents of the local or global variable are not altered, only the part which would have been extracted by the $P function.
$RANDOM returns an integer in the range zero through i1-1. For example: $RANDOM(100) yields a value between 0 and 99
The $SELECT function takes a variable number of arguments delimited by commas. Each argument consists of two parts: a logical expression and a result expression. The function evaluates in sequence each of the logical expressions (shown above as t1, t2, ...tn - note: these can be any expression in reality: a zero result is called false and a non-zero result is called true). If a logical expression is true, the result expression (e1, e2, ... en) is evaluated and becomes the value for the function. In the compiler, there is a maximum of ten argument pairs permitted.
$TEXT(l1) or $TEXT(L1+I2) or $TEXT(+I1)
$TEXT is not supported in the compiler.
$VIEW is not supported.
$Z functions are extensions added by the implementor. Mumps has several $Z functions:
The $ZODBC(...) function accesses a data base server. Details are given below.
The $ZAB(arg) function returns the absolute value of its numeric argument.
The $ZB(arg) function returns a string in which leading and all multiple blanks have been replaced by single blanks.
The $ZCD function dumps the globals to a sequential ascii file in the current directory. The file name is of the form number.dmp where number is the high order 8 digits of the value of the C time() function at the time of the dump.
The $ZCL function restores the globals from the file named dump. Create this file by using the $ZCD function and renaming the output file to dump. Please note this name. It is different than the names used by the dump function.
The $ZDATE (or $ZD ) function returns the system date and time in standard system printable format. This includes: day of week, month, day of month, time (hour:minute:second), and year (4 digits).
The function $ZD1 returns the number of seconds since January 1, 1970 - a standard used in Unix. This number may be used to accurately correlate events.
The function $ZD2(InternalDate) translates the Unix time from $ZD1 into standard system printable format. The argument is a Unix format time value.
The function $ZD3(Year,Month,Day) returns the day of the year (Julian date) for the Gregorian date argument.
The function $ZD4(Year,DayOfYear) returns the Gregorian date for the Julian date argument.
The function $ZD5(Year, Month, Day) returns a string consisting of the year, a comma, the day of year, and the number of days since Sunday (Monday is 1).
The function $ZD6 returns a string consisting of the hour, a colon, and the minute.
The function $ZD7 returns a string consisting of the year, hyphen, month, hyphen, and day of month. If an argument is given in the form of the number of seconds since Jan 1, 1970, the result returned will reflect the argument date.
The function $ZD8 returns a string consisting of the year, hyphen, month, hyphen, and day of month, comma, and time in HH:MM format. If an argument is given in the form of the number of seconds since Jan 1, 1970, the result returned will reflect the argument date.
The function $ZD9 returns a string consisting of the year, hyphen, month, hyphen, and day of month, comma, day of week and time in HH:MM format. If an argument is given in the form of the number of seconds since Jan 1, 1970, the result returned will reflect the argument date.
The $ZF(arg) function returns a zero or one indicating if the file given as the argument exists.
The $ZG function gives a profile of the global file systems. It returns 6 values, separated by blanks, that give the the address of the file system root block, the size of the DATA file, the size of the KEY file, the number of internal buffers in use, the file system mask in hexadecimal and the version number. The files are limited to 2 gigabytes each at present.
The $ZH(arg) function encodes its argument in the form necessary to be a cgi-bin parameter. That is, alphabetics remain unchanged, blanks become plus signs and all other characters become hexadecimal values, preceeded by a percent sign.
The $ZL(arg) function returns the natural log of its numeric argument.
The $ZL(arg1,arg2) function returns a string consisting of the string given as arg1 padded to the right with blanks to the length given as arg2.
The $ZM(global) function returns the next row of the global array matrix.
The $ZN(arg) function normalizes word content for use with information storage and retrieval systems. It converts the word passed as an argument to upper case, removes non-alphabetics, and removes word stems. The result is returned as the value of the function.
The $ZP(arg1,arg2) function left justifies the first argument in a string whose length is given by the second argument, padding to the right with blanks.
The $ZR(arg) function returns the square root of its numeric argument.
The $ZS(arg) function takes one argument string which it passes to a shell for execution. Output is sent to standard out.
The $ZSQR(arg) functions return the square of its numeric argument. (Both functions are identical)
The $ZT function returns the byte offset in the currently open file. Similar to the C ftell() function.
The $ZVARIABLE(arg) function is used in the compiler to retrieve the value of the HTML FORM variable given as a string argument. When an HTML FORM sends data, it sends it under the name used in the FORM. Passing, as a string, this name to this function will retrieve the value from the FORM.
The $ZV1(arg) function determines whether the argument is a valid variable name format and returns 1 if true, 0 if false.
The $ZWI(arg) function loads an internal buffer with the string given as the argument. The contents of this buffer are returned by the $ZWN and $ZWP functions.
The $ZWN function returns successive words from the internal buffer delimited by blanks. When no more words remain, it returns an empty string (string of length zero).
The $ZWP function returns successive words from the internal buffer delimited by blanks and punctuation characters. When no more words remain, it returns an empty string (string of length 0)
$HOROLOG (Abbreviation $H)
- The $H built-in variable returns a string consisting of two
numbers. The first is the number of days since December 31, 1840 and
the second is the number of seconds since the most recent midnight.
The variable may not appear as the target of an assignment or READ
command. The Mumps gives these values relative to Greenwich Mean
Time.
$IO (Abbreviation: $I) -
$IO gives the current unit number. Mumps I/O is, at any given time,
directed to a given unit number. In Mumps, i/o, by default, is
directed to unit 5 - the user's console. This is the unit from which
all read's and write's will take place. If the user open's another
unit number for further file operations, the use command is used to
redirect the read and write commands to this unit. The $IO variable
indicates the current i/o unit number. It may not appear as the
target of an assignment statement or as an argument of a write
command although it may appear in both contexts as a source argument
such as in computation of an index of a target array.
$JOB (Abbreviation $J) -
The $JOB variable returns the system job number. This is the rocess
PID.
$STORAGE (Abbreviation $S)
- Always returns 999.
$X - The $X variable gives
the current horizontal position of the record in the current unit
number. For terminals, this is the horizontal cursor position. For
other files, it is the number of characters since the start of the
current record.
$Y- The $Y variable gives the vertical position of the
current unit number. It is pre-set to zero for each top of forms
format control used.
How To Write CGI Mumps Scripts
Writing Mumps scripts for CGI execution is relatively simple. The main difference between writing ordinary console based Mumps programs and those to be executed in the CGI interface of a web server concerns input/output.
Output
Normally in a Mumps program, you use the write statement to generate program output that appears as generated on the console running the Mumps program. When running under a web server, however, all your output will be captured by the web server and sent to the web browser. The web browser will format and place your output on the browser's screen.
In order to control the placement, font size, color and other factors concerning the display of your output, you must embed in your output HTML codes. The browser will use these in determining the manner in which to display your output.
Any output you write to the default output device (unit 5) will be sent to the browser by the web server. You may use the write statement to send both text to be displayed as well as HTML codes. For example, given that the variable ptid contains "1234":
write "<center> Patient ",ptid,"</center>",! will send the text: <center> Patient 1234 </center>
to the browser and this will cause the text to be centered on the browser's screen.
In order to speed the development process, this Mumps also supports another form of output that allows easier mixing of HTML and Mumps code. In a Mumps program, if a line does not contain a TAB character (required as the start character on a line with no label or the separater bewteen the label and the text of the line in a line with a label), the line will be written to the default (unit=5) output. During compilation, the compiler will will scan the line to be sent directly to the output stream for:
&! codes which will be replaced by line feeds (same as the ! in the write statement.
&~expression~ codes whose expression will be evaluated and the result substituted for the entire code. Thus, the above example could also appear as:
< center > Patient &~ptid~ < /center >
The following is a complete example
showing both write based and the extended output method mixed
together:
ZMain Content-type: text/html&!&! <html> <body bgcolor=SILVER><font size=3> <center><form method="get" action="verify.cgi"> <select name="ptid" size=18> Set n=x nxt Set n=$next(^px(n)) If n<0 Goto done If $e(n,1,1)'=x Goto nxt Set ptid=^px(n) <option value="&~ptid~"> &~^patient(ptid)~, &~^patient(ptid,"addr1")~, &~^patient(ptid,"city")~, &~^patient(ptid,"state")~<br> Goto nxt done Write "</select>",! <p><input type="submit" value="Select"></form></center></body></html> Halt |
This program creates a listbox containing the names of those patients whose last name begins with a letter whose value is in the variable "x". Note the following:
The first line (Content-type...) must be the first line of output sent to the web server. It must be followed by exactly two newline characters.
The next line establishes the browser environment.
The program then intitates an HTML form and sets verify.cgi as the program that will exceute to process data from this form. The web server invokes the verify.cgi and passes to it parameters that are sent by the browser when the user submits the form. Other prameters, passed by the browser in the form: parm=value will be parsed on receipt by the Mumps program verify.cgi when it is initialized. The name used as "parm" will become a Mumps variable and the value associated with it will be assigned to this variable when verify.cgi begins execution.
The HTML code now begin the setup of a listbox. The item selected from the box will become the value part of the ptid=value token that the browser will pass to the server.
Mumps program scans the patient data base for names that begin with the letter whose value is in the variable x. The value in x was passed to the Mumps program by a prior browser form screen.
Each patient name is entered into the listbox and it is associated with the value of the patient's id number. If a user clicks on a patient name and then on the Submit button, the token ptid=value is filled in by the browser with the patient's id number and it and the name of the Mumps program to execute are sent to the server.
Finally, each patient name and id having been entered into the listbox, the form is closed and a submit button placed on the screen.
The following is an example of a server side web script using Mumps and a SQL server. The code was tested using PostgreSQL under Linux. in fcns.h, the preprocessor variable POSTGRES was set to 1 and the NEW_TREE variable was defined and set to 1.
Root HTML file
The following is the root of the project, an HTML file that divides the screen into to horizontal frames. The upper frame will contain control buttons and the lower will contain data displays. The upper frame will be written by the Mumps program "table.cgi" which is a binary executable. The ".cgi" extension is required for executables by the settings on the web server on which this code was written. Other web servers may require different extensions or none.
<html> <!-- frame.html --> <frameset rows="15%,*" border=1> <frame marginheight=0 marginwidth=0 scrolling="auto" name="win1" src="table.cgi"> <frame marginheight=0 marginwidth=0 scrolling="yes" name="win2" src="blank.html"> </frameset> <body> </body> </html> |
table.mps
The program "table.cgi" was
created with the following Mumps program (table.mps):
Zmain Zd #include "cgi.h" # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # Patient Record System # Copyright (C) 2000 by Kevin C. O'Kane # # Kevin C. O'Kane # anamfianna@earthlink.net # okane@cs.uni.edu # # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # A consequence of the above is that if this code is incorporated # into another work, it causes that work to become covered by # the GNU GPL license. Licenses not covered by GNU GPL are available. # A copy of the GNU GPL appears at the end of the code. # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Write "Content-type: text/html ",!! Write "<html>",! Write "<script language='JavaScript'>",! Write "function clean() {",! Write "x=open('blank.html','win2','menubar=yes,resizable=yes,status=yes," Write "location=yes,toolbar=yes,scrollbars=auto'); }",! Write "</script>",! Write "<head><title>SQL Server (PostgreSQL)</title>",! Write "<body onLoad=""clean()"" bgcolor=silver>",! Write "<center>",! If '$Data(a1) Set a1="" If a1'="" Do . Set array="select ptid,namefirst,namemiddle,namelast,nameprefix,namesuffix from ptname" . Set array=array_" where ptid = '"_a1_"'" . Set array=array_";" . Set db="medical" Set user="" Set pass="" Kill ^tmp . Set rslt=$zodbc(db,user,pass,array,"tmp") . If rslt'=1 Do .. Write "Patient Not Found" .. Write "</body></html>",! .. Halt . Write $Piece(^tmp(1),"\",1)," ",$P(^tmp(1),"\",4),", " . Write $P(^tmp(1),"\",2)," ",$P(^tmp(1),"\",3),! If a1="" Write "All Patients",! Write "<table cellspacing=0 cellpadding=0 bgcolor=silver><tr><td>",! Write "<table width=70 border=1 bgcolor=skyblue><tr><td align=center>",! Write "<a target=win2 href=ptselect.cgi >Patient</a>",! Write "</td></tr></table>",! Set array="select ptid,icd,problem,onset,resolved,dxphys from problems " If a1'="" Set array=array_" where ptid = '"_a1_"' order by onset" Set txt="Problems",tbl="problems" Do btn Set array="select ptid,insur,insnbr,insname,emp,empaddr,emptel from financial" If a1'="" Set array=array_" where ptid = '"_a1_"'" Set txt="Financial",tbl="financial" Do btn Set array="select ptid,DOB,Gender,Race,Marital,PriPhys,PriPhysTel,PriPhysAddr," Set array=array_"EContact,ERelat,EAddr,ETel,PriPharm,PriPharmAddr," Set array=array_"PriPharmTel from demographic" If a1'="" Set array=array_" where ptid = '"_a1_"'" Set txt="Demographic",tbl="demographic" Do btn Set array="select ptid,street,city,state,zip,telephone,date from address" If a1'="" s array=array_" where ptid = '"_a1_"'" Set txt="Address",tbl="address" Do btn Set array="select ptid,namefirst,namemiddle,namelast,nameprefix,namesuffix from ptname" If a1'="" s array=array_" where ptid = '"_a1_"'" Set txt="Name",tbl="ptname" Do btn Set array="select ptid,date,time,test,result from labs " If a1'="" s array=array_" where ptid = '"_a1_"' order by date asc, time asc, test asc" Set txt="Labs",tbl="labs" Do btn Set array="select ptid,date,time,sys,dia from bp" If a1'="" s array=array_" where ptid = '"_a1_"' order by date asc, time asc" Set txt="BP",tbl="bp" Do btn Set array="select ptid,date,time,temperature from temperature" If a1'="" s array=array_" where ptid = '"_a1_"' order by date asc, time asc" Set txt="Temp",tbl="temperature" Do btn Set array="select ptid,date,time,med,init,finish,dose,freq,phys from meds" If a1'="" s array=array_" where ptid = '"_a1_"' order by date asc, time asc" Set txt="Meds",tbl="meds" Do btn Write "</table>" Write "</body> </html>",! Halt btn Set array=array_";" Write "<td><table border=1 bgcolor=skyblue><tr><td>",! Write "<a target=win2 href=sql.cgi?array=",$zhtml(array) Write "&ptid=",$zhtml(a1),"&t=",tbl,">",txt,"</a>",! Write "</td></tr></table>",! Quit |
Notes:
The "cgi.h" file is included in order to provide access to web server data passed through QUERY_STRING.
The "Content-type..." string is the first thing sent to the web server. It establishes the that the data following will be HTML code. It is followed by exactly 2 new line characters.
The initial HTML container is sent followed by a brief JavaScript applet that causes the other window to be cleared upon reloading of this program.
More HTML commands set up the environment, background and title.
The program test is "a1" exists. This will be the patient id. If it exists, it means a vale was passed by the web server in the URL (this value comes only from QUERY_STRING). If it does not exist, it is created and given a null value.
If it does not exist, the data base is queried for the patient name:
First, a SQL command is constructed to fetch elements of the patient name from the ptname table using the patient id as the key.
The data base (db) is set to "medical", and the user and password to null.
The global array ^tmp is deleted (Kill) so that any residual values in it from previous queries will not show.
The server is contacted passing the various data items and, as the last parameter, the name of the Mumps global array into which to store the results.
The number of rows is returned and this is checked. Only one row should result.
The row is stored in ^tmp(1). This is decomposed into its parts and they are written. Columns from the table are separated from one another by "\" symbols (see DELIM above).
The phrase "All Patients" is written if a1 was not passed. If a1 was not specified, data for all pateints will be retrieved.
An outer table is built - its purpose is to provide a framework into which the options will be placed.
Each option is presented as clikable text as the only element and row of a small table. This gives the visual impression of buttons.
A small table is created for the option to invoke the ptselect.cgi program. This program presents a list of patient names. Upon select one, it re-invokes table.cgi (this program). Thir re-load of table.cgi will cause the patient names display to disappear by invoking the JavaScript program. The output of the ptselect.cgi program appears in the lower window (target=win2). The window names were assigned when the frames were created.
Now a series of roughly similar button-like table objects are created. For each, an SQL Select command is established. If a1 has a non-null value, it is made part of the selection. If not, all rows of the target tables will be retrieved. The name of the table (tbl) and the text to be displayed in the button-like object (txt) are set. A branch to a sub-routine is then made (Do btn).
The routine at label btn creates the button-like object and generates the HTML code to display it in the upper window. It appends a required semi-colon to the end of the SQL query and creates the small table into which the link will be placed. The link invokes the program sql.cgi (which displays results) and passes to it (after the ? mark) parameters. The first parameter is the SQL command. The command, in the variable array, is encoded according to the HTML standard by the Mumps function $zhtml. The link also passes the patient id (also encoded) and the name of the table. Onle elements which might contain non-alphabetic or non-numeric characters need to be encoded with $zhtml. The text to appear in the box is displayed, the link concluded, the table ended, and return made to the point in the program from which the invocation was made.
When the user click on one of the
text links, the sql.cgi program is invoked and it is passed the
parameters of the query. It processes the query and displays the
results.
sql.mps
Zmain Zd #include "cgi.h" # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # Patient Record System # Copyright (C) 2000 by Kevin C. O'Kane # # Kevin C. O'Kane # anamfianna@earthlink.net # okane@cs.uni.edu # # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # A consequence of the above is that if this code is incorporated # into another work, it causes that work to become covered by # the GNU GPL license. Licenses not covered by GNU GPL are available. # A copy of the GNU GPL appears at the end of the code. # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Write "Content-type: text/html ",!! Write "<html><head><title>SQL Server (PostgreSQL) Search</title></head>",! Write "<body bgcolor=silver>",! If $Data(array)=0 Do . Write "No Table specified. </body></html>",! . Halt Set db="medical" Set user="" Set pass="" Kill ^tmp Set rslt=$zodbc(db,user,pass,array,"tmp") If rslt=0 Do . Write "*** Empty result",! . If $Data(ptid)=0 Halt . If ptid="" Halt . Set ^tmp(1)=ptid_"\" Write "<hr>",! Set dat=$Piece(array," ",2) Set ^tmp(0)=rslt_"\"_dat Write "<center><table border bgcolor=skyblue>",! Set x="^tmp(0)" Write "<tr>",! For %=1:1 Do . Set p(%)=$Piece(dat,",",%) . If p(%)="" Break . Write "<th>",p(%),"</th>",! Set %=%-1 If rslt=0 Do . For i=1:1:%-1 Set ^tmp(1)=^tmp(1)_"empty\" . Set rslt=1 Write "</tr>",! For i=1:1:rslt Do . Set e="" . Set a=^tmp(i) . Write "<tr>" . For %%=1:1 Do .. Set z=$Piece(a,"\",%%) .. If z="" Break .. If z="\" Set z=" " .. Write "<td><font size=2>",z,"</td>",! .. If e'="" Set e=e_"\" .. Set e=e_p(%%)_"="_z . Write "<td><a href=add.cgi?s=",$zhtml(e),"&t=",t,">Add</a>",! . Write "<td><a href=update.cgi?s=",$zhtml(e),"&t=",t,">Edit</a>",! . Write "<td><a href=delete.cgi?s=",$zhtml(e),"&t=",t,">Delete</a></tr>",! Write "</table></body></html>",! Halt |
Notes:
The first part of the program establishes the environment with the web server and send the usual beginning of an HTML document.
The program checks to see if the variable "array" was sent by the web server and halts if not. The "array" variable is communicated through the HTML link that invoked this program and QUERY_STRING. The "cgi.h" module automatically extracts, decodes and instantiates "array" and stores in it the value passed.
The program sets up parameters to the SQL server including the data base name, password, and user. The global array ^tmp is deleted as this is where results will be placed. The SQL server is called and the last parameter, "tmp" tells the interface to store the reults in the global array ^tmp.
If the number of rows returned is zero and the patient id (also instantiated by "cgi.h"), the program halts with a message. If ptid was provided, a dummy entry in ^tmp(1) is created. This provides a stem for an "Add" operation into an empty table.
The variable "dat" is set with the second part of the SQL command. The SQL command second part (as delimited by blanks) contains the names of the columns to be retrieved in the query. The variable "dat" will contain a comma delimited list of these column headings.
A table is constructed and the elements of "dat" become the column headings. Note the non-standard C-like use of Break to exit a group. A special case is handled for retrieval of zero rows.
For each element of ^tmp, each column value, delimited by "\" (DELIM) is printed as a row element. At the end of each row are printed links to update, add, or delete a row.
The following is a display of roughly how the programs'
output windows appear using completely randomly generated data:
All Patients
ptid |
date |
time |
test |
result |
|||
---|---|---|---|---|---|---|---|
1005 |
1999-Jul-11 |
12:29 |
Glucose |
10 |
|||
1005 |
1999-Jul-11 |
12:29 |
Hct |
10 |
|||
1005 |
1999-Jul-11 |
12:29 |
Hgb |
10 |
|||
1005 |
1999-Jul-12 |
12:31 |
Glucose |
12 |
|||
1005 |
1999-Jul-12 |
12:31 |
Hct |
12 |
|||
1005 |
1999-Jul-12 |
12:31 |
Hgb |
12 |
|||
1005 |
1999-Jul-13 |
12:32 |
Glucose |
14 |
|||
1005 |
1999-Jul-13 |
12:32 |
Hct |
14 |
|||
1005 |
1999-Jul-13 |
12:32 |
Hgb |
14 |
|||
1005 |
1999-Jul-14 |
12:33 |
Glucose |
16 |
|||
1005 |
1999-Jul-14 |
12:33 |
Hct |
16 |
|||
1005 |
1999-Jul-14 |
12:33 |
Hgb |
16 |
|||
1005 |
1999-Jul-15 |
12:34 |
Glucose |
18 |
|||
1005 |
1999-Jul-15 |
12:34 |
Hct |
18 |
|||
1005 |
1999-Jul-15 |
12:34 |
Hgb |
18 |
|||
1005 |
1999-Jul-16 |
12:35 |
Glucose |
20 |
|||
1005 |
1999-Jul-16 |
12:35 |
Hct |
20 |
|||
1005 |
1999-Jul-16 |
12:35 |
Hgb |
20 |
|||
1005 |
1999-Jul-17 |
12:36 |
Glucose |
22 |
|||
1005 |
1999-Jul-17 |
12:36 |
Hct |
22 |
|||
1005 |
1999-Jul-17 |
12:36 |
Hgb |
22 |
|||
1005 |
1999-Jul-18 |
12:37 |
Glucose |
24 |
|||
1005 |
1999-Jul-18 |
12:37 |
Hct |
24 |
|||
1005 |
1999-Jul-18 |
12:37 |
Hgb |
24 |
|||
1005 |
1999-Jul-19 |
12:38 |
Glucose |
16 |
|||
1005 |
1999-Jul-19 |
12:38 |
Hct |
16 |
|||
1005 |
1999-Jul-19 |
12:38 |
Hgb |
16 |
Other programs are of similar structure. You may download the entire collection from the Mumps home page (db_demo.tar.gz).
The following is a file of Mumps code that I use to checkout the compiler (sorry, TABS are not visible but they are in the HTML source). Copies of this file are available for FTP from my website.
#......................................................... # Copyright (c) MM by Kevin C. O'Kane, All Rights Reserved # Mumps interpreter checkout code - not exhaustive. # Examples of code that works (mostly - the pattern # match has problems). #......................................................... ; comments may either begin with a semi-colon like ; this PROVIDED that the semi-colon is placed where ; a command would go. Semi-colons may not be used ; prior to the required TAB character at the beginning ; of a line. # This is also a comment - it must be the very 1st thing on # a line and have NO TAB characters. ######################################################################### # this is a separate extrinsic. it compiles # to a separate C function rather than part of # the main function. it may also be compiled # separately. see documentation. # separate functions must appear before they are used. ^subFunction(a,b,c) ; I am a separate extrinsic - a C sub function. ; I must appear before I am used quit a+b+c ^test3(xxx) ; fcn write !,"****** linked function ",xxx,! quit ######################################################################### Zmain write !!,"Compiler Checkout Routines",!! write "Builtin variables (expected ... actual)",!! write "$fnumber(42000,""P"",2) ",?65,$fn(42000,"P",2),! write "$io",?45,"5 ",?65,$io,! write "$zd",?45,"current date ",?65,$zd,! write "$h",?45,"current date ",?65,$h,! if 1=1 set i=1 write "$test",?45,"1 ",?65,$test,! write "$TEST",?45,"1 ",?65,$TEST,! write "$T",?45,"1 ",?65,$T,! write "$t",?45,"1 ",?65,$t,! if 1=0 set i=1 write "$test",?45,"0 ",?65,$test,! write "$job ",?65,$job,! write "$storage",?45,"999 ",?65,$s,! write "$x $y",?65,$x," ",$y,! write !,"Functions (expected ... actual)... ",!! write "$ascii(""Beethoven"")",?45,"66 ",?65,$ascii("Beethoven"),! write "$ascii(""HAYDEN"",3)",?45,"89 ",?65,$ascii("HAYDEN",3),! write "$CHAR(66,-1,65,67,72)",?45,"BACH ",?65,$CHAR(66,-1,65,67,72),! set a="66" write "$char(a)",?45,"B ",?65,$c(a),! set ^a(1)=1 set ^a(2)=1 set ^a(1,1)=11 set ^a(3,1)=1 write "$data(^a(""none""))",?45,"0",?65,$data(^a("none")),! write "$data(^a(1))",?45,"11",?65,$data(^a(1)),! write "$data(^a(1,1))",?45,"1",?65,$data(^a(1,1)),! write "$data(^a(2))",?45,"1",?65,$data(^a(2)),! write "$data(^a(3))",?45,"10",?65,$data(^a(3)),! write "$data(none)",?45,"0",?65,$data(none),! set i=1 write "$data(i)",?45,"1",?65,$data(i),! set xxx(1)=1 write "$data(xxx(1)",?45,"1",?65,$data(xxx(1)),! set xxx(1,1)=11 write "$data(xxx(1)",?45,"11",?65,$data(xxx(1)),! set yyy(1,1)=99 write "$data(yyy(1)",?45,"10",?65,$data(yyy(1)),! write "$data(yyy)",?45,"10",?65,$data(yyy),! write "use $next, not $order",! for i=1:1:10 s a(i)=i write "$next(a(-1))",?45,"1",?65,$n(a(-1)),! write "$next(a(1))",?45,"10",?65,$n(a(1)),! write "$next(a(10))",?45,"2",?65,$n(a(10)),! write "$extract(""Brahms"")",?45,"B",?65,$extract("Brahms"),! write "$extract""Handel"",5)",?45,"e",?65,$extract("Handel",5),! write "$extract*""Mozart"",4,6)",?45,"art",?65,$extract("Mozart",4,6),! write "$find(""SIBELIUS"",""I"")",?45,"3",?65,$find("SIBELIUS","I"),! set i=3 write "i=3",! write "$find(""SIBELIUS"",""I"",i)",?45,"7",?65,$find("SIBELIUS","I",i),! write "$find(""SIBELIUS"",""I"",7)",?45,"0",?65,$find("SIBELIUS","I",7),! write "$justify(""3.14159"",9)",?45,?65,"xx3.14159",?65,$justify("3.14159",9),! write "$justify(""3.14159"",9,2)",?45,?65,"xxxxx3.14",?65,$justify("3.14159",9,2),! write "$length(""Verdi & Wagner"")",?45,"14 ",?65,$length("Verdi & Wagner"),! write "$length(""Verdi & Wagner"",""&"")",?45,"2",?65,$length("Verdi & Wagner","&"),! write "$length(""Verdi & Wagner"","" "")",?45,"3",?65,$length("Verdi & Wagner"," "),! set B3="Beethoven,Bach,Brahms" write "set B3=""Beethoven,Bach,Brahms""",! write "$piece(B3,"","")",?45,"Beethoven",?65,$p(B3,","),! write "$piece(B3,"","",3)",?45,"Brahms",?65,$p(B3,",",3),! write "$piece(B3,"","",1,2)",?45,"Beethoven,Bach",?65,$p(B3,",",1,2),! write "$random(9)",?45,"{0,8}",?65,$random(9),! write !,"Operators ...",!! write "Unary +""27.3 days""",?45,"27.3",?65,+"27.3 days",! write "Unary - -""3.14 radians""",?45,"3.14",?65,-"3.14 radians",! write "Sum 2.718+""2 above e""",?45,"4.718",?65,2.718+"2 above e",! write "Difference 2.12-""6.3 eV""",?45,"4.18",?65,2.12-"6.3 eV",! write "Product 1.00794*""2 atoms/H2""",?45,"2.01588",?65,1.00794*"2 atoms/H2",! write "Division 144.132/12.011",?45,"12",?65,144.132/12.011,! write "Integer Division 82.8\""29.5 years/orbit""",?45,"2",?65,82.8\"29.5 years/orbit",! write "Modulo works like C modulo",! write "Modulo 42#5",?45,"2",?65,42#5,! write "Modulo -42#5",?45,"3",?65,-42#5,! write "Modulo 42#-5",?45,"3",?65,42#-5,! write "Modulo -42#-5",?45,"2",?65,-42#-5,! write "Less Than 1642<1879",?45,"1",?65,1642<1879,! write "Greater Than 1452>1564",?45,"0 ",?65,1452>1564,! write "Concatenate ""Feynman ""_1918",?45,"Feynman 1918 ",?65,"Feynman "_1918,! write "Equals 1969-5=1964",?45,"1",?65,1969-5=1964,! write "Equals 1967=""1967: M""",?45,"0",?65,1967="1967: M",! write "Equals 1966=01966",?45,"1",?65,1966=01966,! write "Equals 1966=""01966""",?45,"0",?65,1966="01966",! write "Equals ""Lovelace""=""Hopper""+2",?45,"2",?65,"Lovelace"="Hopper"+2,! write "Equals ""Lovelace""=""Lovelace""+3",?45,"3",?65,"Lovelace"="Lovelace"+2,! write "not equals ",?45,"0 ",?65,1969-5'=1964,! write "not equals ",?45,"1 ",?65,1967'="1967: M",! write "not equals ",?45,"0 ",?65,1966'=01966,! write "not equals ",?45,"1 ",?65,1966'="01966",! write "not equals ",?45,"3 ",?65,"Lovelace"'="Hopper"+2,! write "not equals ",?45,"2 ",?65,"Lovelace"'="Lovelace"+2,! write "Contains ",?45,"1 ",?65,"Darwin"["win",! write "Contains ",?45,"1 ",?65,"Linnaeus"["",! write "Follows ",?45,"0 ",?65,"COPERNICUS"]"KEPLER",! write "Pattern ",?45,"0 ",?65,"Leakey"?1a,! write "Pattern ",?45,"1 ",?65,"Boaz"?1.a,! write "Pattern ",?45,"1 ",?65,"Fossey"?1u.5l,! write "Pattern ",?45,"0 ",?65,"Goodall"?.4l.p6c.e,! write "And ",?45,"0 ",?65,"Watson"&"Crick",! write "And ",?45,"0 ",?65,"Morgan"&1735,! write "And ",?45,"1 ",?65,1838&1839,! write "And ",?45,"1 ",?65,-12000&1996,! write "And ",?45,"0 ",?65,1859&0,! write "Or ",?45,"0 ",?65,"Jennifer"!"Pasteur",! write "Or ",?45,"1 ",?65,"Hoffman"!1928,! write "Or ",?45,"1 ",?65,1898!-400,! write "Or ",?45,"1 ",?65,1867!0,! set a="aaa$bbb$ccc" set b="$" set $p(a,b,2)="test" write "setpiece: ",a,! write "exponention works",! set a=3 set a=a**2 write "exponentiation: ",a,! set a=9 set a=a**.5 write "exponentiation: ",a,! write "Not ",?45,"1 ",?65,'"Turing",! write "multiple not signs doesn't work",! ; write "Not 0 ",''"Babbage",! write "Not ",?45,"1 ",?65,'"Backus"&1957,! write "Not ",?45,"1 ",?65,'("Wirth"&"Codd"),! write !!,"Command examples...",!! write "Open a file for output and write to it...",! open 1:"tmp.tmp,new" use 1 for i=1:1:10 write "test ",i,! close 1 use 5 write "Reopen the same file and read from it ...",! open 1:"tmp.tmp,old" for i=1:1 use 1 read a quit:'$t use 5 write "***",a,! use 5 close 1 write "Set command ...",! set a=1+(2+(3+(4+5))) set:1=1 a=1+(2+(3+(4+5))) set a=0 write "0 ",a,! write "for command ...",! for i=1:1:10 write " ",i write ! for i=1:1 quit:i>10 write " ",i write ! write "Goto based loops...",! set i=1 lab1 write i," " set i=i+1 if i<11 goto lab1 write ! write "Init a global and Next thru it ...",! for i=1:1:10 for j=1:1:10 set ^a(i,j)=i_","_j for i=1:1:10 write ! for j=1:1:10 w ^a(i,j)," " write ! set i=-1 lab2 set i=$next(^a(i)) if i<0 goto lab3 write !,i,":" set j=-1 lab2a set j=$next(^a(i,j)) if j<0 goto lab2 write ^a(i,j)," " goto lab2a lab3 write ! # This version of mumps normally stores numeric subscripts as strings. # this means that, on retrieval, the order will be according to a # character collating sequence. The ZNUMBER command causes numeric # subscripts to be stored padded to the left with blanks to a fixed # length field of width 8. Thus, positive integers will appear # in numeric order when retrieved. ZNUMBER must be ineffect when # the array is created. znumber write !,"Init a global and Next thru it using cannonical numbers...",! for i=1:1:10 for j=1:1:10 set ^b(i,j)=i_","_j for i=1:1:10 write ! for j=1:1:10 w ^b(i,j)," " write ! set i=-1 xlab2 set i=$next(^b(i)) if i<0 goto xlab3 write !,i,":" set j=-1 xlab2a set j=$next(^b(i,j)) if j<0 goto xlab2 write ^b(i,j)," " goto xlab2a xlab3 write ! ; too confusing - works but disabled ; read "read with prompt - enter something: ",a ; write "you wrote ",a,! write "Kill test ...",! kill ^a(5) write "6 ",$n(^a(4)),! set i=1 set j=2 write "do i and j exist? ",$d(i)," ",$d(j),! kill i,j write "do i and j exist now? ",$d(i)," ",$d(j),! set i=1,j=2,k=3 write "do i, j and k exist? ",$d(i)," ",$d(j)," ",$d(k),! kill (i,k) write "do i, j and k exist now? ",$d(i)," ",$d(j)," ",$d(k),! set i=10 set j="i" kill @j write "Hang test for 1 second ...",! hang 1 write "Was that 1 second?",! # I'm not sure about all these. $zd is safe, though. write "Horolog (wrong) ",$zd," ",$h,! write "$zd ",$zd,! write "$zd1 - seconds since Jan 1, 1970: ",$zd1,! write "$zd2($zd1): ",$zd2($zd1),! write "$zd3(1998,11,25) (day of year) ",$zd3(1998,11,25),! write "$zd4(1998,329) (Nov 25 1998) ",$zd4(1998,329),! write "$zd5(1998,11,25) ",$zd5(1998,11,25),! write "$zd6 ",$zd6,! write "$zd7 ",$zd7,! write "$zd8 ",$zd8,! set i="test" set j=9 write "select (test): ",$select(j+1:i,1:2),! # New only works in a dotted indent. # If used anywhere else, you cannot undo it. write "New command - limited service",! write "if you use a goto to leave a block, the symbol table",! write "is not restored",! set i=99 write "before ",i,! do . write "in block before New ",i,! . new . set i=100 . write "in block after new ",i,! write "after bloock ",i,! set b=1 set c=2 set aa(1,2)="test array" set a=aa(b,c) write "array test ... ",a," ",aa(b,c),! write "block test",! for j=1:1:5 do . write "I am an outer block ",j,! . for i=1:1:5 do .. write "I am an inner block ",i,! .. quit . quit write "I am not a block",! write "Value returned by argumentless function: ",$$test1,! Set x=123 Do ^test3(123) # on entry into a function, a New is done. # the arguments are instantiated - these become, initially, the # only variables in the function. on exit, the New is undone. write "Value returned by function: ",$$test2(1,2,3),! # separately compiled extrinsic - appears at front of program write "Value returned by separately compiled extrinsic ",$$^subFunction(1,2,3),! # indirection can be used with IF, SET and WRITE only. write "and, finally, for something completely different...",! write "indirection...",! set i="2*3" set %=@i write "@""2+3"" ",%,! set ^a(1)=99 set i="^a(1)+1" set %=@i write "@",i,"=>",%,! set i="^a(j)" set j=10 set @i=999 set %=@i write %,! if @i=999 write "yes",! halt test1 set i=12345 quit "xxx"_i test2(a,b,c) ; I must have a tab quit a+b+c
Appendix B
GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. <> GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) <> These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. <> 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. <> 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS <> How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) 19yy <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
In March, July, October and May, the Ides fall on the fifteenth day.