SML PROGRAMMING

Retrieving Data

This article covers three basic sources of input to an SML program: arguments, user prompts, and files (menus and forms will be discussed in future articles).

Passing Arguments

Arguments passed to an SML program are automatically assigned to global variables according to rank; that is, argument 1 is assigned to variable 1, argument 2 to variable 2, and so on. Note that a string containing blanks must be surrounded by double quotes if it is to be treated as a single argument; for example, if you were to run birthday.sml as follows:

   &run birthday Fred is 12 years old today
"Fred" would be assigned to variable 1, "is" to variable 2, "12" to variable 3, and so on. However, if you were to type:

   &run birthday Fred "is 12 years old" today
"Fred" would be assigned to variable 1, "is 12 years old" to variable 2, and "today" to variable 3.

Arguments may be passed to local variables via the &r directive[1]. The advantages of this are two-fold: 1) arguments will not wipe out global variables, and 2) if arguments are absent, those variables will be blank; there is no danger of residue from other routines. The latter is especially helpful for routines with default arguments. Also, if no arguments are given, the routine's usage may be displayed:

&goto usage &if &eq "x%-1" "x"
&goto usage &if &eq "x%-1" "x/?"
&goto usage &if &eq "x%-2" "x"
&sv -7 %-1
&sv -8 %-2
   &goto true01 &if &eq "x%-3" "x"
   &goto false01 &if &ne "x%-3" "x#"
   &label true01
      &sv -9 1
   &goto end01
   &label false01
      &sv -9 %-3
   &label end01
   &goto true02 &if &eq "x%-4" "x"
   &goto false02 &if &ne "x%-4" "x#"
   &label true02
      &sv -10 0
   &goto end02
   &label false02
      &sv -10 %-4
   &label end02
...
&return

&label usage
&type "Usage: &r NARROW [x] [y] {width} {angle}"
&type " "
&type "            x = midpoint x coordinate in page units"
&type "            y = midpoint y coordinate in page units"
&type "        width = width of north arrow in page units"
&type "                (default = 1)"
&type "        angle = angle of north arrow in decimal degrees"
&type "                (default = 0)"
&return
In the above example, if the user were to type "@narrow" or "&r narrow /?", the routine's usage would be returned. If the third and fourth arguments were omitted, the default values would be substituted. (The &goto directive will be discussed in the next article.)

[TIP: In addition, you can create simple HELP support for an SML by creating a text file in the appropriate help directory. For example, if you have a program sb.sml to generate a scalebar in ARCPLOT, you could create a help file named @sb in the %ARC%\HELP\ARCPLOT directory. Then, at the ARCPLOT command prompt, you can invoke the file by typing "HELP @SB" (I recommend the "@" as a reminder that the help file represents an SML program and not an ARCPLOT command). Look at the existing files to get an idea of the format you should follow.]

Returning Values

The &return and &rv directives allow an SML to return a value to the variable specified by parent routine. The following example replaces the old "SML Developer's Toolkit" sysprogr.sml, which no longer works in the 3.5 Windows Extensions:

sysprogr.sml

&value -1 WKSP
 &goto false &if &FN %-1t$arc.bat
   &return ARC
 &label false
&open %-1t$arc.bat error
&read -2 error
&read -2 error
&read -2 error
&read -2 error
&extract -3 -2 2
&value -4 -3 %<lpos \ %-3 + 1>
&return %-4

&rem **** I/O error

&label error
&type "I/O Error:  SYSPROGR"
&return ERROR
If a parent routine executes the following lines:

&r sysprogr
&rv -11
The argument given in SYSPROGR's &return statement will be assigned to variable -11. Note that more one value may be returned, as in this example taken from the Online Help:

&routine ask
&ask -10 "First number:"
&ask -4 "Second number:"
&r compute %-10 %-4
&rv -5 -6

&routine compute
&calcvar -3 %-1 + %-2
&calcvar -4 %-1 * %-2
&return %-3 %-4
Of course, if a value to be returned contains spaces, it should be enclosed in double quotes.

User Input

At any time during its execution an SML program may prompt the user for one of four types of input: a keystroke, a Y or N response, a string of text, or a coordinate point.

