VT Program Documentation Description ----------- Vaportalk (VT) is an Internet client program. Its main features are an I/O scheduler for communication with remote hosts, a minimal windowing system, and a C-like language called VTC. VT and its documentation were written by Greg Hudson, who can be reached via Internet email at ghudson@athena.mit.edu. This file documents the VT program. It makes reference to the distribution VTC code from time to time, but does not describe it fully. Most users of VT will initially be more concerned with the distribution documentation in dist.doc. Startup ------- VT must read a startup file to initialize key bindings and other parameters. The startup file is a VTC file 1. If VT is invoked with an argument, it loads that file. 2. If the environment variable VTSTART exists, VT loads that file. 3. VT searches for the following files, in order: ~/main.vtc ~/vt/dist/main.vtc ~/vt/main.vtc /usr/local/lib/vt/main.vtc Once one file is loaded, VT stops searching for others. If VT is invoked with an argument or the VTSTART environment variable exists, and VT cannot load either of those files, then it will print a warning message. VT will exit if it cannot find any startup file. Environment variables --------------------- Apart from the VTSTART environment variable, VT looks in the VTBOLDON and VTBOLDOFF variables for the names of the terminal capabilities to use for turning boldface text on or off. These default to "md" and "me", which are the usual boldface capabilities. (A common alternative pair is "so" and "se", which turn inverse text on and off.) The Compiler ------------ VTC is a C-like language. It does not feature strong typing, type definitions, or a switch statement. It does feature pointers and pointer arithmetic, structures in modified form, and dynamic allocation of arrays and strings. VTC Syntax ---------- VTC tokens are of five types: string constants, integer constants, operators, reserved words, and identifiers. Reserved words and identifiers are case-sensitive. The VTC compiler ignores spaces and tabs, except as they separate tokens. It also ignores text between C comment delimiters (/* and */), and the rest of a line after a pair of slashes (//). VT character and string literals are similar to old-style C character and string literals. A character literal is a single character inside single quotes ('c'), while a string literal is a sequence of characters inside double quotes ("This is a string literal"). To specify an unprintable or special character in a character or string literal, use one of the following codes: \n Newline \t Tab \v Vertical tab \b Backspace \r Carriage return \f Form feed \\ Backspace \' Single quote \" Double quote You do not need to escape single quotes, and you only need to escape double quotes within string literals. You may also follow a backslash with up to three octal digits specifying the ASCII code for the character. The parser will ignore a newline preceded by a backslash, allowing the string literal to continue on the next line. An integer constant is a series of digits with no suffix. To specify an integer in octal, begin the series of digits with 0. To specify an integer in hexidecimal, prefix the series of digits with 0x or 0X. The compiler reserves the following words: do while if else for func goto break continue return The compiler also reserves the names of builtin objects and named constants (q.v.). The compiler does not reserve the names of VTC or primitive functions. You may use those names as variables, although this may make your code confusing to read. An identifier is a sequence of letters, numbers, and underscores, beginning with a letter or an underscore. Use identifiers to refer to variable and function names. Identifiers are significant to 32 characters and are case-sensitive. A directive to the VTC parser is either a function definition or a command. These have the formats: definition: func ident (params) [local vars] compound func ident (params) --> expr ; command: [local vars] statements [local vars] may be omitted in either case, but the square brackets are literal. Command directives are executed immediately, while function definitions are saved so that you can call the functions later. "--> expr ;" is a shorthand for "{ return expr ; }" in a function definition. Parameters and local vars are sequences of zero or more identifiers separated by commas ("foo, bar, baz"). Parameters can also be two such sequences separated by a slash ("foo, bar / baz, quux"), in which case the identifiers in the second sequence denote optional parameters. Calling a function with fewer arguments than it has non-optional parameters results in a runtime error, although calling a function with more arguments than it has parameters is legal. Some examples: func echoln(line) { echo(line, "\n"); } /* Echo a line */ [i] for (i = 0; i < 5; i++) echoln(itoa(i)); // Count to 5 A statement can be one of the following: statement: compound expression; if (expression) statement if (expression) statement else statement while (expression) statement do statement while (expression) for (expression; expression; expression) statement label: statement goto label; break; continue; return expression; You can omit the expressions in the expression statement and the return statement, as well as any of the expressions in the for statement. ";", "for (;;);", and "return;" are all valid statements. A compound statement, as in C, is zero or more statements contained in { and }. All the flow control statements are identical their counterparts in C. Expressions are as follows: lvalue: * expression identifier expression[expression] expression->identifier (lvalue) expression: lvalue constant builtin-object . identifier identifer(args) (lvalue)(args) (expression) prefix-operator expression expression postfix-operator expression binop expression lvalue assign-op expression expression ? expression : expression expression ? : expression ! expression ~ expression - expression + expression & lvalue ++ lvalue -- lvalue lvalue ++ lvalue -- ". identifer" indicates a pointer to the function named by the identifier. In C, you might write: extern int bar(); int (*foo)() = bar; (*foo)(args); /* or foo(args); for most compilers */ In VTC you would write: foo = .bar; (*foo)(args); In most C compilers, you can write "foo(args)" instead of "(*foo)(args)". VTC does not permit this. Expressions are mostly the same in VTC as in C. The logical operators, as in C, only evaluate their right-hand arguments if their left-hand arguments are not sufficient to determine their truth values. The operators are, in order of precedence from highest to lowest: unary: . unary: ! ~ ++ -- + - * & binary: * / % binary: + - binary: << >> binary: < <= > >= binary: == != binary: & binary: ^ binary: | binary: && binary: || binary: ?: assignment: = += -= *= /= %= &= != <<= >>= ?:= binary: , The unary, assignment, and conditional operators associate right to left. All other operators associate left to right. The interpreter evaluates arguments and binary expressions left to right. 'args' refers to zero or more expressions separated by commas. The comma binary operator does not apply to expressions in an argument list. Outside of an argument list, a list of expressions separated by commas takes the value of the last expression in the list, as in C. As noted in the expression syntax, you can omit the middle argument of a conditional expression, in which case it evaluates to the value of the left argument if the left argument is true, and to the value of the right argument if the left argument is false. There is also a ?:= operator; '(a ?:= b)' is equivalent to '((a) = (a) ? : (b))'. The + operator can take two string pointer arguments, producing a concatenated string. The form of input to the parser is slightly restricted. The parser must know when the user has completed a parser directive (command or function definition) because of the way it accepts input. The parser assumes that this is true at the end of a line unless one of the following conditions holds: (1) You escape the newline with a backslash. (2) You have entered more { tokens than } tokens (you are in the middle of a compound statement). (3) You have entered a func token but not a --> token or a { token (you are declaring a function and have not begun the function body). (4) You have entered a "/*" but no matching "*/". The following is a syntactically legal sequence of input: func foo() { bar(); } { foo(); bar(); } foo(); bar(); if (foo()) \ bar(); Note that a line like: foo(); bar(); is somewhat different than two separate lines: foo(); bar(); The intepreter treats the first as a single task and the second as two tasks. In the first case, if foo() aborts, bar() will not be executed, and if foo() is suspended due to an input request or sleep() primitive, bar() will wait until foo() exits before running. In the second case, bar(); will run right after foo() terminates or is suspended. The parser will become confused if a directive contains unbalanced left and right brace tokens. A sufficiently long series of right braces will rectify this situation. Fatal Errors ------------ VT will abort a task whenever the task passes arguments of the wrong type to a primitive, attempts to assign to a negative array or string element, passes too few arguments to a VTC function, calls or makes a pointer to an undefined function, or makes an illegal assignment to a builtin variable. See the section on "non-fatal errors" below for information on how VT deals with other error conditions. Data ---- VT has eleven data types, each associated with a named constant: Type Description ---- ----------- T_INT Integer T_PPTR Primitive pointer T_BPTR Builtin object pointer T_RMT Remote pointer T_WIN Window pointer T_KEY Key binding pointer T_FILE File pointer T_SPTR String pointer T_APTR Array (or variable) pointer T_FPTR Function pointer T_REG Regexp pointer T_ASSOC Association type T_PLIST Property list pointer T_NULL No value Use NULL to refer to the data object with type T_NULL. 0 and NULL are false. Every other data value is true. Variables --------- There are three types of variables: parameters, local variables, and globals variables. You declare parameters in a function declaration, and local variables in the square brackets at the beginning of a function declaration or command. You can store any type of data in a variable. You can take its address with the & operator. The address of a variable is an object of type T_APTR. VT will reset any data value holding the address of a parameter or local variable when those variables go out of scope. To refer to a global variable, simply use its name in a variable context, assuming you are not in the scope of a local or parameter variable with the same name. If the global variable does not already exist, VT will create it and initialize it to NULL. Arrays ------ Arrays are sequences of variables. They are of three classes: arrays returned by the alloc() primitive, local and parameter variable arrays, and the global variable array. The first and third type of array will stretch if the user attempts to assign values to an array element past the current amount of allocated space, so those arrays can be thought of as an infinite storage space with a fixed beginning. Uninitialized array elements and an elements before the beginning of an array have the value NULL. Assigning to an element before the beginning of an array results in a runtime error. You cannot explicitly free an array. VT frees a function's local and parameter arrays when the function exits. VT frees arrays returned by alloc() when there is nothing pointing to them. VT will not automatically free a self-referential array or group of arrays if nothing external is pointing to them, but you can free all such groups of arrays by calling the garbage() primitive at any time. alloc() takes a size argument. This number will not affect program correctness, since the array will stretch if necessary. It is only for reducing wasted space and unnecessary resizing. Property lists and association types ------------------------------------ Property lists vaguely mirror structures in C. Here is an example using of using a property list: Tmytype = new_assoc(); p = alloc(2, Tmytype); p->name = "Druid"; p->num = 673; echo("p->name: ", p->name, ", p->num: ", itoa(p->num), "\n"); A property list consists of an array and an association type. To create a new association type, use the new_assoc() primitive. All plists with a given association type will have the same associations between element names and array offsets. Each time you use a new element name with a plist of a given association type, it assigns an array offset to it, starting at 0. To retrieve an element of a plist, you can use the lookup() primitive. "lookup(plist, str)" returns a pointer to the array element in 's array containing the element . If is a simple string constant, you can use "plist->str" as an abbreviation for abbreviation for "*lookup(plist, str)". You can retrieve the array part of a plist using the base() primitive. After the above example, base(p)[0] would be "Druid" and base(p)[1] would be 673. You can use the lookup() operator with windows, remotes, and association types in addition to plists. "lookup(win, str)" and "lookup(rmt, str)" are short for "lookup(obj(win), str)" and "lookup(obj(rmt), str)" respectively. "lookup(assoc, str)" returns the integer offset of for the association type . You can "lookup(assoc, str)" to force the order of associations for an association type. For instance: world_type = new_assoc(); &world_type->name; &world_type->addr; &world_type->port; world = alloc(3, world_type); acopy(base(world), table("Name", "Addr", 1111), 3); By forcing the order of associations after creating the association type, we can initialize new worlds directly by using acopy() and base(). (The table() function comes from the distribution.) Note that "&world_type->name" is an abbreviation for "&(*lookup(world_type, "name"))", which is the same as "lookup(world_type, "name")". If you leave out the &, you will be dereferencing an integer, which is an illegal operation. Strings ------- Strings are treated in much the same as arrays. They will extend with an attempt to write past their ends using a primitive or an assignment operator. Intervening elements are initialized to spaces. That is, [a] a = ""; a[10] = '#'; echo(a); writes a string of ten spaces followed by a '#'. Strings are null-terminated, so setting a string element to 0 shortens the string if the string previously stretched being the array element in question. Elements past the new end of the string are irretrievably lost. A string constant in VTC code is inviolate. Each reference to that constant produces a different copy of the string. That is, the following code: [a, i] { a = alloc(2); for (i = 0; i < 2; i++) a[i] = "foobar"; *a[0] = 'g'; echo(a[1]); } will produce the output "foobar," not "goobar." String elements beyond the end of a string or before the beginning have the value 0. Assigning to an element before the beginning of a string results in a runtime error. When a primitive takes a string argument, it accepts a string pointer, which is taken to be the beginning of the string. Functions --------- You define functions using the parser. You cannot undefine functions, although you can redefine them. Your VTC code can contain a call to an undefined function or a pointer to an undefined function, but this will produce a runtime error if you do not define the function before executing that part of the code. Function declarations contain zero or more parameter variables. Calling a function with fewer arguments than there are parameters results in a fatal error. You can call a function with more arguments than there are parameter variables. The function can use the argc and argv builtins to access these arguments. The argc builtin returns the number of arguments passed to the function, and the argv builtin returns an array that contains all of the arguments, including the declared parameters. You can declare some of your function's parameters to be optional, by preceding them with a '/'. For instance, in the parameter list "(foo, bar / baz, quux)", baz and quux are optional arguments. These parameters have the value NULL if you do not specify them in a call to the function. Functions return a value specified by a return statement, or NULL if they exit normally. Many functions have the form: func name(args) { return expr; } where is a short expression. A shortcut for this is: func name(args) --> expr If takes several lines, you must escape each newline explicitly with a '\', so this construct is best only for short expressions. Windows ------- VT divides the screen into two areas: an input window occupying several lines at the bottom of the screen, and an output region, which is further divided into one or more output windows. Each output window has a divider line below it. When VT starts up, the input window occupies three lines, and the rest of the screen is used for one output window. At all times, one of the output windows is 'active'. This window has an asterix (*) in the first column of the divider line below it. To change the active window, use the ^XO key combination defined by the distribution VTC code. The input window can be in either VTC mode or text mode. In VTC mode, a line entered in the input window will be parsed, while in text mode it will be passed to the active window. To change modes, use the ESC ESC key bombination defined by the distribution. VT checks each character typed against a list of key bindings. If a character completes any one of these bindings, VT executes an editing function or a VTC function. If the character only partially matches a multiple-character key binding, then VT holds onto it to see if following characters match the rest of the sequence. If they do not, or if a character does not match a key binding at all, then VT displays it in the input window. Following is a list of editing functions, along with the bindings assigned to them by the distribution. The mnemonics are named constants in the VTC compiler. To get a list of key bindings from with the client, use the command "/list_keys" defined by the distribution. Mnemonic Keys Description -------- ---- ----------- K_CUP ESC [A Cursor up K_CDOWN ESC [B Cursor down K_CLEFT ESC [C Cursor left K_CRIGHT ESC [D Cursor right K_CHOME ^A or ^[[H Cursor to start of line K_CEND ^E or ^[[K Cursor to end of line K_CWLEFT ESC B Cursor word left K_CWRIGHT ESC F Cursor word right K_BSPC ^H or DEL Delete character left K_BWORD ^W Delete word left K_BHOME ^[K Delete to start of line K_DBUF ^X or ^U Delete input buffer K_DCH ^D Delete character right K_DWORD ESC W Delete word right K_DEND ^K Delete to end of line K_REFRESH ^R Rewrite the input window K_REDRAW ^L Redraw the screen K_MODE ESC ESC Toggle input window mode K_PROCESS newline Process input buffer VTC tasks can intercept keystrokes using the getch() primitive. getch() takes a priority argument to determine whether to check key bindings before returning the keypress, and an optional window argument specifying which window must be active in order for a keypress to be intercepted. VT dispatches keypresses as follows: Resume task that called getch(HIGH) if any, or Resume task that called getch(HIGH, window) if any, or Execute key binding, if matched, or Hold key, if key partially matched key binding, or Resume task that called getch(LOW) if any, or Resume task that called getch(LOW, window) if any, or Insert in input buffer When the user enters a line in text mode, VT "passes" it to the active window. A VTC task can also do this with the pass() primitive. VTC tasks can intercept lines passed to a window using the read() primitive. read() with no arguments will intercept a line entered while any window is active; read() with a window argument will intercept a line entered while that window is active. Windows have a "termread" function to handle unintercepted lines. VT dispatches user input lines as follows: Parse, if the input window is in VTC mode, or Resume task that called read() with no arguments, if any, or Resume task that called read(window), if any, or Run active window's termread function, if it exists, or Send to active window's remote connection, if it exists, or Discard VT calls the window's termread function with one argument, the line passed to the window, with no newline character at the end. The builtin variable "prompt" contains a string which VT displays in the input window. A VTC task can set this prompt using a statement like: prompt = "> "; The K_PROCESS editing function resets the prompt to an empty string. Remote connections ------------------ VTC tasks can open connections to remote hosts with the connect() primitive. VT associates three VTC-settable flags with each remote: a "busy" flag signalling that VT should not process any input from that remote, a "background" flag controlling whether VT should process input from the remote connection when it is not associated with a window, and a "raw" flag which determines how VT will process input from the remote host. To set these flags, use the set_busy(), set_back(), and set_raw() primitives. To read them, use the rmt_busy(), rmt_back(), and rmt_raw() primitives. A VT connection is said to be in raw mode when the raw flag is on, and in line mode when the raw flag is off. In line mode, VT breaks up incoming server data into lines before dispatching it. In raw mode, VT dispatches the text in blocks. In both modes, VT will dispatch a prompt if it sees a prompt terminator, which is a telnet IAC GA sequence or possibly a telnet IAC EOR sequence. VT supports the telnet protocol and the telnet options ECHO and EOR. Remote connections have two non-user-settable flags, "echo" and "eor". You can check the state of the echo and eor flags with rmt_echo() and rmt_eor(). The "echo" flag determines whether VT will display input in the input window, and defaults to on. If the server sends an IAC WILL ECHO while the echo flag is set, VT will reply with an IAC DO ECHO and clears the echo flag. While the flag is clear, if the server sends an IAC WONT ECHO, VT responds with an IAC DONT ECHO and sets the echo flag. VT will not display input in the input window while the echo flag is clear. The "eor" flag defaults to off. The remote server can turn it on with an IAC WILL EOR, which VT will respond to with an IAC DO EOR. When the eor flag is on, VT will accept an IAC EOR sequence as a prompt terminator. The remote server can turn the flag off with an IAC WONT EOR, which VT will respond to with an IAC DONT EOR. While the flag is off, or if the connection is in raw mode, VT ignores IAC EOR. VT interprets IAC GA as a prompt terminator regardless of the eor flag. In line mode, VT interprets a newline as a line terminator, and ignores carriage returns entirely. In raw mode, VT uses newlines only to determine the beginning of a prompt when it encounters a prompt terminator. The display() primitive associates a remote connection with a window and vice versa. You can only associate one remote connection with a window and vice versa. VTC tasks can intercept lines or blocks from a remote using the read() primitive, but they cannot intercept prompts. Remote connections have "promptread" and "netread" functions to handle prompts and unintercepted lines or blocks. Blocks (in raw mode) and lines (in line mode) are dispatched as follows: Resume task that called read(remote), if any, or Run remote's netread function, if it exists, or Write to the remote's window, if it exists, or Write to the active window with a "[background] " prefix Prompts are dispatched as follows: Run remote's promptread function, if it exists, or Write to the remote's window, if it exists, or Write to the active window with a "[background] " prefix Hooks ----- VT has two "hooks", or functions it calls automatically in certain situations. When VT redraws the screen automatically (that is, after returning from a SIGTSTP (^Z) signal or after the screen is resized), if there is a function redraw_hook(), VT will call that function rather than redraw the screen. The function can call edfunc(K_REDRAW) to redraw the screen normally. Second, whenever a remote connection is closed, whether through the disconnect() primitive or due to a read error or a foreign host, VT will call disconnect_hook() if it exists. Named constants --------------- There are several reserved words in VTC that are translated directly into integer constants. These numbers have some meaning to certain primitives in the compiler. The constants are: Name Value Description ---- ----- ----------- T_INT 0 Data types T_PPTR 1 T_BPTR 2 T_RMT 3 T_WIN 4 T_KEY 5 T_FILE 6 T_SPTR 7 T_APTR 8 T_FPTR 9 T_REG 10 T_ASSOC 11 T_PLIST 12 T_NULL 13 K_CUP 0 Editing functions K_CDOWN 1 K_CLEFT 2 K_CRIGHT 3 K_CHOME 4 K_CEND 5 K_CWLEFT 6 K_CWRIGHT 7 K_BSPC 8 K_BWORD 9 K_BHOME 10 K_DBUF 11 K_DCH 12 K_DWORD 13 K_DEND 14 K_REFRESH 15 K_REDRAW 16 K_MODE 17 K_PROCESS 18 SEEK_SET 0 For fseek(): beginning of file SEEK_CUR 1 current position in file SEEK_END 2 end of file HIGH 0 For getch() primitive LOW 1 INTR 2 EOF -1 For reading files NSUBEXP 10 Max. number of regexp sub-expressions Built-in objects (builtins) --------------------------- Most built-in objects act like variables whose values are tied to functions of the client. Their values can change automatically. Here is a complete list: Name Type Description ---- ---- ----------- rows INT Number of rows in the terminal cols INT Number of columns in the terminal active WIN Active window prompt SPTR Input window prompt kbuf SPTR Key buffer in input window kpos INT Offset of cursor in key buffer cur_win WIN Current window (see below) cur_rmt RMT Current remote (see below) argc INT Number of arguments to current function argv APTR Current function's argument array time INT Current time (seconds since Jan 1, 1980) wd SPTR Current working directory rnd INT A random integer from 0 to max int value version SPTR Version number errflag INT Error flag (1 or 0) errmsg SPTR Error message for last error NULL NULL Null value You can assign to the builtins active, prompt, kbuf, kpos, argc, argv, and wd. Assigning to argc and argv affects only the current function and is a convenience for functions that traverse their argument array. Assignments to argv also do not affect which arguments the parameter variables refer to. Assigning to other builtins has an immediate effect. Assigning to the wd builtin will sometimes fail, in which case the errflag and errmsg builtins are set as with primitives that can fail. Attempting to assign an object of the wrong type to a builtin, or assigning to a builtin that cannot be written to, results in an "Illegal builtin assignment" runtime error. This also occurs if you attempt to assign an array pointer to the argv builtin that does not point into the current function's parameter array. The & operator can be used to get a pointer to a builtin object (of type BPTR), just as with variables. The prompt, kbuf and wd builtins store strings, not pointers to strings, so that while "kbuf++" is legal, the first character of the keyboard buffer is lost, and a subsequent "kbuf--" will not allow you to recover it (it will instead clear the keyboard buffer). Also, any nonprintable characters will be stripped from a string you assign to the kbuf or prompt builtins. The prompt builtin applies to the input window only in text mode, and the prompt persists only until one line is entered, at which time it reverts to the empty string. Non-fatal errors ---------------- A number of primitives can fail with a non-fatal error if their arguments are invalid or some other condition occurs. These primitives return an error code under such conditions. The errflag builtin is nonzero if the last error-returning primitive failed. If errflag is nonzero, then errmsg contains a string with some information as to why the error occurred. errflag and errmsg are also set when the bobj assignment to the wd builtin fails, and when VT calls the disconnect_hook() primitive. In the latter case, errflag is 0 if the remote was disconnected through the disconnect() primitive, and 1 if it was disconnected because of a failed send or receive or a closed connection. Primitives that set the errflag and errmsg builtins are marked with a * in the primitive descriptions below and in prmtref.txt. Current window and remote ------------------------- The current window and current remote depend on how VT invokes a task. VT can invoke a task for one of the following reasons: a key binding, a command parser directive, an automatic call to redraw_hook(), an automatic call to disconnect_hook(), a keyboard line passed to a termread function, or a remote line or prompt passed to a netread or promptread function. For a key binding, a command parser directive, or an automatic call to redraw_hook(), VT defines cur_win as the active window and cur_rmt as the remote associated with cur_win. If the active window changes while the task executes or while the task is suspended, cur_win will change with it, so you must assign its value to a variable if you wish to retain the value across a primitive that runs another task or a primitive that causes the task to be suspended. In the case of a line passed to a window's termread function, VT defines cur_win to be the window in question, and cur_rmt to be the remote associated with that window. In the case of an automatic call to disconnect_hook() or a remote line or prompt passed to a remote's netread or promptread function, VT defines cur_rmt to be the remote in question, and cur_win to be the window associated with that remote. More systematically: cur_win cur_rmt ------- ------- Key binding active win_rmt(active) Parser active win_rmt(active) Redraw active win_rmt(active) Disconnect rmt_win(cur_rmt) Termread win_rmt(cur_win) Netread rmt_win(cur_rmt) Promptread rmt_win(cur_rmt) means that VT defines it when the task begins. Otherwise, evaluating cur_win or cur_rmt is equivalent to evaluating the expression in the table. Writing to a null window or remote ---------------------------------- In some situations, VT may attempt to write to a null window or remote. This occurs most often when either cur_win or cur_rmt is NULL as defined above. For instance, if a remote has no netread function, VT will write its output to the current window, which will be NULL if it has no associated window. Writing a line to a null remote is a null operation, but writing a line to a null window is not. VT will write the line to the active window, prefixing it with "[background]". This is not very informative, but fortunately it can be avoided easily. Broken pipes ------------ When a VTC task requests input from a window or a remote connection, VT suspends it while the regular activities of the client continue. If the window is closed or the remote is disconnected before input becomes available, VT will resume all tasks which asked for input, and the getch() or read() call which requested input will return NULL. getch() and read() calls without window or remote arguments can never return NULL, because they do not depend on a window which can be closed. Destroyed pipes --------------- If you resize the space which VT is running in such that it cannot fit as many windows on the screen as there were previously, VT has no opportunity to resume the tasks which were waiting for input on that window. In this case, VT will destroy those tasks altogether. Fortunately, this is rare. I/O scheduling -------------- If multiple tasks request input from a remote or window at the same time using read(), the requests are placed on the tail of a dequeue for that remote. The reread() primitive acts like the read() primitive but places requests at the head of the dequeue. VT processes requests starting at the head of the dequeue. A routine that uses a read() and pass() loop should call read() to get the first line and reread() to get subsequent lines. For example: func waitfor(rmt, match) [line] { for (line = read(rmt); line; line = reread(rmt)) { pass(rmt, line); if (!strcmp(line, match)) return; // Found it } abort(); /* Connection closed; abort */ } If several waitfor() functions are running, VT will plac them on the dequeue in FIFO order, as is appropriate, but after they pass() a line, VT replaces them on the dequeue in LIFO order using reread(). Otherwise, the dequeue would reverse order each time a line arrived. For windows, VT places getch(HIGH) and getch(LOW) calls onto separate queues, since they apply at different times. Both read() and getch() requests which depend on a particular window being active have lower priority than those which do not. Breaking -------- If the current task has gone into an infinite loop or is taking too long, you can use ^C B to break out of it. ^C B breaks all currently running tasks. This includes tasks that are in the middle of a primitive that runs another task, such as callv(), detach(), load_file(), parse(), or insert(), but not suspended tasks. Debugging --------- There is a minimal debugging utility for the VTC compiler. If you use ^C D to turn debugging on, the compiler will output a list of global variables and undefined functions used by each function and command that uses them. This is to help prevent typos, since VT ordinarily does not complain about such problems until runtime. Console ------- You can use the console to eliminate unwanted suspended tasks. Use ^C C to enter the console. The console is a simple menu-driven system for viewing I/O queues and timed events. VT performs none of its normal activities while the console is active. In the console, VT assigns a number to each window and remote by their ordering in the internal lists, starting at 0. Window ordering is top to bottom; remote ordering is by creation time. This is the only time that VT assigns a number to windows and remotes. The (S)how option lists suspended tasks attached to the various I/O functions or the timer. The (D)elete option aborts a selected suspended task. Screen resizing --------------- If an external windowing task resizes the window that VT is running in, VT will adapt by shrinking windows to take up roughly the same percentage of screen space as they did before. If the screen is too small to hold the previous number of windows, then VT will discard windows until there are few enough, and immediately terminate all tasks waiting for input from discarded windows. If the space VT runs in becomes smaller than 4x15, VT will abort. Primitives ---------- Following is a description of all of the primitives, grouped according to type. Preceding each group of primitives is a list of prototypes for those primitives giving the types of the return value of the primitive and the arguments it accepts. A type specification of ?? indicates that the argument is not restricted to any specific types. Square brackets delimit optional arguments to primitives. Elipses (...) usually indicate that you can give more arguments, as explained in the description. A * before the name of a primitive indicates that it sets the errflag and errmsg builtins. Arrays ------ These primitives deal with arrays: NULL acopy(APTR dest, APTR src, INT n) APTR/PLIST/NULL alloc(INT size, [ASSOC assoc]) APTR/SPTR base(APTR/SPTR/PLIST ptr) INT garbage() APTR/INT lookup(ASSOC/PLIST/WIN/RMT plist, SPTR name) ASSOC new_assoc() NULL acopy(APTR dest, APTR src, INT n) Copies array elements from to . If is 0 or less, no action is performed. If is an argument array and cannot be extended to accomodate elements, elements are copied until the end of the argument array is reached. APTR/PLIST/NULL alloc(INT size, [ASSOC assoc]) Allocates an array or plist. is the initial size of the array, and should be an estimate of the number of data elements the array will need. Since the array will stretch if necessary, however, the value of does not affect program correctness. If is specified, alloc() returns a plist using that association type; otherwise, it returns an array. APTR/SPTR base(APTR/SPTR/PLIST ptr) Pointers are stored internally as an array and an offset. The base() primitive returns the array. This allows you to avoid storing the base of an array as a separate variable, if you are certain that the pointer you began with pointed to the base of the array. You can get the offset of a pointer from the expression (ptr - base(ptr)). base() can also be used to get the base of an array associated with a plist. INT garbage() Calls the garbage collection routine. This frees up memory from groups of self-referential arrays that nothing is pointing to. garbage() returns the number of leaked arrays that were freed. Normally, this routine is only necessary if you are making regular use of self-referential data structures such as linked lists with backlinks. APTR/INT lookup(ASSOC/PLIST/WIN/RMT plist, SPTR name) Returns a pointer to the array element referenced by in . If a WIN or RMT is given for and the object of that window or remote is a plist, then that plist is used. See the section "Property lists and association types" for details. If an ASSOC is given for , then lookup() returns the offset of for that association type. ASSOC new_assoc() Returns a new association type, which can be used to generate property lists using alloc(). Once an association type is created, it is never destroyed, and thus should be kept track of. Environment ----------- These primitives have to do with the shell running VT: SPTR/NULL *getenv(SPTR envvar) INT system(SPTR cmd) SPTR/NULL getenv(SPTR envvar) Returns the value of in the environment, or NULL if it does not exist. INT system(SPTR cmd) Runs the shell command . VT is suspended while the command runs. There is no screen preparation for the running the shell command; you must prepare the screen yourself using the screen primitives. Files ----- These primitives handle I/O and related functions involving files in the file system: NULL *fclose(FILE file) INT feof(FILE file) SPTR file_name(FILE file) FILE/NULL find_file(SPTR name) INT fflush(FILE file) INT *fgetc(FILE file) INT *fmtime(SPTR filename) FILE/NULL *fopen(SPTR name, SPTR mode) INT *fputc(INT char, FILE file) SPTR/NULL *fread(FILE file) INT *fseek(FILE file, INT offset, INT base) INT *fsize(SPTR filename) INT *ftell(FILE file) INT *fwrite(FILE file, SPTR text) INT *load_file(SPTR filename) FILE/NULL *popen(SPTR cmd, SPTR mode) INT *unlink(SPTR filename) NULL fclose(FILE file) Closes . If was a process, VT waits for the process to finish before resuming execution. INT feof(FILE file) Returns nonzero if has been read to its end. SPTR file_name(FILE file) Returns the name of as given to fopen() or popen(). FILE/NULL find_file(SPTR name) Searches the list of currently opened files for a file with name . must match the filename given to fopen() or the command given to popen(). INT fflush(FILE file) Flushes output to the file if text has been buffered. INT fgetc(FILE file) Reads a character from . Returns EOF if nothing could be read. INT fmtime(SPTR filename) Returns the last modification time of the file with the name in the file system. Returns -1 if the file was not found. FILE or NULL fopen(SPTR name, SPTR mode) Opens a file with name . is "r" for reading, "w" for writing or "a" for appending, with a "+" added to indicate both reading and writing. fopen() returns NULL if the file could not be opened. INT fputc(INT char, FILE file) Writes to . Returns . Text is buffered by lines. SPTR/NULL fread(FILE file) Reads a line from . Returns T_NULL if nothing could be read. INT fseek(FILE file, INT offset, INT ptrname) Moves to a new position in a file. is a starting position, 0, 1 or 2 (SEEK_SET, SEEK_CUR, or SEEK_END) for the beginning, current position, or end of the file, respectively. is added to this position. fseek() returns non-zero on error. INT fsize(SPTR filename) Returns the size of the file with the name in the file system. Returns -1 if the file was not found. INT ftell(FILE file) Returns the current position in . INT fwrite(FILE file, SPTR text) Writes to . Returns the number of characters written. INT load_file(SPTR filename) Loads the file with the name in the file system into the parser. Returns -1 if the file cannot be opened and 0 otherwise. FILE/NULL popen(SPTR cmd, SPTR mode) Starts a process with the shell command . is "r" to read from the process or "w" to write to it. Two-way communication is not possible. popen() returns NULL if the process cannot be started. INT unlink(SPTR filename) Deletes the file with the name in the file system. Returns -1 if the file was not found, 0 otherwise. Functions --------- These primitives deal with functions: NULL abort() ?? callv(FPTR/PPTR func, []...) NULL detach(FPTR/PPTR func, args...) FPTR/NULL find_func(SPTR name) PPTR/NULL find_prmt(SPTR name) SPTR func_name(FPTR func) NULL abort() Terminates the currently-running task. ?? callv(FPTR/PPTR func, []...) Runs with a variable number of parameters. The arguments to callv() come in groups. A group's first argument must be an integer specifying the number of parameters to add to the count. If this integer is zero or more, then the parameters are taken from an array contained in the next argument to callv(); if it is negative, they are taken from the next arguments to callv(). This is best illustrated with an example: callv(.f, 10, array, -3, a, b, c); (group 1) ( group 2 ) This calls function f with 13 arguments, ten of which are taken from locations starting at , and the remaining three of which are a, b, and c. As an example, an echoln() function that takes a variable number of string arguments could be written as follows without callv(): func echoln() [w, i] { if (type(*argv) == T_WIN) { w = *argv++; argc++; } else w = cur_win; while (argc--) echo(w, *argv++); echo(w, "\n"); } The following function does the same thing more simply and more quickly: func echoln() { callv(.echo, argc, argv, -1, "\n"); } callv() returns the return value of . NULL detach(FPTR/PPTR func, args...) Runs detached. That is, if is suspended or exits due to a fatal error or abort(), the activities of the task that calls detach() will continue. The calling task will wait for to be suspended or to exit before resuming execution. FPTR/NULL find_func(SPTR name) Searches for a function with the name . Returns NULL if no such function exists. PPTR/NULL find_prmt(SPTR name) Searches for a primitive with the name . Returns NULL if no such primitive exists. SPTR func_name(FPTR func) Returns the name of . Key Bindings ------------ These primitive handle binding of key sequences to commands: KEY/NULL bind(SPTR sequence, INT/FPTR func) KEY/NULL find_key(SPTR sequence) FPTR/INT key_func(KEY key) SPTR key_seq(KEY key) NULL unbind(KEY key) KEY/NULL bind(SPTR sequence, INT/FPTR kfunc) Binds a key sequence to an editing function or VTC function. If is a blank string, bind_edit() returns NULL; otherwise, it returns a pointer to the key binding. can be specified with the named constants beginning with "K_" if it is an editing function. Binding a key to an integer that is not an editing function causes the key to do nothing. If matches the sequence of a key that has already been bound, that binding is replaced. KEY/NULL find_key(SPTR sequence) Searches for a key with sequence . Returns T_NULL if no such key exists. FPTR/INT key_func(KEY key) Returns the function of key, which is either a VTC function pointer or an editing function. You can use type() to determine what type of function was returned. SPTR key_seq(KEY key) Returns the key sequence of . NULL unbind(KEY key) Deletes the key binding . Key Buffer ---------- These primitives are related to the key buffer in the input window: NULL edfunc(INT kfunc) INT getch(INT type, [WIN win]) NULL insert(SPTR text) NULL edfunc(INT kfunc) Runs the editing function . The named constants K_* are of use in specifying . No action is taken if is out of range. INT getch(INT type, [WIN win]) Reads a character from the keyboard. If is specified, the task will only intercept a character when is the active window. If is deleted while the task is suspended, getch() returns NULL. Non-window-specific requests have higher priority than window-specific requests. determines whether the incoming character will be checked to see if it is part of an editing key before it is intercepted. For instance, a pager would probably only wish to accept a key if it is not an editing function. The named constants HIGH (0) and LOW (1) can be used to specify this type, with HIGH intercepting the key before checking for editing functions, and LOW intercepting it afterwards. HIGH requests have a higher priority than LOW requests, even if the LOW request would ordinarily have higher priority because it is non-window-specific and the HIGH request is window-specific. If a window argument is not specified, can be INTR (2). In this case, client operations are interrupted until a key is pressed. NULL insert(SPTR text) Mimicks being typed by the user. Miscellaneous ------------- These primitives perform miscellaneous functions: SPTR ctime(INT time) APTR find_var(SPTR name) ?? head(INT type) ?? next(?? node) NULL parse(SPTR str) ?? prev(?? node) NULL quit() NULL rndseed(INT seed) NULL sleep(INT seconds) ?? tail(INT type) INT type(?? data) SPTR ctime(INT time) Converts