Programming in the RSX Indirect Command Language - RX019 - T. R. Wyant E. I. DuPont de Nemours October 8, 1986 Housekeeping Outline Housekeeping Outline Goal & Caveats Acknowledgment Command Procedure Libraries Arrays Using a String Symbol as an Array Example (from PPORT.CMD) Using a String Symbol as an Attribute List Example (from SCRED.CMD) Using Groups of Symbols as an Array Example (from SCRED.CMD) Content-addressable Memory Example (from CRASHDUMP.CMD) Searching a (Very) Sparse Array Example (from CRASHDUMP.CMD) Binary File I/O Example (from SYMDMP.CMD) Screen Handling Screen Handling With FMS Screen Handling Without FMS Example (from SCRED.CMD) Error Control Example (from SCRED.CMD) Parsing an MCR- or DCL-like syntax Example (from SYMDMP.CMD) Bibliography Housekeeping Goal The goal of this presentation is to exhibit some of the more advanced, exotic, and otherwise weird things that can be done with the RSX Indirect Command Processor (ICP), and to stimulate more ideas in the same vein. This presentation is aimed at an audience that is familiar with the RSX ICP, though attendance of "Introduction to the RSX, P/OS, and RT Indirect Command File Processor" (RX018) should give sufficient background. These slides and all command files cited as examples will be distributed on the Fall, 1986 RSX SIG tape. Caveats The current releases of all the operating systems are assumed. Since the things covered in this paper are "off the beaten track", they are more likely than usual to be affected by bugs or other differences between releases of the ICP. The examples cited all work under RSX-11M+ V2.1 C, except where noted. I will mention bugs where I am aware of their existence. I am not the final authority on the ICP in all its multifarious versions. Errors in research and transcription do occur. I apologize in advance for these, but assume no responsibility for their consequences. Housekeeping Acknowledgment Allen A. Watson's paper, "Nifty Things to Do with RSX Indirect Command Files", presented at the Spring, 1983 DECUS US Symposium, was both an inspiration and a reference for this paper. Command Procedure Libraries It can be desirable to break a very large command procedure into separate modules. These will be easier to keep track of if they are kept in a Command Procedure Library. Examples include: LB:[1,2]INDSYS.CLB LB:[200,200]SYSGEN.CLB LB:[137,10]NETGEN.CLB Principles: A Command Procedure Library is just a Universal Library (as documented in the LBR manual), created as follows: LBR library.CLB/CR::::UNI:CMD Once the command procedure library is created, modules can be inserted and removed just as they are for a macro or object library. Modules in a Command Procedure Library can be executed without extracting them from the library by: @library/LB:module Options: A module in a Command Procedure Library can execute another module in the same Command Procedure Library by: @/LB:module If you do not specify a module of the Command Procedure Library to execute, module .MAIN. is executed. If you do not specify the /LB switch when invoking a Command Procedure, the ICP checks the attributes of the file to determine whether it is a Command Procedure Library. So, you can call your Command Procedure Library "library.CMD", and execute module .MAIN. by: @library You can store things other than Command Procedures in a Command Procedure Library. For instance, you could store the sources for a software package there, and have the .MAIN. module extract the sources and build the package. Arrays Using a String Symbol as an Array Small arrays can be stored in a String Symbol and extracted by forming a substring. Requirements: All elements in the array must be the same size. The total size of all elements in the array must not exceed the maximum size of a String Symbol. The location of each element in the string must be manually calculated from the INDEX of the desired element and the SIZE of the elements, as follows: .SETN START (INDEX-1)*SIZE+1 .SETN END START+SIZE-1 Recommendations: This works best with arrays where the element is one byte long, as the index can be used directly as both the start and the end of the substring. Arrays Using a String Symbol as an Array (continued) Example (from PPORT.CMD) .; PPORT.CMD dumps a file to your printer port, after setting .; your screen width and the horizontal pitch on your printer. .; Initialize: .ENABLE SUBSTITUTION .SETS MCR "" .IF <> "MCR" .SETS MCR "" .SETN NJUNK 33 .SETS ESCAPE "'NJUNK%V'" .SETS CSI ESCAPE+"[" .SETN OLDWID 'MCR'SET /BUF=TI:255. .; Table to translate line length into pitch control character: .SETS PITCH "5555555555555555555555555555555555555555" .SETS PITCH PITCH+"66666666888888888888888888" .SETS PITCH PITCH+"000000000000002222222222222222" .SETS PITCH PITCH+"444444444444444444444444444444444444" .; Table to translate line length into screen width: .SETS SCREEN "llllllllllllllllllllllllllllllllllllllll" .SETS SCREEN SCREEN+"lllllllllllllllllllllllllllllllll" .SETS SCREEN SCREEN+"lllllllhhhhhhhhhhhhhhhhhhhhhhhhhh" .SETS SCREEN SCREEN+"hhhhhhhhhhhhhhhhhhhhhhhhhh" .; Main (and only) loop: .LOOP: .ASKS FILE What file shall I print .IF FILE = "" .GOTO EXIT .; Get the record size to use as line length: .OPENR 'FILE' .PARSE "," RTYP RATT RSIZ JUNK .CLOSE .; Extract proper characters from pitch and screen arrays: .SETS SCRCHR SCREEN['RSIZ'.:'RSIZ'.] .SETS PITCHR PITCH['RSIZ'.:'RSIZ'.] .; Set screen width, turn on printer, and set printer pitch: .DISABLE DISPLAY ;'CSI'?3'SCRCHR''CSI'5i'CSI''PITCHR'w .; The rest of the command file is omitted. Arrays Using a String Symbol as an Attribute List As a variant on arrays, a String Symbol can be used to store a list of attributes to be associated with the name of the string symbol, or a portion thereof. Principles: The attributes are listed in the string symbol's value, punctuated by a unique character. Attributes may be subdivided by using another unique character. An attribute list can be searched for the presence of a given attribute using the two-argument .TEST directive, with = 0 denoting absence of the desired value from the list. Attributes can be extracted from the list using the .PARSE directive (or a series of them to extract subattributes). Requirements: The character chosen as separator may not appear in any of the attributes. Recommendations: If the primary use of the attribute list is to search using .TEST, it may be helpful to start the list off with a punctuating character. Options: If the attributes are all one byte long, and the only purpose of the list is to use it with the .TEST directive, the punctuation may be omitted. Arrays Using a String Symbol as an Attribute List (continued) Example (from SCRED.CMD) .; SCRED.CMD is a single-screen editor. The active function .; keys are: .; The arrows - move cursor in the given direction; .; PF1 - coarse motion - multiplies vertical arrows by 8, .; and horizontal arrows by 16. .; Define parameters used in processing the arrow keys: .; These are defined by: .; .SETS ARROWx "axis,fac,sign,limit,test" .; where: .; x = last character of escape sequence; .; axis = name of symbol to update; .; fac = factor to apply if PF1 in effect; .; sign = direction of travel along axis; .; limit = limiting value on the axis; .; test = type of test to make against limit. .SETS ARROWA "LINE,8,-,1.,<" ! A = Up arrow. .SETS ARROWB "LINE,8,+,23.,>" ! B = Down arrow. .SETS ARROWC "COLUMN,16,+,80.,>" ! C = Right arrow. .SETS ARROWD "COLUMN,16,-,1.,<" ! D = Left arrow. . . . .; Note: Code used to recognize an escape sequence is .; discussed later. Suffice it for the moment .; to say that they are recognized, and if an .; arrow key is struck, the code below is entered .; with: .; .; CHAR = last character of escape sequence; .; ESCA0 = numeric value of first argument. .; An arrow key generates these escape sequences: .CSI101:.; A = Up arrow; .CSI102:.; B = Down arrow; .CSI103:.; C = Right arrow; .CSI104:.; D = Left arrow. .PARSE ARROW'CHAR' "," AXIS FAC SIGN LIMIT TEST .IF ESCA0 = 0 .SETN ESCA0 1. ! Force param. .IFT GOLD .SETN ESCA0 ESCA0*'FAC'. ! Apply PF1. .SETN NJUNK 'AXIS' ! Save old location. .SETN 'AXIS' 'AXIS''SIGN'ESCA0 ! Set new loc. .IF 'AXIS' 'TEST' 'LIMIT' .SETN 'AXIS' 'LIMIT' .SETF GOLD ! Clear shift key. .GOTO ESCPSR ! Get next character. Arrays Using Groups of Symbols as an Array An array of arbitrary size can be constructed by using a group of symbols with similar names. Principles: A symbol is defined for each accessible element in the array. These need not be the same type. Only those elements that are actually used need be defined. Arrays can be sparsely populated, or have ragged rows. The names of the individual symbols consist of a constant part (which can be thought of as the name of the array) and a variable part (which can be thought of as the index into the array). Arrays can have more than one dimension. Arrays can be indexed by Numeric Symbols, String Symbols, or Logical Symbols, or by a mixture of types of symbols. Array elements are referred to by using symbol substitution to construct the element's symbol name out of the array name and the element index(es). Requirements: Substitution can not be done on an arbitrary array element. The value of that array element must be assigned to another, "constant-named" symbol, which is used instead in the desired substitution. The method chosen for encoding the index(es) in the array element's symbol name must give rise to legal and unique symbol names for each element in the array. Recommendations: Use %RnZ format control on Numeric Symbol indices, if the index can be more than one digit. This is especially true for multi-dimensioned arrays. Arrays Using Groups of Symbols as an Array (continued) Example (from SCRED.CMD) .; SCRED.CMD is a single-screen editor. The active function .; keys are: .; The arrows - move cursor in the given direction; .; PF1 - coarse motion - multiplies vertical arrows by 8, .; and horizontal arrows by 16. .OPENR 'FILE' .SETN LINE 0. .INPLP: .INC LINE .READ LINE'LINE' . . . .GOTO INPLP Arrays Content-addressable Memory This is really just an extension of the concept of using a group of symbol names as an array. There is no logical reason why you can't use a string symbol as an array subscript, provided the contents of the symbol give rise to a valid symbol name. The same consideration applies to allowing the array name to shrink to zero bytes. What you have left is a system where the entire symbol table is an array, where the symbol names are chosen to describe the information stored in the symbol. Requirements: The string symbol that contains the information to be looked up had better be validated first to be sure it's a legal symbol name. You must check for existence of the symbol before you use it, as a random symbol name is probably NOT defined in the symbol table. Recommendations: Since it is probably not desirable to literally use the whole symbol table, a subset is selected (eg: all symbols with alphanumeric names), and symbols used otherwise in the command procedure are selected to fall outside this subset (eg: names with embedded dollar signs). Arrays Content-addressable Memory (continued) Example (from CRASHDUMP.CMD) ; Run the crashdump analyzer. .; The supported systems must be defined via .; .SETS sysnam "cdanal;memsiz;crdev;blk;stbnam;hlptxt" .; Where: .; sysnam is the name of the system to which the .; information pertains; .; cdanal is the name of the crash dump analyzer task .; file - the defaults are: .; device ---- LB: .; directory - .; name ------ CDA .; type ------ .TSK .; memsiz is the memory size for the system; .; crdev is a list of legal crash devices (separated by .; commas); .; blk is the block on which the crash dump starts; .; stbnam is the STB file name - the defaults are: .; device ---- LB: .; directory - .; name ------ RSX11M .; type ------ .STB .; hlptxt is identifying text for the system. .; .SETS HOST "CDAFSL;256;DR;2;;RSX-11M+ system." .SETS FRONT "CDA41;124;DX,DY;1;[1,64];RSX-11S system." .SETS OTHER "CDAFSL;512;DR;2;OTHER;Another M+ system." .; .; Get the system being dumped. .; .SYSASK: ; .DISABLE LOWERCASE .ASKS [0:6:] S$SYS What system is crashdump for .ENABLE LOWERCASE .IFT .GOTO SYSHLP .IFF .GOTO SYSERR .TEST S$NOGO ",'S$SYS'," .IF > 0 .GOTO SYSERR .IFDF 'S$SYS' .GOTO SYSOK Arrays Searching a (Very) Sparse Array One of the disadvantages of using sparsely populated arrays is the inefficiency of weeding out nonexistent elements when iterating over the entire array. A better alternative may be to iterate over the entire symbol table, searching for elements of the array. Principles: Each time the Special String Symbol is referred to, it returns the name of the next symbol in the Symbol Table. You can use this fact to iterate over the entire Symbol Table. When the end of the Symbol Table is reached, returns a null string. Requirements: To initialize the iteration, you must: .SETS "" You must refer to only once in each iteration, or you will skip symbols. A useful way to do this is: .SETS SYMNAM Recommendations: The documentation of contains warnings about its availability for general use. My experience is that you might have to fiddle a bit to get it to work. See: LB:[1,2]INDSYS/LB:SYMDMP for an example. I recommend against defining any new symbols inside the iteration loop. Arrays Searching a (Very) Sparse Array (continued) Example (from CRASHDUMP.CMD) .SYSERR:; ; Error - System name "'S$SYS'" is invalid, or not ; supported by this command file. .SYSHLP:; ; Valid system names are: .SETS "" .; Now, loop through the Symbol Table: .SYSHLL: .; Get the next Symbol name; if none, we are done: .SETS S$SYS .IF S$SYS = "" .GOTO SYSASK .; If it is not alphanumeric, we do not want it: .TEST S$SYS .IFF .GOTO SYSHLL .; If it is not a String Symbol, we do not want it: .TEST 'S$SYS' .IF <> 4 .GOTO SYSHLL .; If it is COMMAN or P0-P9, we do not want it: .TEST S$NOGO ",'S$SYS'," .IF > 0 .GOTO SYSHLL .; It it a legal system name; pick it apart and display .; the identifying text; .PARSE 'S$SYS' ";" S$CDA S$MEM S$CDL S$CBK S$STB S$HELP ; 'S$SYS%L6' - 'S$HELP' .; Go get the next Symbol: .GOTO SYSHLL Binary File I/O I/O on files containing binary data can be done with the ICP, by converting the bytes to numeric values, and then assembling the byte values in ways appropriate to the field in which they occur. Requirements: All records read or written must be less than 133 bytes long. Subject to the above restriction, any sequential file can be opened for read or append access. Files opened for output will have variable length records, with "list" carriage control. Recommendations: Use substitution with %V format control to convert bytes to their numeric values. If this is unavailable, the next best thing is probably to store all 256 ASCII characters in a couple string variables in order, and use .TEST to do the conversion. Options: If you wish to write a file with "unusual" attributes (fixed length records, no spanned blocks, etc.) you can invoke RMSDEF to create an empty file, with the desired attributes, and then open it for append access. Binary File I/O Example (from SYMDMP.CMD) .; SYMDMP.CMD is a command file that reads a .OBJ or .STB file, .; and displays the symbols it finds there. .; The example covers decoding of binary bytes, binary words, .; and RAD50 words. .; Read the next record: .RDLP: .READ S$REC .IFT .GOTO CLOS .; Strip off the first two bytes, and convert to numeric: .SETS S$B0 S$REC[1:1] .SETS S$B1 S$REC[2:2] .SETN O$W ('S$B1%V'&377)*400+('S$B0%V'&377) .; Record type 2 signals end of items of interest: .IF O$W = 2 .GOTO CLOS .; Record type 1 is what we want. Loop for others: .IF O$W <> 1 .GOTO RDLP .; Strip off the record type: .SETS S$REC S$REC[3:*] .; Process each symbol in the record: .SYLP: .IF S$REC = "" .GOTO RDLP .; The symbol name is 6 RAD-50 characters; extract it from the .; 4 bytes it is stored in, and convert to ASCII: .SETS S$B0 S$REC[1:1] .SETS S$B1 S$REC[2:2] .SETN O$W ('S$B1%V'&377)*400+('S$B0%V'&377) .SETS S$SYM "'O$W%X'" .SETS S$B0 S$REC[3:3] .SETS S$B1 S$REC[4:4] .SETN O$W ('S$B1%V'&377)*400+('S$B0%V'&377) .SETS S$SYM "'S$SYM''O$W%X'" .; The flags byte is stored in binary; extract it: .SETS S$B0 S$REC[5:5] .SETN O$FLG 'S$B0%V'&377 Screen Handling Screen Handling With FMS The RSX-11M+ ICP comes with an interface to FMS-11. You can use this interface to generate a form-driven application. You can get a demonstration of the FMS capability of the ICP by: @LB:[1,2]INDSYS.CLB/LB:FMSDEM Requirements: Forms must be designed and inserted in a form library, just as for any FMS application. The .FORM directive is used to display forms and gather input. You can use .IFENABLED FMS to determine if FMS support is available. Special Symbol will contain the status code for the previous FMS operation. You can also (if necessary) use .IFDF to determine whether .IFENABLED FMS will produce a syntax error, if you really need to. Screen Handling Screen Handling Without FMS If you don't own RSX-11M+ and FMS-11, you can write your own screen handler for the ICP. Principles: The ICP does all input through the .ASKS directive. The terminal is conditioned so that escape sequences generated by the function keys are passed at the end of an input string. The prompt sequence of the .ASKS is used to position the cursor for input. Requirements: You must issue the .ENABLE ESCAPE-SEQUENCE directive to allow the ICP to receive escape sequences in response to the .ASKS directive. You must issue the MCR command SET /ESCSEQ=TI: or the DCL command SET TERMINAL/ESCAPE_SEQUENCE to allow the TT: driver to pass the escape sequences through to your terminal. You must issue the .DISABLE DISPLAY directive to prevent the ICP from sending unwanted characters to your screen. The ICP must parse the escape sequence off the end of the .ASKS input string, and interpret it. Screen Handling Screen Handling Without FMS (continued) Recommendations: You should issue the .DISABLE DETACH directive, so that input from additional keystrokes will not be lost if you type faster than the ICP can process your input. Note that if you do this, programs run by the ICP will not have access to your terminal. Literal escape sequences should not be embedded in the command file. If you violate this rule, you may not be able to TYPE the procedure on your terminal. Constant escape sequences (eg: home cursor, clear screen, setup sequences) should be assigned to appropriately named string symbols on initialization. This enhances portability, and makes it easier to handle multiple terminal types. Variable escape sequences (eg: cursor postioning) should be generated in .GOSUB modules, for the same reasons. Input should also be done in a .GOSUB module, so that the escape sequence can be easily parsed off the rest of the input. Avoid leaving the cursor on the last line of the screen. If you must, repaint the screen after the inevitable scroll-up. Options: You can SET /NOECHO=TI:, and have the ICP take care of displaying the characters on the screen. Screen Handling Screen Handling Without FMS (continued) Example (from SCRED.CMD) .; SCRED.CMD is a single-screen editor. The active function .; keys are: .; The arrows - move cursor in the given direction; .; PF1 - coarse motion - multiplies vertical arrows by 8, .; and horizontal arrows by 16. .; Required ICP initialization: .ENABLE SUBSTITUTION .DISABLE DISPLAY .DISABLE DETACH .ENABLE ESCAPE-SEQUENCE .; Required TT: driver initialization (must come after the .; .ENABLE ESCAPE-SEQUENCE): .SETN TIWID .SETS MCR "" .IF <> "MCR" .SETS MCR "MCR " 'MCR'SET /LOWER=TI: 'MCR'SET /ESCSEQ=TI: 'MCR'SET /BUF=TI:132. .; Useful symbol definitions: .; The following are suitable for ASCII terminals: .SETN NJUNK 16 ! Define SO to be a .SETS SO "'NJUNK%V'" ! char. .SETN NJUNK 17 ! Define SI to be a .SETS SI "'NJUNK%V'" ! char. .SETN NJUNK 33 ! Define ESCAPE to be .SETS ESCAPE "'NJUNK%V'" ! an character .SETN NJUNK 217 ! Define SS3 to be an .SETS SS3 "'NJUNK%V'" ! (="O") .SETN NJUNK 233 ! Define CSI to be a .SETS CSI "'NJUNK%V'" ! (="[") .; The following are suitable for ANSI terminals: .SETS HOME ESCAPE+"[H" ! HOME homes cursor .SETS CLEAR ESCAPE+"[J" ! CLEAR clears screen .; The following initializes a DEC VT100 or VT200 .; series terminal by homing and clearing the screen, .; loading the normal ASCII character set into G0 and .; the graphics character set into G1, and selecting G0. .SETS INIT HOME+CLEAR+ESCAPE+"(B"+ESCAPE+")0"+SI Screen Handling Screen Handling Without FMS (continued) Example (from SCRED.CMD) (continued) .; And, some useful subroutines: .; .; .GOSUB ASKE line;column .; .; Positions the cursor at the given line and column, .; and issues a .ASKS. On return, .; TEXT contains the text part of the answer; .; ESCSEQ contains the escape sequence; .; contains the location of the escape .; character. .; .ASKE: .GOSUB POSITN 'COMMAN%C' .SETS ESCSEQ "" .ASKS TEXT 'COMMAN' .IFT .RETURN .TEST TEXT ESCAPE .IF = 0 .TEST TEXT CSI .IF = 0 .TEST TEXT SS3 .IF <> 0 .SETS ESCSEQ TEXT[:*] .IF <> 0 .SETS TEXT TEXT[1:-1] .TEST TEXT .RETURN .; .; .GOSUB POSITN line;column .; .; returns the escape sequence in COMMAN. .; .; The following is suitable for an ANSI terminal. .; .POSITN: .SETS COMMAN ESCAPE+"["+"'COMMAN%C'"+"H" .RETURN Screen Handling Screen Handling Without FMS (continued) Example (from SCRED.CMD) (continued) .; Initialize the escape sequence parser: .ESCPSI: .SETS ESCTYP "INI" ! Set parser "state". .SETN ESCAMX 0. ! Set number of arguments. .SETN ESCA0 0. ! Clear the first argument. .; Main parser loop: .; Strip off the next character (if any), convert it to .; a number, and do a "computed" GO TO based on current .; parser state and character code: .ESCPSR: .IF ESCSEQ = "" .GOTO LOOP ! If done, get more. .SETS CHAR ESCSEQ[1:1] ! Get first character, .SETS ESCSEQ ESCSEQ[2:*] ! remove from buffer. .SETN CVALUE 'CHAR%V' ! Get its value. .ONERR LOOPI ! Trap unexpected chars. .GOTO 'ESCTYP''CVALUE' ! Handle this character. .; First character = ; set state: .INI33: .SETS ESCTYP "ESC" ! Go to escape "state". .GOTO ESCPSR ! Get next character. .; First character is , or .INI217:.; .; First was and second is "O"; set state: .ESC117:.SETS ESCTYP "SS3" ! Go to SS3 "state". .GOTO ESCPSR ! Get next character. .; Got P = PF1 - use it as shift key: .SS3120:.SETT GOLD ! Set shift flag. .GOTO ESCPSI ! Get next escape seq. Error Control It can be useful to perform an operation even though it may produce an error in the ICP. Although the ICP can not be set to ignore such errors, it can be set to dispatch them to the label of your choice for handling. Principles: To cause errors to be trapped to your error handler, issue the ICP directive: .ONERR label The next error encountered will cause control to be transferred to the given label. Errors are divided into numbered classes, as described in the ICP documentation. You can set bits in Special Numeric Symbol to determine which classes are trapped to your error handler. Untrapped errors will cause the ICP to abort. By default, only Class 1 errors are trapped. On entry to the error handler, Special Numeric Symbol contains the Error Class Number of the error encountered. Requirements: The .ONERR directive must be reasserted after each error trapped. Recommendations: The Error Classes are pretty broad (there are only two), and don't tell you very much about what actually caused the fault. If you are expecting more than one source of error, you will need to build your own logic to distinguish between them. The manual says you should not resume processing after trapping a Class 2 Error, as the state of the ICP is indeterminate. I have found that it works in some cases, but recommend trying each case out before you build an application around it. Error Control Example (from SCRED.CMD) .; Initialize the parse by turning off the "GOLD" key: .LOOPI: .SETF GOLD .; Main input loop: .; Ask for data, and insert the text part into the line .; buffers: .LOOP: .GOSUB ASKE 'LINE';'COLUMN' .IFT .GOTO CLENUP .SETS JUNK LINE'LINE'[1:COLUMN-1] .SETN COLUMN COLUMN+ .IF COLUMN > 80. .SETN COLUMN 80. .SETS LINE'LINE' JUNK+TEXT+LINE'LINE'[COLUMN:*] .SETS LINE'LINE' LINE'LINE'[1:80.] .; Initialize the escape sequence parser: .ESCPSI: .SETS ESCTYP "INI" ! Set parser "state". .SETN ESCAMX 0. ! Set number of arguments. .SETN ESCA0 0. ! Clear the first argument. .; Main parser loop: .; Strip off the next character (if any), convert it to .; a number, and do a "computed" GO TO based on current .; parser state and character code: .ESCPSR: .IF ESCSEQ = "" .GOTO LOOP ! If done, get more. .SETS CHAR ESCSEQ[1:1] ! Get first character, .SETS ESCSEQ ESCSEQ[2:*] ! remove from buffer. .SETN CVALUE 'CHAR%V' ! Get its value. .ONERR LOOPI ! Trap unexpected chars. .GOTO 'ESCTYP''CVALUE' ! Handle this character. Parsing an MCR- or DCL-like Syntax Command Files can be invoked in much the same way as a CLI command, and the parameters passed are available to the Command File in string symbols P0-P9. Parsing these parameters normally takes place in two phases: Parsing the file specification(s); Parsing the switches and options. Requirements: You need to design a command syntax that is both clear and easily parsed. Either MCR or DCL can serve as a model. Recommendations: You get a "nicer" parser if you can process all the file specifications in one loop, with an inner loop for the switches. You should specifically check for a null command line, and get the information you need through .ASKx directives if not passed in the command line. Options: If the syntax for switches is properly defined, your switch parser code will be completely generic - that is, you can add switches without modifying the parser. This is done by: Defining a consistent symbol name convention for storing switch settings. In the example, "V$xx" is used, where "xx" represents the switch name, and the Symbol Type of V$xx determines how the switch is processed. Defining a consistent and restricted syntax. In the example, no switch may have more than one argument. Parsing an MCR- or DCL-like Syntax Example (from SYMDMP.CMD) .; PHASE 1 - Parsing the file specification(s): .; Define and initialize the command switches: .SETF V$SP ! Define /SP (spool) and assume false. .SETT V$BR ! Define /BR (page break), assume true. .; Determine processing mode (interactive or command line): .IF P1 = "" .GOTO PROMPT .; Get the file specs, from either MCR or DCL syntax: .IF P2 <> "" .GOTO SWIEXT .PARSE P1 "=" S$OUT S$FIL .IF S$FIL <> "" .GOTO SWIEXT .SETS S$FIL S$OUT .SETS S$OUT "" .; Peel the switches off the file specifications: .SWIEXT:.PARSE S$FIL "/" S$FIL S$SWIT .PARSE S$OUT "/" S$OUT S$JUNK .SETS S$SWIT "/"+S$SWIT+"/"+S$JUNK Parsing an MCR- or DCL-like Syntax Example (from SYMDMP.CMD) (continued) .; PHASE 2 - Parsing the switches and options: .SWITLP: .IF S$SWIT = "" .GOTO PROCES .; Peel the next switch off, and get its arguments: .PARSE S$SWIT "/" S$SWX S$SWIT .PARSE S$SWX ":" S$SWX S$SWP .; Figure out whether it is asserted or negated: .SETT L$ASRT .IF S$SWX = "" .GOTO SWITLP .IF S$DASH = S$SWX[1:1] .GOTO SWITNM .IF S$NO <> S$SWX[1:2] .GOTO SWITAS .SETS S$SWX S$SWX[2:*] .SWITNM:.SETS S$SWX S$SWX[2:*] .SETF L$ASRT .SWITAS:.SETS S$SWX S$SWX[1:2] .; See if this switch has a corresponding V$sw symbol: .TEST S$SWX .IFF .GOTO SWIBAD .IFNDF V$'S$SWX' .GOTO SWIBAD .; Dispatch the rest based on the symbol type: .TEST V$'S$SWX' .GOTO SWIT'' .; Logical symbol. Set its value to switch polarity: .SWIT0: .IF S$SWP <> "" .GOTO SWINPR .SETL V$'S$SWX' L$ASRT .GOTO SWITLP .; Numeric symbol. Set its value to switch parameter: .SWIT2: .IFF L$ASRT .GOTO SWINNG .TEST S$SWP .IFF .GOTO SWIIVP .SETN V$'S$SWX' 'S$SWP' .GOTO SWITLP .; String symbol. Set its value to switch parameter: .SWIT4: .IFF L$ASRT .GOTO SWINNG .SETS V$'S$SWX' S$SWP .GOTO SWITLP Bibliography "Nifty Things to Do with RSX Indirect Command Files" Allen A. Watson RSX/IAS SIG Symposium Handout Spring 1983 DECUS US Symposium LA50 Printer Programmer Reference Manual Documents printer escape sequences. RSX LB:[1,2]ICP.HLP On-line help file for ICP. Contains some information that is not in the manual. RSX LB:[1,2]INDSYS.CLB Sample command routines. RSX-11M/M-Plus Task Builder Manual Documents object file layout. RSX-11 MCR Operations Manual The primary reference for the ICP under RSX. RSX-11 Utilities Manual Reference for the librarian task (LBR). VT220 Programmer Pocket Guide Documents escape sequences for VT2xx terminals.