The &key directive prompts the user for a single keystroke, and may be used to construct a simple menu of options, for example:

   &key 1 "A=pan E=extent F=full I=in O=out V=pan/in X=pan/out"
   &extract 1 1 1
   &goto pan &if &eq %1 A
   ...
Because &key is case sensitive, &extract is used in the above example to convert the response to upper case. Note that if a function key is pressed as a response to &key, the resulting character depends on whether the key has been redefined using &setkey[2]: if the key has not been redefined, the response will be the number of the key ("0" for <F10>); if it has been redefined, the response will be the first character of the string assigned to that key.

The &query directive expects the user to type "Y" or "N", followed by the <Enter> key (upper case is always returned):

   &query 1 "File exists.  Overwrite" &N
   &goto cancel &if &eq %1 N
In the above example,"&N" indicates that the default response is "N" if the <Enter> key alone is pressed; similarly, the "&Y" option indicates that "Y" is the default response, and "&F" (the default option) indicates that the prompt will be repeated until "Y" or "N" is entered. &query appends a space plus a query prompt to the given string, the actual prompt depending on whether a default response is supplied: "(Y or N)?" if "Y" is the default answer, "(N or Y)?" if "N" is the default answer, and "(Y/N)?" if there is no default answer.

There are two SML directives to prompt the user for text: &ask and &response. Whereas &response assigns the literal text of a response to an SML variable, &ask assigns only those characters before the first space and converts them to upper case. As with &query, a default answer (or "&F") may be specified, for example:

   &response 1 "Enter user name: " "Tina Turner"
   &ask 2 "Enter project code: " &F
In the first line, if the user presses the <Enter> key, the string "Tina Turner" will be returned; in the second line the prompt will be continued until a string containing at least one character is entered. If the project code response were "a001 x", the value assigned to variable 2 would be "A001 ". Note that no additional space or prompt is appended to the given string. Note also that "&F" is not the default option for &ask or &response: if neither a default response or "&F" is supplied, a null response ("") will be returned if the user presses the <Enter> key.

Coordinate points are obtained from the user via one of four directives: &getxy, &getxyc, &getxym, and &getxyp, the difference being the coordinate system which is used to assign the returned values. &getxym returns values in map coordinates, &getxyp returns values in page (graphic display) coordinates, and &getxy returns page coordinates unless a mapextent has been defined, in which case map coordinates are returned. In ARCEDIT, the coordinate device may be the screen cursor, mouse, or digitizer, depending on the current COORDINATE setting (the command will not work properly when COORDINATE is set to KEYBOARD). In ARCPLOT, the coordinate device may be the screen cursor or mouse.

&getxyc returns values in row/column coordinates, and works differently if you are using DOS vs. Windows Extensions. In DOS, the entire primary monitor screen is used to input coordinates; in Windows, the dialog window is used. &getxyc always uses the screen cursor or mouse even if COORDINATE is set to DIGITIZER.

All four commands return three values: the x coordinate, the y coordinate, and the key of the coordinate device which is pressed[3]. All commands require that the graphic display (DISP 4) is active. Here is an example routine (t.sml) which adds text to a map composition in ARCPLOT:

   UNITS PAGE
   &response 1 "Enter text: "
   &goto end &if &eq %1
      &type "Point to location (9 to cancel):"
      &getxyp 2 3 4
      &goto end &if &eq %4 9
         MOVE %2 %3
         TEXT '%1'
   &label end
In the above example, the user is first asked for a string of text. If <Enter> is pressed, the routine skips to the end; otherwise, the user is asked for a coordinate. The value of the key pressed is checked to see if the user wants to abort at the last second.

Reading and Writing Files

Directives associated with file input are &open, &read, and &close. Each directive supports up to three file handles (1, 2, or 3)[4], defaulting to 1.

The &open directive establishes the file which is to be read. An error routine label must be provided to jump to in case the file cannot be found or opened (e.g. if it is locked by SHARE).

