APPENDIX C: Tips and Pitfalls This reference documents the major features of the SPP language. However, it is necessarily incomplete. For the most complete and up-to-date details of any specific library package or procedure, consult the on-line source and documentation. There is high-level documentation in the IRAF doc$ directory. The source for each library package described here, imio, clio, etc., resides in a separate directory in the IRAF hierarchy, having the name of the package. In addition, a cl environment variable is defined for each library package. Thus, the source for imio is in the directory imio$. There is a directory containing documntation describing the packages in a doc subdirectory of each library package and the source also contains documentation. Procedure Arguments If a procedure has formal parameters, they should agree in both number and type in the procedure declaration and when the procedure is called. In particular, beware of short or char parameters in argument lists. An int may be passed as a parameter to a procedure expecting a SHORT inte- ger on some machines, but this usage is not portable, and is not detected by the compiler. The compiler does not verify that a procedure is declared and used consistently. Do not use type coercion in procedure actual arguments. Such as: call foobar (..., short (intvar), ...) In some cases, the coercion is not performed in passing the argument to the procedure. A particular problem is using a literal (quoted) character in the calling sequenct to a procedure expecting a char such as stridx(). Such a literal is converted into an integer constant. On some systems, it won't matter if the called procedure expects a long or short integer, but on some, it will result in the wrong value passed. Calling Fortran Since SPP is preprocessed into Fortran, in most cases, it is quite straightforward to call an existing Fortran subroutine from an SPP proce- dure. The most important caution is the case of character strings. SPP strings are not the same as Fortran strings. SPP strings are implemented as arrays of integers. However, there are procedures available to transform between the two: f77pak() converts an SPP string to a Fortran string, and f77upk() converts a Fortran string to an SPP string. Note that you must declare the Fortran string in the SPP procedure with a Fortan state- ment. This is possible with the % escape character as the first character on a line. This indicates to the xc compiler that the following statement should not be processed but copied directly to the Fortran code. See Example C.9, below. Example C.9: Declaring a Fortran String in SPP. Character Strings SPP strings are not scalar variables. Their value cannot be changed by an assignment statement. Strings are, in fact, arrays of short integers, with the additional complication of an extra element at the end for the EOS char- acter. It is possible to declare strings with dynamic memory allocation. In fact, is a common practice to use stack memory for temporary string stor- age. Example C.10: Stacking Memory for Temporary String Storage. Arrays of Strings It is possible to declare an array of strings, but remember that each string element needs its own EOS character. Typically, the strings would be allocated dynamically and referenced in a called procedure, as shown in Example C.11. Example C.11: Referencing Dynamically Allocated Strings. The important points to keep in mind are that strings implemented as arrays of chars (shorts), even though they are declared a fixed size, they may not use the entire declared space. A special character value (EOS, implemented as ASCII NUL) is used as the string terminator. Most proce- dures that require strings also take an argument specifying the string length. This does not mean that the entire declared string will be used, only the maximum possible string size. There are a few important exceptions. Characters vs. Strings Note the distinction between single and double quoted characters. Sin- gle quotes indicate the ASCII value of a single character and are treated as an int scalar in processed SPP. Double quoted strings are literal strings and may only be specified as actual procedure arguments or the object of a string declaration. Using single quoted characters in place of a char array can cause unexpected problem, for example in: stridx ('x', string) 'x' is an int, while stridx() expects a char. Other routines with this problem include ungetc() and putc(). Note that the cast operator char ('x') does not work! It translates into int(120). You should use something like: char x_char x_char = 'x' i = stridx (x_char, string) Formatted I/O Newlines are significant. Lines of output, to STDOUT for example, is separated by newlines, a carriage return and a line feed. The printf() procedure does not automatically issue a newline with every call. You must explicitly write the newlines using the \n escape as part of the format string. Otherwise, your output will be strung together, rather unintelligably. Actually, this can be useful, as you can use multiple printf() calls to build a single line of output. On input, a text file consists of lines delimited by newlines. The file may be read line by line using getline(). The newline terminating each line is returned as part of the string. Note that getline() and putline() are two of the procedures dealing with strings that do not have a string length argument. It is assumed that the string buffer is allocated with the size SZ_LINE. The % Character To output a percent character (%) using any of the formatted output pro- cedures, use two adjacent percent characters, %% in the format string. call printf ("Ratio: %f%%\n") call pargr (ratio) Results in: Ratio: 12.34% (assuming the value of ratio is 12.34). Buffered Output Standard formatted output is normally buffered. The result is that output to STDOUT may not appear on the user's terminal right away. The buffer is flushed when it is full, at the end of the task, or when it is explicitly flushed. The buffer may be flushed with flush(), whose argument is the file descriptor of the stream, STDOUT for example. In some cases, particularly in deing stages of development, it may be desirable to have output appear more quickly. Rather than using flush() repeatedly, you may set the fio parameter F_FLUSHNL to YES with a call to fset(). This advises fio to flush the buffer whenever it prints a newline character. Thus, output will appear on every line. Output to STDERR always flushes on newlines. Dynamic Memory Allocation In order to use dynamic memory pointers properly, you must declare at least one pointer variable in the appropriate procedures. This will gen- erate the code defining a common block with declarations for all of the Mem arrays: Memd, Memr, Memi, Mems, etc. Otherwise, you will get a compiler error complaining of undeclared variables. Image I/O Perhaps the most confusing aspect of image I/O is the rather unintuitive way images are written in imio. It is necessary to obtain an output pointer using one of the imp... procedures and then filling in the values in the out- put buffer. The pixels are not actually written to the output file until the out- put buffer is flushed or the image is closed. This can, in fact, lead to another pitfall. If you wish to write and read the same image in the same task, you must be sure that the pixels are written out before trying to read them in again. This may be assured with a call to imflush() after filling the out- put buffer. Alternately, you might close the image using imunmap() and then reopen it with immap(). A brief example may clarify this situation. The following fragment of code opens an image for read and write access, writes some pixels and reads them back in. Example C.12: Image I/O. If you read two lines using arbitrary line I/O with two separate buffer pointers, the second call may make the first pointer x1 invalid. x1 = imgl2r (im, i) x2 = imgl2r (im, i+1) This applies to output, impl2T() as well as input. Group Format One additional wrinkle involves multi-image group format STF (STSDAS format) images. This format allows more than one image in a single logical image (pair of files; header and pixel file) with a common image header. It is possible to access more than one image in the group simultaneously in a task. With imio, each sub-image (sometimes referred to confusingly as a group) you need to use immap() separately. To specify which image in the set to open, append the image number enclosed in square brackets to the file name in the immap() call. The following opens the second image in a multi-image group format file: Example C.13: Opening the Second Group of a Group Format STSDAS Image. In many cases, it would be up to the user to specify the group number on the image file name when using the task. There may be cases, however, in which a task would use specific groups in an image. To create a new multi-- image file, you must specify the total number of images in the set as well as the image number. Example C.14 creates a four image set and opens the first image. Example C.14: Creating a Four-Image Set and Opening the First Image. A slight complication arises when you wish to create a multi-image group format file and simultaneously access more than one image. In this case, you must create the image, close it, and reopen the individual images. Note also that the pixel file will not be created propeunless a write opera- tion is performed. This may be done by simply writing a single line before closing the image. Example C.15: Accessing More Than One Image in a Multi-Image File. Logical Flags In addition to bool data type variables, many SPP programs use the macro predefined constants YES and NO as flag or switch values. Note that these are int constants, not bools. The bool literal constants are true and false.