The C- programming Language by Ray Davison ray_davison@sfu.ca May 1992 Revised October 1992 Computing Services Simon Fraser University Burnaby, B.C. Canada V5A 1S6 Introduction The C- programming language was developed as a vehicle for writing scripts for establishing serial line connections. In particular it was developed for Eudora, a Macintosh e-mail package. As the name implies, C- is based on a subset of C. The main differences are C-'s smaller set of control structures and operators, and the different handling of types, although there are many other minor differences. C- is an interpreted language and so some things (like local variables) may not be handled in quite the way you would expect in a compiled language. Because C- is imbedded in other programs (e.g. Eudora), frills such as error detection have been kept to a minimum to keep the implementation small. Because login scripts are usually small (the example in Appendix B to the contrary) it should not be too great a task to write scripts that are syntactically correct. Getting them to handle the information that is spewed at them is another thing entirely. 1. The C- Programming Language A C- program consists of a series of statements that are executed one after another. Each statement consists of various syntactic items that may be separated by liberal amounts of white space (spaces, tabs and carriage returns) and comments. Comments in C- are any sequence of characters starting with /* and ending with */. Note: In order to maintain compatibility with a precursor to C-, comments can also consist of any string of characters starting with # and continuing to the end of the line. Case is not significant in input to the C- interpreter, and except in string constants will be ignored. Statements in C- are either compound statements (statement blocks, whiles and ifs) or simple statements (variable definition, function call or assignment statements). Appendix C provides a BNF syntax for C-. The following sections describe in more detail the various statement types and other elements of the language. 2. Types, Operators and Expressions 2.1 Variables Variable names in C- are a sequence of characters, which may be letters, digits or the underscore. The first character must be a letter. Upper or lower case letters may be used, but are considered equivalent. Thus the variable name MYPASSWORD is the same as the variable name MyPaSsWoRd. Variable names may be any length, restricted only by available memory. If a variable is defined using the variable statement then it becomes a local variable. Any variable that is used but not defined is global. A global or local variable will be inherited by statements within subsequent statement blocks. There is a difference between global variables and local variables at the top level. While both are inherited by all parts of the script, global variables live from one invocation of the script to the next (and even from one script to another as from a Navigate In script to a Navigate Out script. See Appendix D) while local variables are disposed of when a block is finished or the script ends. 2.2 Data Types Variables and constants in C- are one of two types, numeric or character. Variable types are not fixed, but can change if assigned a value of a different type. 2.2.1 Numeric Types A numeric type is an integer value. C- does not support floating point numbers. Numeric constants are an optional plus or minus sign followed by one or more decimal digits. Note: Implementation restriction. Numbers are restricted to four byte integers. 2.2.2 Character Types Character constants are represented by strings of characters enclosed in double quotes. The backslash is used as an "escape" character to help represent certain other characters as outlined in the table below: \\ represents a single \ \" represents a " "" also represents a single " \n is a new line character \r is a carriage return character \t is a tab character \b is a backspace character \ddd is an arbitrary character where ddd is one to three octal digits. Note: Implementation restriction. Character strings may be any length, but internal Macintosh functions make 255 characters an upper limit for string lengths. 2.3 Operators C- expressions consist of constants, variables and function calls combined with a small set of operators. The operators include the four basic arithmetic operators, four relational operators and two logical operators. C- expressions follow the precedence rules for the operators as outlines in the following table: 1 (highest) multiplying operators *,/ 2 adding operators +,- 3 relational operators <, >, ==, != 4 logical operators &,| 2.3.1 Arithmetic operators C- supports the four basic arithmetic operators addition (+), subtraction (-), multiplication (*) and division (/). All of these operators take two numeric operands and will convert character string operands to numeric values if needed. 2.3.2 Relational Operators C- supports four relational operators: less than (<), greater than (>), equal to (==) and not equal to (!=). The result of these operators is a numeric 0 if the relation is false and a numeric 1 if the relation is true. If both operands are numeric then the comparisons are done as would be expected for such operands. If only one of the operands is a character string, then the other is converted to a character string before the comparison is done. Character strings are treated normally by the less than and greater than operators, with the result being a lexicographic comparison. The equal to and not equal to operators, however, are done as a "contains" comparison. So the expression: "abcd" == "bc" will yield a value of 1 (true) because "abcd" contains "bc". Note: To maintain compatibility with a precursor to C- a single = is treated the same as the equality operator == in circumstances where a comparison is expected. 2.3.3 Logical Operators C- supports two logical operators: and (&) and or (|). Both of these operators take two numeric operands and will convert character string operands to numeric values if needed. The operands are interpreted as false if 0 and true otherwise. The result is either a 0 for false or a 1 for true. The logical expressions are not evaluated as McCarthy-and and McCarthy-or. In other words, both operands are evaluated even if the outcome can be determined from the value of the first. 2.4 Expressions Expressions in C- consist of constants, functions and variables combined with the preceding operators, and optionally combined with parenthesis to force order of evaluation. 3. Simple Statements Simple statements in C- are described below. Each statement is followed by a semicolon. Note: In order to maintain compatibility with a precursor to C-, the semicolon is optional and its absence should not cause any ambiguity problems. 3.1 Variable definition The variable definition statement consists of the keyword variable followed by one or more variable names separated by commas. The variables defined in this statement become local variables at the level they are defined. If a local variable with that name at the same level already exists the statement does nothing. For example: variable MyPassword, MyId; Note: The variable command is an executable command. In a sequence of statements as shown below, if foo is a global variable, or a local variable inherited from an enclosing block, then the first reference to the variable foo is not the same as the last reference: foo = 1; variable foo; foo = 2; But then you wouldn't write code like this, would you. 3.2 Assignment statement An assignment statement is simply a variable name followed by an equal sign followed by an arbitrary expression. The variable will acquire the type of the expression (either numeric or character string). If the variable does not exist, a global variable will be created. For example: foo = substring(bar, length(bar)-3, 1); 3.3 Function calls Function calls can be used as stand alone statements, as well as parts of expressions. If a function that normally returns a value is used as a simple statement, its value is simply ignored. For example: get(2); /* get the next line and ignore it */ 4. Compound Statements The compound statements in C- allow the controlled execution of statements (if), repeated execution of statements (while) or grouping of statements (block). 4.1 Statement Block A statement block in C- is a sequence of statements (simple or compound) contained in left and right braces. Statement blocks can be useful to limit the scope of local variables and to combine statements to be executed within the if and while statements. 4.2 While statement The while statement consists of the key word while followed by an expression and a C- statement (simple or compound). If the expression evaluates to true (non zero) then the statement is executed and the expression is re-evaluated. The sequence of expression evaluation and statement execution will continue as long as the expression evaluates to true. For example: while get(1) != "DATAPAC" { while length(get(1)) ; send(".\r"); } 4.3 If statement The if statement consists of the key word if followed by an expression and a C- statement (simple or compound). If the expression evaluates to true (non zero) then the statement is executed and any optional elseif or else parts are skipped. If the expression evaluates to false (zero) then the optional elseif or else part will be executed. The if statement can optionally be followed by one or more elseif parts. Each of these consists of the key word elseif (which may have white space between the word else and the word if) followed by an expression and a statement (simple or compound). If the preceding if expression and all preceding elseif expressions have evaluated to false then the expression is evaluated and if true then the statement is executed. In this case all following elseif and else parts are skipped. The if statement can optionally be followed (after all elseif parts) by an else part. This part consists of the keyword else followed by a statement. If the preceding if expression and all subsequent elseif expressions evaluated to false then the statement is executed, otherwise it is skipped. For example: if substring(Phone_Number,i,1)<"0" | substring(Phone_Number,i,1)>"9" Phone_Number = Concat(Substring(Phone_Number,0,i), Substring(Phone_Number,i+1)); else i=i+1; 5. Built-in Functions Most of the work that your program will do while making a connection will be done using various built-in functions. All of these functions will convert their arguments to the type they expect. 5.1 Sending data out along the connection When your program needs to send data over the serial line, there are two functions to do this. Both functions take one or more character arguments and send the concatenation of them. Note that no carriage return or line feed will be added by these routines. If you want to terminate the string with a carriage return, you must do so yourself. Note: In the Eudora implementation of C-, strings should be a maximum of 255 characters long. In the case of Send and QuietSend this applies to the result of concatenating all of their arguments. Function Send(arg1, ...) Description The send function takes one or more strings as arguments. The concatenation of the strings is sent down the serial line and is also echoed in the progress window. Example send(popaccount(), "\r"); Function QuietSend(arg1, ...) Description The quietsend function takes one or more strings as arguments. Like the send function, the concatenation of the arguments is sent down the serial line, but is not echoed in the progress window. Instead the string <<>> is echoed. Example quietsend(password(), "\r"); Function Break(duration) Description The break function takes one numeric argument which is the number of ticks (60ths of a second) that the break should last. Example break(10); 5.2 Getting data from the connection Getting data back from the serial connection is done with the get function. Function Get(wait) Description The get function takes one numeric argument which indicates the maximum length of time in seconds to wait for data. If the wait exceeds the amount of time indicated, the function returns a null string. Otherwise it returns a string containing the data received. Example nextline = get(2); 5.3 Saving and restoring information The following functions are used to save and retrieve information that is either private to the script (and so can be retrieved during a later run) or modifies some variable used by Eudora (be careful with this). Function Save(rsrcnum, [item,] value) Description The save function modifies an item in an STR or STR# resource. The resource number is given as the first argument and, for an STR# resource, the string position is given as the second argument. The final argument is a string value that is placed in the resource. Example save(7400, 13, "telnet -r %p %d\r"); Function Restore(rsrcnum[, item]) Description The restore function retrieves a value from an STR or STR# resource. The resource number is given as the first argument and, for an STR# resource, the index into that resource is given as the second argument. The value returned by the function is the value of the string resource. If the resource doesn't exist, then the value returned will be a null string in the case of an STR# resource. For an STR resource the value returned will be a nonexistant variable, which can ge tested for with the exists() function. See Appendix G for more on string resources. Example CTBtool = Restore(1001); 5.4 Functions on strings Because C- was designed to control the sending of information over a serial connection, it is no surprise that there are a few functions for dealing with character strings. Function length(string) Description The length function takes a string as an argument and returns a number indicating the number of characters in the string. Example if length(Phone_Nbr)==0 progress("Turn on your Blue Box"); Function substring(string,start,length) Description The substring function takes a string as its first argument and returns a part of that string starting at the point indicated by the second argument (zero indicating the first character) and for a length indicated by the third. If the length argument is missing or larger than the rest of the string then the rest of the string is returned. If the length argument is zero or negative, or the start argument is past the end of the string then a null string is returned. Example line = substring(line, 0, length(line)-1); Function concat(arg1,...) Description The concat function takes one or more strings as arguments and returns a string that is the concatenation of all its arguments. Example line = concat(line, "\r"); 5.5 A dialog with the user There are times when it is necessary to inform the user of certain things, or to get information from the user. Function dialog(dialogid, arg1,...) Description The dialog function puts a dialog box on the screen and waits for the user to click on one of the dialog's buttons. The first argument is either a number corresponding to a DLOG resource or a character string corresponding to a DLOG resource name. Up to four more parameters may be given, and the text of these parameters will replace the special strings '^0' through '^3' in all statText items in the dialog. You must use ResEdit to create the DLOG resource and its corresponding DITL resource. The function returns a number indicating the button the user clicked on to dismiss the dialog. If item number one in the dialog is a button, then the dialog function will outline that button before displaying the dialog. The value in any user-editable fields are returned in variables named DialogStrn where n is the item number of the text field. The variables are assigned values as though there was an assignment statement at that point, so if there is an appropriately named local variable defined at that point, it will be assigned the value, otherwise a global variable will be used (and created if necessary). The user-editable fields are also initialized if an appropriatly named DialogStrn variable exists. The lowest numbered of these fields is selected and the cursor is placed in that field. Example dialog("OK", "Datapac is not available at this time."); Function pwdialog(dialogid, arg1,...) Description All information about the dialog function applies to the pwdialog function as well. The difference is that the pwdialog function uses a filter to blank out text entered in one of the user-editable fields of the dialog. As the name implies, this can be used to prompt for passwords. This filter is also used by Eudora for its own password prompt, so certain fields in the dialog should only be used for certain purposes. item 1 - an OK button (return or enter are like clicking here) item 2 - a CANCEL button (command-. is like clicking here) item 4 - a text field to be blanked item 5 - a field to be flashed on and off if the caps lock is down Any other items that are text fields will not be blanked. Note that if item 3 is a text field then the cursor will be placed in that field first, rather than in item 4. This is useful when prompting for both an id and password in one dialog, as the user would expect to enter the id first. Example pwdialog("OurPassWord", OurAccount); Function progress(arg1, ...) Description The progress function is used to place a character string in the progress window (the small rectangular window used by Eudora to display information on the progress of the connection). The progress function takes one or more character strings as arguments and concatenates them before displaying them in the progress window. Note: In the Eudora implementation of C-, strings should be a maximum of 255 characters long. In the case of Progress function this applies to the result of concatenating all of its arguments. Example Progress("Turn on your Blue Box."); 5.6 Information about the environment It is often useful to get various pieces of information about the environment that the user has set up. Some of the information available from the following functions can also be retrieved using the restore function, but these routines are often more convenient. Function password() Description The password function takes no arguments and returns a string containing the users password. If the password was not saved by Eudora and has not yet been prompted for, Eudora will be asked to prompt for it at this time. Example quietsend(password(), "\r"); Function forgetpassword() Description The forgetpassword function will make Eudora throw away the password it has. This will cause Eudora to re-prompt for the password the next time it needs it, including the next use of the script password function. Example forgetpassword(); Function popaccount() Description The popaccount function takes no arguments and returns a string containing the users POP account. Example send(POPaccount(), "\r"); Function popserver() Description The popserver function takes no arguments and returns a string containing the POP server name. Example ourserver = POPserver(); Function smtpserver() Description The smtpserver function takes no arguments and returns a string containing the SMTP server name. Example oursmtp = SMTPserver(); Function mousedown() Description The mousedown function returns 0 if the mouse button is up and 1 if it is down. Example if mousedown() trace(1); Function getconfig(item) Description The getconfig function takes one argument which is a string indicating which Communications Tool Box tool variable to return. The value returned is a string containing the value of that variable if it exists, or a null string if it doesn't. Appendix A contains the variables available for the Apple Modem Tool and the Serial Tool. Example Phone_Number = GetConfig("PhoneNumber"); 5.7 Miscellaneous Functions A few miscellaneous functions. Function quit() Description The quit function causes the script to abort. It does not return and the connection will be abandoned by Eudora. Example quit(); Function trace(level) Description The trace function is a debugging tool that may be useful while composing a script. The function takes one numeric argument which indicates the detail of tracing needed. The value can range from 0 (the least information) to 2 (the most). Appendix E contains a description of the information produced by the trace function. The trace information will be dumped unceremoniously in a file called "Trace" which will be created if necessary, and will be given the type TEXT. Example if mousedown() trace(1); Function echo(state) Description The echo function takes one numeric argument which controls the echoing of data by the get function. Normally echo is true and information retrieved by the get function is echoed in the progress window. If echo is called with a zero argument then the information from get will no longer be echoed in the progress window. Echoing can be turned on again by using a non-zero argument. Example echo(0); Function trapecho(state) Description The trapecho function takes one numeric argument which controls the handling of echoing of characters when doing a send. Normally trapecho is false and the next get statement will return the echoed characters. If you use trapecho(1) then the echoed characters will be ignored. The state of trapecho is used after the script is finished as well, so if characters will be echoed by the system connected to, it is important to set trapecho on. Example trapecho(1); Function exists(variable) Description It is sometimes useful to assign a variable the first time a script is run, and use that value during subsequent runs without resetting it. For example, if your script needs to prompt the user for some information, such as a second password, you may not want to bother the user each time the script is used to make a connection. The exists function can be used for this purpose. It takes as an argument a variable name and returns zero if the variable has not yet been assigned a value, otherwise it returns one. Example if 0==exists(ourpassword) { /* prompt for password here */ } 6. Helpful Hints The mousedown function can be used to turn on trace information while debugging. For example: if mousedown() trace(1); It is probably unwise to leave this in the script when you are finished because if the user has set auto check for mail on than while Eudora is checking for mail, the user might well have the mouse button down and generate an unwanted Trace file. The telnet command that Eudora uses to connect to the POP, SMTP or PH server is kept in STR# resource 7400 at position 13. As outlined in the Eudora documentation, a "%p" in the string is replaced with the server name and a "%d" is replaced with the port number. You can use the save function to modify this string. For example save(7400, 13, "telnet %p %d\r"); The line end character(s) used by Eudora when writing lines to the servers is kept in STR# resource 6000 at position 17. You can use the save function to modify this string. For example: save(6000, 17, "\r"); At various points in your script you may want to read and ignore information being sent down the line by the computer or terminal server you are connected to. An easy way to do this is to use an empty while loop that waits for the line to be quiet for some fixed length of time. For example, if we want to wait until no data comes down the line for two seconds, we can use the following command: while length(get(2)) ; While C- will complain about the use of uninitialized variables at run time in most circumstances, assignment is not one of them. You can use this to reset a variable to an uninitialized state by assigning it an uninitialized variable. For example, if you use the exists() function to prompt for a password the first time through a script, you could allow the user to force a prompt for a password by holding down the mouse button on subsequent connections by putting the following line at the beginning of the script: if mousedown() userpw = dummyvalue; if exists(userpw) == 0 { . . . } Appendix A CTB variables The GetConfig function is used to return the value of various Communication Tool Box variables. The following list shows what variables are available from the Apple Modem Tool and the Serial Tool. The variable name must be given as shown (i.e. upper/lower case is significant). If the variable name does not exist for that tool, a zero length string is returned. The following table is for the Apple Modem Tool. Variable Description Example Baud Baud rate of modem. "2400" DataBits Number of data bits to use. "5" Dial Dialing method. "Tone" Handshake Type of handshaking on connection. "None" HoldConnection When true, tool does not drop DTR while closing connection. "True" ModemType Type of modem to which computer is connected. "Apple Data Modem 2400" Parity Type of parity on connection. "None" PhoneNumber Phone number to dial. "291-5947" Port Current port for sending and receiving data. "Modem Port" RemindDisconnect When true and HoldConnection is true, tool reminds user it is holding DTR high. "True" Retry Specifies whether tool should retry number when remote modem does not pickup. "True" RetryInterval Number of seconds between retries. "1" RetryTimes Number of times to retry. "3" StopBits Number of stop bits on connection. "1" TypeOfCall Specifies whether originating or answering a call. "Originate" WaitRings Number of rings to wait before answering incoming call. "2" The following table is for the Serial Tool. Variable Description Example Baud Baud rate of modem. "2400" Data BitsNumber of data bits. "8" Handshake Specifies type of handshaking on connection. "None" HoldConnection When true, tool does not drop DTR while closing connection. "True" Parity Type of parity on connection. "None" Port Current port for sending and receiving data. "Modem Port" RemindDisconnect When true and HoldConnection is true, tool reminds user it is holding DTR high. "True" StopBits Number of stop bits on connection. "1" Appendix B Example Script The following script is used by Eudora to connect over a serial line to Simon Fraser University. It detects (by looking at the phone number) if the user is calling in over Datapac (Canada's public packet switched network) or not. If not then the user is either calling in over a phone line or is using a directly connected serial line. In either case the connection is through a Gandalf PACX and then an Annex terminal server. The terminal server requires the user's Unix id and password. /* This is a script used by Eudora to login via dialup to the PACX and then into the annex terminal server or via Datapac. */ variable nextline; /* used to store the next input line */ variable Phone_Number; /* the CTB phone number being called */ while length(get(2)); /* loop until the line is quiet */ Phone_Number = GetConfig("PhoneNumber"); /* Try to tell from the phone number if this is a call to Datapac. */ variable i; i=0; /* strip all the non-numbers out of the phone number */ while (i "9" ) Phone_Number = Concat(Substring(Phone_Number,0,i), Substring(Phone_Number,i+1)); else i=i+1; } /* if it isn't a 291 number, then it must be Datapac */ if (length(Phone_Number) > 6 & substring(Phone_Number,length(Phone_Number)-7,3) != "291") { send(".\r"); /* Datapac needs a dot to wake it up */ while (get(1) != "DATAPAC") { while length(get(1)); send(".\r"); } send("83501100\r"); /* send our datapac address */ nextline = get(1); while (nextline != "call connected") { if (nextline == "destination not responding") { Dialog("OK", "Datapac is not available at this time."); quit(); } nextline = get(1); } while length(get(2)); /* read the rest of the stuff */ /* Set the telnet command for the VAX */ Save(7400, 13, "telnet %p /PORT=%d\r"); TrapEcho(1); /* Let Eudora know we can't shut echo off */ } /* Else it isn't Datapac, so connect through the PACX to the annex. */ else { /* Wake up the PACX and tell it we want the annex terminal server */ send("\r"); /* send a carriage return to wake up the PACX */ get(1); /* ignore echo of carriage return */ nextline = get(1); echo(0); /* turn off echo so our "progress" message sticks around */ while (nextline != "INVALID RESPONSE" & nextline != "ENTER CLASS" & nextline != "Checking authorization") { while length(get(1)); send("\r"); /* send a carriage return again */ if (length(Phone_Number)==0) progress("Turn on your Blue Box"); get(1); /* ignore the echo again */ nextline = get(1); } echo(1); /* turn echo back on now that we are connected */ /* If we are connected to the PACX, give it what it wants. */ if (nextline == "INVALID RESPONSE" | nextline == "ENTER CLASS") { send("16\r"); /* port 16 for the annex */ get(0); /* get echo of annex request */ /* If all ports to the annex are busy, we can wait. */ if (get(0) == "ENTER CLASS BUSY") /* oops, no lines available */ { if (dialog("ports busy",get(0)) != 1) /* ask the user to wait */ { while length(get(2)); send("n\r\r\r\r"); /* send "n" for no and enough returns to make PACX hang up */ quit(); /* let Eudora know things didn't work */ } while length(get(2)); send("y\r"); while (get(0) != "You are subscriber") ; /* wait until a line is free */ } while length(get(2)); /* Now try to wake up the annex */ send("\r"); while (get(1) != "Annex") { /* loop until we hear from the annex */ send ("\r"); } } /* Now login to the annex */ while length(get(1)); variable TryingPassword; TryingPassword = 1; /* Give the Id/Password a couple of tries in case noise eats them */ while (TryingPassword) { send(popaccount(), "\r"); get(1); /* get the echo */ quietsend(password(), "\r"); variable WaitingForDecision; WaitingForDecision = 1; while WaitingForDecision { nextline = get(0); if (nextline == "Permission granted") { /* Everything is OK, so break out of these loops. */ TryingPassword = 0; WaitingForDecision = 0; } else if (nextline == "Permission denied") { /* Something is wrong with the ID/password so give up */ Dialog("OK", "Your password is incorrect. Try again."); /* We will only forget the users password if it wasn't saved. otherwise the user will have to forget it manually. */ if (restore(1000,24) != "y") ForgetPassword(); Quit(); } else if (nextline == "Incorrect") { /* Bad ID/password, but maybe we got hit with line noise */ WaitingForDecision = 0; } } } send("stty -echo\r"); /* Eudora doesn't like things echoed */ while length(get(1)); /* Eat up any other data the annex may send */ /* Finally, make sure Eudora sends the right things */ Save(7400, 13, "telnet -r %p %d\r"); /* the telnet cmd for the annex */ } Save(6000, 17, "\r"); /* the line end char */ Appendix C BNF Syntax The grammar that follows is a slightly simplified version of the grammar used by the C- interpreter. The actual grammar contains rules required by the interpreter to perform semantic actions at the appropriate points. ::= ::= | ::= | ; ::= | | ::= | | ::= { } ::= if ::= ::= else | elseif | empty ::= while ::= variable ::= | , ::= identifier ( ) ::= | , | empty ::= identifier = ::= | & | ::= | < | > | != | == ::= | + | - ::= | * | / ::= ( ) | ::= integer | string | | identifier Appendix D Eudora Implementation Before trying to understand how C- scripts work with Eudora, it is a good idea to read Appendix D of the Eudora Reference Guide and understand how Eudora Navigation works. If the navigation feature in Eudora will work for you, you may not have to write a script at all. If you do need a script then you will need to write from one to three scripts. Like the Eudora Navigations Strings, the scripts consists of a Navigate In scripts for getting connected, a Navigate Mid script for getting ready for another server connection, and a Navigate Out script for closing the connection. The scripts are kept in separate TEXT resources numbered consecutively. If the Navigate In script is 128, for example, the Navigate Mid script must be 129 and the Navigate Out script must be 130. Both the Navigate Mid and the Navigate Out script may be missing if not needed. The Navigate In script must have a name, and the name must be in three parts. The first, possibly null, part is ignored. The second part must be "Script" with the capital S. The final part is the name presented by the user to allow them to select the script. The Navigate Mid and Navigate Out scripts should not have names. Each collection of Navigate In, Navigate Mid and Navigate Out scripts along with associated dialogs can be kept in separate files or Reseditted into Eudora. If they are in separate files, then they must be included as Eudora Plug-in resource files. Each script will be presented to the user in a pop up menu in the Communications dialog. Users can then select the appropriate script much as they select the CTB Tool to use. If the user chooses no script, then the process as outlined in Appendix D of the Eudora Reference Guide is followed to find an STR# resource to use for its standard navigation procedure. Appendix E Reading C- traces The trace function in C- is a debugging tool that may be of use when trying to figure out exactly what the machine at the other end is sending and what it expects in return. One thing is sure about the trace function, it will produce a lot of data. Each level of trace, from 0 to 2 produces more output. The following describes each level and what the output will look like. Level 0 At level 0, the information written out by the various built in functions or other parts of the language to show what they are doing. For example, the get function might write out a line such as: Text returned -> "sfu> " As another example, a text operator might product out like the following (note that the carriage return at the end of the first string causes a new line in the Trace file): Test: Left side = "DATAPAC: 8340 0301 ". Test: Right side = "DATAPAC". As a final example, a while or if statement whose expression was false (i.e. had the value 0) would write out the following line: Execution suspended..... Level 1 At level 1, all the information of level 0 is also produced. In addition, the parser will produce a line of output each time a "semantic routine" is called. The semantic routines are where the work is done by the language and some of these produce the output at level 0. In addition to the name of the semantic routine called, the output line contains a short sample of the script program with a vertical bar indicating where the parser is. This may give you a bit more help following the execution of your script. An example of some (slightly cleaned up) output at level 1 follows. You can see that the while test was true so the parse point is set back to the beginning of the while in the last line of the example. @Pop_Clean ... Datapac address */\n|while length(... @Push_Parm ... Datapac address */\nwhile| length(... @Push_Parm ...while length(|get(2)); /* read a... @Push_Parm ...while length(get(|2)); /* read a... @Push_Integer ...while length(get(2|)); /* read a... @Pop_Parm ...while length(get(2|)); /* read a... @Get_Function ...while length(get(2)|); /* read a... Text returned -> "Simon Fraser University, Computing Services " @Pop_Parm ...while length(get(2)|); /* read a... @Length_Function ...while length(get(2))|; /* read a... @Pop_Parm ...while length(get(2))|; /* read a... @Do_Test ...while length(get(2))|; /* read a... @Test_While ...while length(get(2));| /* read a... @Pop_Clean ... Datapac address */\n|while length(... Level 2 This level of trace is probably only useful to the developers of Eudora and those wishing to use up lots of disk space for the trace file. In addition to all the information produced by the level 1 trace, level 2 provides a detailed trace of the parse of the script. Thus as each character in the script is analyzed, one or more lines of trace may be generated. Appendix F C- Error Messages C- will catch a number of syntax and run-time errors. The Eudora version of C- will put up a dialog box with the error message and some information on where the error occurred. When you press the OK button, the script will be terminated. For most errors, the dialog box will contain two numbers. The first will be the offset into the script text that the current syntactic item started. The second number is the current position in the text. These two numbers should give you some idea where to look for the problem. For example, if the error is "Missing right parenthesis", then the second number is where in the script the parser expected the right parenthesis, and the first number will point to the left parenthesis that was being matched. The memory error message contains different information. It too gives two numbers. The first is the error number from the system (for example -108 is the error message meaning "Not enough room in heap zone".) while the second number is the line number in the source file for the C- semantic routine (useful only if you suspect a bug in the C- interpreter). Appendix G Eudora String Resources There are a number of string resources in Eudora that may be referenced from within scripts. Some of these may be safely changed from a script as well. See the SAVE and RESTORE functions for information on how to access string resources. The preferences set by the user with the Configuration and Switches dialogs are kept in STR# 1000. In particular are the following: 61 The Dialup Username from the Configuration screen. 62 A place to store a second password. 63-69 Strings for use within scripts. 70 The name of the script. Other strings that may be of use: STR 1001 contains the name of the CTB Tool being used. STR# 7400, 13 contains the format of the telnet command used by Eudora to connect. May be set by a script. STR# 6000, 17 contains the end of line character sent to the host by Eudora. May be set by a script. See the Eudora Q&A stack for information on other string resources.