.he Fido/FidoNet/FidoTerm Script Language Copyright Tom Jennings # Tom Jennings Fido Software 164 Shipley San Francisco CA 94107 SCRIPT LANGUAGE MACHINE MODEL Fido/FidoNet's script language is a machine language, and like allچ machine languages, there is an underlying "model" machine thatچ the script instructions manipulate. The buckets and Beetles, thatچ is. The script machine model has the following components at yourچ disposal: NAME DESCRIPTION &A The numeric "accumulator", values 0 - 65535 &C The "clock", counts up, in seconds &T The "trap register", 0 or 1 - 65535 seconds &E The "error register", either 0 or 1 &R The general purpose Recognizer &S The "shift register", 20 characters, &1 - &8 General purpose registers stack 10 levels of data stack &A: The "accumulator" (such a quaint word) is a simpleچ register for storing or manipulating numbers. It can be set toچ any value from 0 to 65535. It can be stored into any otherچ register. There are instructions to test when the accumulator isچ zero. &T: The "trap register" is part of a mechanism to breakچ endless loops; the "TRAP" instruction is used to set a failsafeچ if a script or part of a script takes too long to execute. &E: The "error register" can be either 0 or 1, with 1چ generally indicating an error condition. There are instructionsچ to set and test the error register. &R: The Recognizer can be used to store text or numbers. &1 - &8: These are general purpose registers, and can holdچ numbers or text. They are used to pass parameters to subroutines.چ Each subroutine level has it's own set of these registers. When aچ subroutine is called, the initial values are those of the callingچ routine. &S: The "shift register" is a special register that containsچ the last 20 characters received from the modem, and is whatچ patterns are matched against. Characters are shifted in from theچ right, and the left most character "falls off" the end. You canچ also store text in &S and use the "IF" instruction to do stringچ comparisons. &1 through &8 are "local variables"; they are not shared amongstچ all subroutine levels. When a CALL is executed, the called scriptچ "inherits" the calling scripts initial values for theseچ registers, but any changes made are lost upon RETURN. The stack is a general purpose "push down" stack; you PUSH itemsچ onto the stack, and POP them off. Items can only be accessed fromچ the top of the stack. There is room for up to ten items on theچ stack. If there are (for instance) five items on the stack, toچ get at the first one pushed (the "bottom" of the stack) you mustچ first pop off the four on "top" of it. .pa INSTRUCTION SYNTAX All instructions are of the form: INSTRUCTION OPERAND OPERAND Blank lines or lines beginning with a semicolon are ignored.چ Lines beginning with a colon (label statements) are ignored whenچ processing instructions. Operands are usually text strings, and all strings should beچ quoted. INSTRUCTION PROCESSING The script processor is basically a text processor. The macroچ language applies to all commands at all times. Anything to theچ right of the instruction itself is expanded by the macroچ processor. Macros are expanded from left to right, once. There are twoچ special tokens the macro processor understands: &(one letter register name) \(special text character) The language chosen allows just about every disgusting trickچ possible with machine language; self-modifying code, computerچ GOTOs, etc. I'll dispense with the formality, and show someچ generalized examples and leave it at that: Message "This is a string" The MESSAGE command merely displays what it's given on theچ command status line. It makes for a simple example. In this case,چ centered in the status line you'd see: This is a string If the recognizer (&R) contained the string "HELLO DUDE" theچ instruction: Message "The recognizer contains &R" Would display: The recognizer contains HELLO DUDE To display the same thing, but with quotes surrounding theچ contents of the recognizer, you would use: Message "The recognizer contains \"&R\"" Displayed as: The recognizer contains "HELLO DUDE" Note the \ before the quote character merely quotes what follows.چ There are some convenient exceptions: \r carriage return, 13 decimal \n line feed, 10 decimal \e escape, 27 decimal \b backspace, 8 decimal \c etx (Control-C), 3 decimal \1 SOH character, decimal 1 \127 DEL character, decimal 127 ... ... If you don't like that, ("C" programmers will like it though) youچ can enter control characters like so: ^A 1 decimal, ASCII SOH ^M carriage return, 13 decimal ^J linefeed, 10 decimal ... ... The ADD instruction adds a value to the accumulator. For example: ADD 27 Would add 27 to the accumulator; if it had contained 3, it wouldچ now contain 30. If the &2 register contained "34", the following: ADD &2 Would add the numeric value 34 to &A. If &2 contained "ABRACADAB"چ then it would add zero to &A. Here are some further examples. Assume first: &A contains 20 &R contains "Text" &1 contains "764-1629" &2 contains 300 INSTRUCTION OPERAND EXPANDED message "sample text" sample text message &R 20 message "pattern is &r, OK?" pattern is 20, OK? message "&A&R&A" 20Text20 message "Dialing &1 at &2 baud" Dialing 764-1629 at 300 baud You can take this to absurd extremes. The instruction JMP <label>چ passes execution to the line following the line containingچ :<label>. You can take advantage of this by using a "computedچ goto". The command: jmp "&R" Would attempt to jump to a line: :TEXT .pa .sh Pattern Matching PATTERN MATCHING The main purpose of Fido/FidoNet's script language is patternچ matching; looking for particular strings of characters receivedچ from the modem. All of the script language instructions areچ intended to work together to do this. The single most important component of the script machine is theچ Shift Register. The shift register contains the last 20چ characters received from the modem, and works like this: Assume that with your modem and Fido/FidoNet, you just called intoچ and connected with some bulletin board system, and are about toچ be asked to log in. As characters are received they are put into the right hand endچ of the shift register; the character on the left edge of theچ shift register is lost. For example, if the bulletin board signonچ message you'd see on your screen was: WELCOME TO THE ACME HOG THROWERS BBS AT THE PROMPT ENTER YOUR NAME AND PASSWORD: Lots of times a simple "wait until the word XYZ" will do exactlyچ what you want. This sort of thing is easy to write scripts for,چ and even in complex scripts is a very useful function. The problems start when you need to base different actions on aچ number of possible patterns, not just one. A simple "MATCH"چ instruction just won't do it, and brute force techniques ofچ searching through a screenful of text ("rummaging") has seriousچ limitations. In Fido/FidoNet the technique used involves a 20 character arrayچ called the "shift register", referred to as "&S" in the scriptچ language. The shift register changes after each character received from theچ modem; in the example above, W, E, L, C, O, M, E, etc. Part wayچ through the above message being displayed, the shift registerچ would look like: .... &S " WEL" &S " WELC" &S " WELCO" &S " WELCOM" .... &S "LCOME TO THE HOG THRO" .... Fido/FidoNet compares patterns you specify against the right hand endچ of the shift register; this means that for each characterچ received (therefore a new string each time) you can look for anyچ reasonable number of possible patterns. ("Reasonable" will beچ discussed later ...) In this example, to do an automatic login, once we see the wordچ "PASSWORD:", we can output thename and password. This is a commonچ and simple case. The instruction "MATCH" will do the job. "MATCH" compares text you specify against the contents of theچ shift register. For this example we'd use "WORD:". A string matchچ is used, so you only need to use enough characters to make itچ unique; the string "WORD:" does not appear anywhere else in theچ initial signon message. (Actually, ":" will work fine here, butچ it's a good idea to use at least a couple of characters; "RD:"چ would be even better.) MATCH "WORD:" To continue the example above, as characters come in from theچ modem they shift through the shift register. Fido/FidoNet isچ executing the "MATCH" instruction above, and the shift registerچ looks like: .... match WORD: &S= "OUR NAME AND PASSWOR" match WORD: &S= "UR NAME AND PASSWORD" match WORD: &S= "R NAME AND PASSWORD:" For each character received, the "MATCH" instruction is comparingچ it's pattern ("WORD:") against the contents of the shiftچ register. When the patterns match, the next line of the scriptچ file is executed, which in this case could be an instruction toچ output your name and password. .pa COMPLEX PATTERN MATCHING You don't need to understand the how and whys of the shiftچ register to use the "MATCH" command, and for most casual scriptچ writers that will be all you need. A good example of the limitations of such a simple system is aچ script that dials phone numbers with a Hayes modem using "AT"چ commands, and responding to the many possible "result codes".چ (This example assumes a basic understanding of Hayes modemچ commands.) The dialing part is easy; you just issue the ATDT command and theچ phone number. The problem arises when you try to interpret theچ results of that command, such as busy, no answer, connecting atچ various baud rates, errors, etc. A simple "MATCH" instructionچ just won't work. For example the US Robotics Courier HST has about a dozenچ possible responses to a simple dial command; any script you writeچ must be able to handle them all. That's what we'll do here. Dialing a US Robotics Courier HST The command we'll use, for simplicity, is: "AT DT 764 1629\r" ("\r" means "carriage return, CR, decimal 13) The modem can respond with any of the following result words: CONNECT (300 baud) RING (incoming call) NO CARRIER (failed to connect) ERROR (bad command given) CONNECT 1200 NO DIAL TONE BUSY NO ANSWER CONNECT 2400 RINGING VOICE CONNECT 9600 This is where you take advantage of the shift register. Theچ script fragment below handles the problem at hand and isچ explained below. You will be using this SAMPLE ... IF ... GOTOچ loop to do most complex string compares. &T= 60 &C= 0 TRAP dial-failed output "ATDT 764-1629\r" :get-result sample if "NO" dial-failed if "ERR" dial-failed if "12" conn-1200 if "24" conn-2400 if "96" conn-9600 if "ECT\r" conn-300 if "BU" redial if "VO" dial-failed if "RING\r" dial-failed goto getresult :dial-failed &A= 0 return 1 :conn-300 &A= 300 goto connect :conn-1200 &A= 1200 goto connect :conn-2400 &A= 2400 goto connect :conn-9600 &A= 9600 :connect $$ "b &A\r" message "connected at &a baud" return 0 "SAMPLE" is a special instruction for use in SAMPLE/IF/JMP loops,چ and causes one character to shift into the shift register. (Theچ "MATCH" instruction and most others do this automatically, butچ "IF" doesn't.) You should have one, and only one, "SAMPLE"چ instruction before each set of "IF" instructions; if you haveچ none, the shift register will not ever change, and if you haveچ more than one you will miss patterns. "IF" compares the given pattern against the current contents ofچ the shift register, and branches to the specified label if aچ match is made. The patterns searched for by the "IF" instructions need to beچ mentioned. You could search for the entire modem responseچ ("CONNECT 1200" instead of just "12") but the script will runچ faster is short strings are used. Use as few as possible is theچ general rule. The "NO" pattern will match any of the following,چ which all mean no connection: NO CARRIER, NO DIAL TONE, NOچ ANSWER. The 300 baud connection result code is "CONNECT", hard toچ distinguish from "CONNECT 1200" because the "IF" will matchچ "CONNECT" without waiting for the "1200" or whatever follows. Theچ "ECT\r" means that it will only match that word at the end of aچ line, that is, followed by a CR character. A loop of this length will run pretty slowly. It really shouldچ have a "timeout" check, using the &C clock, to limit the dialingچ attempt to a minute or so, and this will slow it further. To accomodate this, Fido/FidoNet has rather huge buffers, and usingچ the explicit "SAMPLE" instruction means that characters won'tچ speed through the shift register faster than you can test them. With a loop of approximately this size, it would take a fewچ thousand characters at continuous 9600 baud to cause characterچ loss. In the example above, speed is not a problem; the modemچ issues very little text. SAMPLE/IF/JMP loops should be as tight as possible. This isچ interpreted, so keep that in mind; keep comments out of loops,چ keep labels and operands short. Use as few "IF"s as possible. If you need to do such a series of compares through a largeچ amount of text at high speed, it is best to cut down the search,چ for example by issuing commands to reach some intermediate pointچ from which you can do your multiple compares. .pa FURTHER NOTES ON PATTERN MATCHING The strings you provide to the "MATCH" and "IF" instructions haveچ some characteristics that make for easier comparisons. All searches are case IN-sensitive. "ANC" matches "anc", etc. Youچ can specify it in your script in either way. "?" is a wildcard character; it mean match any character.چ "CONNECT ??00" will match "CONNECT 1200", "CONNECT 2400", etc. Control characters are treated just as any other character. "Endچ of line" characters will vary with the bulletin board program orچ whatever it is you call; Fido for instance is like most BBSs, andچ uses a CR/LF sequence (decimal 13, decimal 10). You can searchچ for "\r" or "\13" or for "\r\n" or "\13\10", but keep in mindچ that other systems could use LF/CR, or other combination. HINT: You can store strings directly in to the Shift Register andچ then test it with the "IF" command. For instance, user input fromچ the "INPUT" command copied into &S could be tested for aچ particular value. .pa .sh Register and Arithmetic Instructions These are the most basic instructions to change the contents ofچ the various registers. Please note that the space after the "="چ is signifigant. &S= text &1= text or number &2= text or number &3= text or number ... &8= text or number Set the specified register. Numbers are really textچ strings, but you can treat them either way. The string must beچ under 20 characters. &A= value Set the accumulator to the specified value. Theچ accumulator is numeric only. &B= value Set the baud rate register to the specified value; if theچ value is not a valid baud rate then the &B register will notچ change. &C= value Set the free-running clock to an initial value. The clockچ counts up continuously every second. &T= value Set the trap register to the specified value. Setting itچ to zero disables the trap. ADD value SUB value Add or subtract the value to the accumulator. PUSH text Puts the text onto the top of data stack. There are onlyچ 10 levels so watch out. Anything can be pushed onto the stack. POP register Take the top item off the stack and place it in theچ specified register. If you try to put non-numeric text into aچ numeric register it becomes "0". You cannot POP items not PUSHed. .pa .sh Flow Control Instructions TRAP label This instruction is used to set a time limit on scriptچ execution, usually loops searching for a pattern. The trap usesچ the &C clock. The &T trap register is set to the specified number of seconds,چ and the &C clock is reset. Before each script instruction isچ executed, the clock is compared against the trap time; if theچ time limit is exceeded, the script branches to the specifiedچ label in the script file that initially set the trap; thereforeچ the trap can even abort scripts running many subroutine callsچ deep. Setting the &T trap register to zero disables the trap. &T= 0 &C= 0 TRAP label .... If at any time during script execution the &C clockچ reaches or exceeds the &T trap register, script executionچ proceeds from the specified label in the current script file. JMP label Branch to the specified label unconditionally. JZ label JNZ label Branch to the specified label in the current script fileچ if the accumulator is zero (JZ) or not zero (JNZ). This is usedچ to test previous arithmetic instructions. DJNZ label A basic looping instruction: "Decrement and Jump if Notچ Zero". Subtracts 1 from &A, and if the result is not zero,چ branches to the specified label. JERROR label Branches if the &E error register is not zero. &E isچ generally set by the MATCH instruction failing, the RETURN (fromچ subroutine) instruction. CALL scriptfile parameters ... Execute the script file as a subroutine. The filename isچ "scriptfile.SCR". The called script file can in turn call otherچ scripts, and each executes until the end of the script file or aچ RETURN instruction is executed. Each subroutine has it's own set of registers, &1 to &8.چ Parameters can be passed to the called routine, which set theچ initial value of the local registers. The contents of the localچ registers are lost when the subroutine returns. RETURN value Return to the calling subroutine (if any) and optionallyچ set the error register to the specified value. .pa .sh String Comparison Instructions MATCH pattern limit Wait until the pattern is found in the incoming characterچ stream before executing the next instruction in the script. Theچ pattern can contain "?" wildcard characters. There is an implicitچ limit of 60 seconds; if a match is not found with the time limitچ the error register is set and the next instruction executed.چ (Presumably a JERROR ... instruction.) Optionally a time limit,چ in seconds can be specified. SAMPLE Shifts one character into the shift register; to be usedچ in SAMPLE/IF/GOTO loops. IF pattern label Compares the pattern against the contents of the shift register, and branches to the specified label if it matches. The pattern can contain "?" wildcard characters. .pa .sh Character Output Instructions OUTPUT string Queue the string for output to the modem, simulatingچ manual keyboard entry. The speed with which characters are output is determined by the THROTTLE setting. COPY string Output the string directly to the modem at maximum speed. THROTTLE n (0 - 500) This controls the maximum speed that Fido/FidoNet will outputچ characters queued up by the OUTPUT command. The speed isچ specified in a minimum wait between characters, in milliseconds.چ The default is 20 milliseconds. .pa .sh File I/O Instructions Fido/FidoNet's file system is very simple; there can only be one fileچ open at a time. It is not a cooincidence that the file format isچ compatible with most BASICs; each record in a Fido/FidoNet fileچ contains 8 fields, each field is a quoted string seperated byچ commas. Each record is delimited with a CR/LF. There is noچ Control-Z or other end of file terminator. FOPEN filename Opens a file for reading or writing. The script isچ aborted if the file cannot be found, or if "UNATTENDED" is set,چ then the error register &E is set. The file must be FCLOSEd whenچ not needed. FNEW filename Creates a new file of the specified name. The script isچ aborted if the file cannot be created. The file will be emptyچ after creation. FREAD Reads one record from the file, and deposits the contentsچ of the record into the eight registers &1 to &8. Each record canچ contain any number of items, but only the first 8 will be used.چ Each should be an argument as described elsewhere, preferablyچ quoted. Lines are read from the file one per FREAD instruction,چ starting with the first line in the file, until the last line isچ read; all FREADs after the last line will leave all registersچ empty and set the error register &E. FWRITE A new record is added to the end of the open file, thatچ contains the contents of the eight registers &1 to &8. Each isچ "quoted". The script is aborted if the disk becomes full, or ifچ "UNATTENDED" is set, then the error register &E is set. th FCLOSE This closes the current file and allows the FOPENing ofچ another file. If any FWRITEs were done, they are written out toچ disk at this time. .pa .sh User Input Instructions KEYBD char Temporarily suspend script file operation until the userچ types the specified key; CR if none specified. All characters theچ user types, including the terminating one, are outout to theچ modem. This allows the user to manually enter something such asچ password. INPUT prompt The prompt is displayed and the user enters a line ofچ text, which is parsed into variables &1 through &8. Any extraچ text is ignored. ASK question The question is displayed and Y or N is input as aچ response, with &E set true if the answer is Y. PAUSE prompt The prompt is displayed and the user must hit any key toچ continue. MESSAGE message Output the message to the command status line. .pa .sh Special Instructions These instructions generally take one operand and perform someچ hopefully useful instruction. They are generally high levelچ functions to perform common functions. IOPORT n (1 - 2) Select the serial port to do business with; the defaultچ is the first port, on a pclone COM1. CD-BIT (1,2,4,8,16,32,64,128) Set the Carrier Detect (CD) mask bit for proper operationچ of the on-screen "ONLINE/OFFLINE" display. TIME hh:mm (00:00 - 23:59) Wait until the specified time of day before executing theچ next line in the script file. FILES filespec Sets &A to the number of files matching the fileچ specification. DELAY milliseconds (1 - 65000) Delay execution of the script until time elapsed. QUIET milliseconds (1 - 65000) Wait for no input from the modem for the specified time. DTR Lowers the modem control line Data Terminal Ready (DTR)چ for 1/2 second. BREAK Causes a 1/2 second line break.