[TIP: Many of the programs in the SML Developer's Toolkit (e.g. sysdate.sml) direct some form of command output to a temporary file and then read the file to extract the desired data. If your program runs one of these SMLs and at the same time needs to keep an input file open, assign it file handle 2 or 3 to prevent conflicts.]

The &read directive reads a line from the opened file and assigns the text to the specified variable. You should provide a label to jump to in case the end-of-file is encountered[5] and optionally the file handle. &close closes the opened file; otherwise, the file will remain open (and inaccessible by other applications) until that particular session is terminated. The &close directive also has an &all option to close all opened files. For example, the following routine (lshift.sml) reads a list of MOSS export files to be processed by yshift.sml (note the use of file handle 3):

   &rem Arguments: 1 = name of file list
   &rem            2 = y shift (e.g. -4000000)
   &open %1 error 3
   &label do
      &read 1 end 3
      &type "Converting %1.MOS..."
      &run yshift
      &goback do
   &label end
   &close 3
   &return
   &label error
   &type "Error:  %1 cannot be opened"
   &return
The directives to write text to files are similar to those for reading files: &openw, &write, and &closew. &openw establishes the file which is to be written to. Up to three output files may be open at a time. By default, if the specified file exists, it will be overwritten; use the "A" option to append to an existing file. As was mentioned in the first article of this series, &openw may be used to redirect command output to a file:
   &value 10 WKSP
   SELECT OWNER = %1 XOR TRS = %2
   &openw %10t$temp.lis
   WHO
   &closew
   NSELECT
   RESELECT OWNER = %3
   &openw %10t$temp.lis a
   WHO
   &closew
   &open %10t$temp.lis error
   ...
&closew closes an output file so that it may subsequently be read either by the SML routine or by another application; the &all option closes all open output files. The &write directive allows specific text to be written to the output file, for example:

   &write "%11"
   &write "User: %1  Project: %2"
As with &type, if there is a possibility of embedded blanks in the contents of a variable, the reference should be enlosed in double quotes. One file may be read and another written concurrently, as is shown in the following example (yshift.sml) which adds a y shift to a MOSS export file before generating a coverage:

   &rem Arguments: 1 = name of file w/o .MOS extension
   &rem            2 = y shift (e.g. -4000000)
   &rem **** open input file, jump to "error" if fail
   &open %1.mos error
   &rem **** open output file
   &openw %1.m2
   &label do
      &rem **** input line, jump to "end" if EOF
      &read 21 end
      &rem **** check for polygon header
      &val 22 21 9 9
      &goto header &if &ne %22 .
         &rem **** extract vertex coordinates
         &val 11 21 1 11
         &val 12 21 13 22
         &rem **** add y shift and write new line
         &cv 13 %12 + %2
         &sv 14 %13 (F10.2)
         &write "%11 %14"
      &goback do
      &label header
         &rem **** write header line
         &write "%21"
      &goback do
   &label end
   &close
   &closew
   MOSSARC %1.m2 %1 POLY
   &return
   &rem **** i/o error routine
   &label error
   &type "Error:  %1.MOS cannot be opened"
   &stop
Note the line which reformats variable 13 to the FORTRAN F10.2 format and assigns the results to variable 14: this feature of &sv, which was not discussed in the previous article, is a relatively simple way to reformat variable contents (see the &setvar entry in the Online Help for other interesting examples). Note also the &stop directive: this halts the execution of all programs within the current SML processor; that is, if this routine were called by a parent SML (such as lshift.sml), control would not be returned to it—everything would stop dead and the command prompt would appear[6].

Next: Loops and Branches


[1]Note that "@" is still equivalent to &run and should not be used to execute routines which expect arguments to be passed to local variables.

[2]The &setkey directive allows the user to assign a particular string to a function key or function key combination; this provides a useful shortcut for executing frequently used commands and SMLs (see Customizing PC ARC/INFO 3.5.X for an example).

[3]See &getxy in the online help for discussion of key values. If you use a digitizer, the <0> key will cause crosshairs to appear at the cursor location on the screen, the <A> key will return "10", and the <B> key will return "12"; only map coordinates will be returned whether you use &getxy, &getxym, or &getxyp: table coordinates will only be returned if COORDINATE DIGITIZER defaults to table coordinates.

[4]Attempting to open more than three will lead to strange results.

[5]Technically the label is optional, but if the routine encounters an EOF and no label is specified, there will be an SML error, causing the routine to bomb.

[6]However, if the routine were running within a module such as TABLES, which in turn was executed from an SML at the ARC level, once the user entered "Q" at the TABLES command prompt the parent SML at the ARC level would resume execution.


Return to ArcTips page