SECTION 21 1 MISCELLANEOUS 21.1 Measuring Functions time[timex;timen;timetyp] is an nlambda function. It executes the computation timex, and prints out the number of conses and computation time. Garbage collection time is subtracted out. _TIME((LOAD (QUOTE PRETTY) (QUOTE PROP] FILE CREATED 7-MAY-71 12:47:14 GC: 8 582, 10291 FREE WORDS PRETTYFNS PRETTYVARS 3727 CONSES 10.655 SECONDS PRETTY If timen is greater than 1 (timen=NIL equivalent to timen=1), time executes timex timen number of times and prints out number of conses/timen, and computation time/timen. This is useful for more accurate measurement on small computations, e.g. _TIME((COPY (QUOTE (A B C))) 10) 30/10 = 3 CONSES .055/10 = .0055 SECONDS (A B C) ------------------------------------------------------------------------ 1 Some of the functions in this section are TENEX or implementation dependent. They may not be provided in other implementations of INTERLISP. 21.1 If timetype is 0, time measures and prints total real time as well as computation time, e.g. _TIME((LOAD (QUOTE PRETTY) (QUOTE PROP)) 1 0] FILE CREATED 7-MAY-71 12:47:14 GC: 8 582, 10291 FREE WORDS PRETTYFNS PRETTYVARS 3727 CONSES 11.193 SECONDS 27.378 SECONDS, REAL TIME PRETTY If timetyp = 3, time measures and prints garbage collection time as well as computation time, e.g. _TIME((LOAD (QUOTE PRETTY) (QUOTE PROP)) 1 3] FILE CREATED 7-MAY-71 12:47:14 GC: 8 582, 1091 FREE WORDS PRETTYFNS PRETTYVARS 3727 CONSES 10.597 SECONDS 1.487 SECONDS, GARBAGE COLLECTION TIME PRETTY Another option is timetype=T, in which case time measures and prints the number of pagefaults. The value of time is the value of the last evaluation of timex. 2 date[] obtains date and time, returning it as single string in format "dd-mm-yy hh:mm:ss", where dd is day, mm is month, yy year, hh hours, mm minutes, ss seconds, e.g., "14-MAY-71 14:26:08". idate[d] d is a time and date string. Value of idate is d converted to a number such that if d1 is before (earlier than) d2, then idate[d1] < idate[d2]. ------------------------------------------------------------------------ 2 In INTERLISP-10, date will accept a value for ac3 as an argument. ac3 can be used to specify other formats, e.g., day of week, time zone, etc., as described in TENEX JSYS manual. 21.2 clock[n] for n=0 current value of the time of day clock i.e., number of milliseconds since last system start up. for n=1 value of the time of day clock when the user started up this INTERLISP, i.e., difference between clock[0] and clock[1] is number of milliseconds (real time) since this INTERLISP was started. for n=2 number of milliseconds of compute time since user started up this INTERLISP (garbage collection time is subtracted off). for n=3 number of milliseconds of compute time spent in garbage collections (all 3 types). dismiss[n] In INTERLISP-10, dismisses program for n milliseconds, during which time program is in a state similar to an I/O wait, i.e., it uses no CPU time. Can be aborted by control-D, control- E, or control-B. conscount[n] conscount[] returns the number of conses since INTERLISP started up. If n is not NIL, resets conscount to n. boxcount[type;n] In INTERLISP-10, number of boxing operations (see Section 13) since INTERLISP started up. If type=NIL, returns number of large integer boxes; type=FLOATING, returns number of floating 4 boxes. If n is not NIL, resets the corresponding counter to n. gctrp[] number of conses to next GC: 8, i.e., number of list words not in use. Note that an intervening GC of another type could collect as well as allocate additional list words. See Section 3. ------------------------------------------------------------------------ 3 In INTERLISP-10, this number is directly accessible via the COREVAL GCTIM. 4 In INTERLISP-10, these counters are directly accessible via the COREVALs IBOXCN and FBOXCN. 21.3 gctrp[n] can be used to cause an interrupt when value of gctrp[]=n, see Section 10. pagefaults[] In INTERLISP-10, number of page faults since INTERLISP started up. 5 logout[] returns control to operating system. In INTERLISP-10, a subsequent CONTINUE command will enter the INTERLISP-10 program, return NIL as the value of the call to logout, and continue the computation exactly as if nothing had happened, i.e., logout is a programmable control-C. As with control-C, a REENTER command following a logout will reenter INTERLISP-10 at the top level. logout[] will not affect the state of any open files. 6 21.2 Breakdown Time gives analyses by computation. Breakdown is available to analyze the breakdown of computation time (or any other measureable quantity) function by function. The user calls breakdown giving it a list of functions of interest. These functions are modified so that they keep track of the "charge" assessed to them. The function results gives the analysis of the statistic requested as well as the number of calls to each function. Sample output is shown below. _BREAKDOWN(SUPERPRINT SUBPRINT COMMENT1) (SUPERPRINT SUBPRINT COMMENT1) _PRETTYDEF((SUPERPRINT) FOO) FOO.;3 _RESULTS() FUNCTIONS TIME # CALLS PER CALL % SUPERPRINT 8.261 365 0.023 20 SUBPRINT 31.910 141 0.226 76 COMMENT1 1.612 8 0.201 4 TOTAL 41.783 514 0.081 NIL The procedure used for measuring is such that if one function calls ------------------------------------------------------------------------ 5 In INTERLISP-10, if INTERLISP was started as a subsidiary fork (see subsys, page 21.15), control is returned to the higher fork. 6 breakdown was written by W. Teitelman. 21.4 other and both are 'broken down', then the time (or whatever quantity is being measured) spent in the inner function is not charged to the outer 7 function as well. To remove functions from those being monitored, simply unbreak the functions, thereby restoring them to their original state. To add functions, call breakdown on the new functions. This will not reset the counters for any functions not on the new list. However breakdown[] can be used for zeroing the counters of all functions being monitored. To use breakdown for some other statistic, before calling breakdown, set the variable brkdwntype to the quantity of interest, e.g., TIME, CONSES, etc. Whenever breakdown is called with brkdwntype not NIL, breakdown performs the necessary changes to its internal state to conform to the new analysis. In particular, if this is the first time an analysis is being run with this statistic, the compiler may be called to compile the 8 measuring function. When breakdown is through initializing, it sets brkdwntype back to NIL. Subsequent calls to breakdown will measure the new statistic until brkdwntype is again set and a new breakdown performed. Sample output is shown below: _SET(BRKDWNTYPE CONSES) CONSES _BREAKDOWN(MATCH CONSTRUCT) (MATCH CONSTRUCT) _FLIP((A B C D E F G H C Z) (.. $1 .. #2 ..) (.. #3 ..)) (A B D E F G H Z) _RESULTS() FUNCTIONS CONSES # CALLS PER CALL % MATCH 32 1 32.000 41 CONSTRUCT 47 1 47.000 59 TOTAL 79 2 39.500 NIL The value of brkdwntype is used to search the list brkdwntypes for the information necessary to analyze this statistic. The entry on brkdwntypes corresponding to brkdwntype should be of the form (type form function), where form computes the statistic, and function (optional) converts the value of form to some more interesting quantity, e.g. ------------------------------------------------------------------------ 7 breakdown will not give accurate results if a function being measured is not returned from normally, e.g., a lower retfrom (or error) bypasses it. In this case, all of the time (or whatever quantity is being measured) between the time that function is entered and the time the next function being measured is entered will be charged to the first function. 8 The measuring functions for TIME and CONSES have already been compiled. 21.5 9 (TIME (CLOCK 2) (LAMBDA (X) (FQUOTIENT X 1000))) measures computation time and reports the result in seconds instead of milliseconds. If brkdwntype is not defined on brkdwntypes, an error is generated. brkdwntypes currently contains entries for TIME, CONSES, PAGEFAULTS, BOXES, and FBOXES. More Accurate Measurement Occasionally, a function being analysed is sufficiently fast that the overhead involved in measuring it obscures the actual time spent in the function. If the user were using time, he would specify a value for timen greater than 1 to give greater accuracy. A similar option is available for breakdown. The user can specify that a function(s) be executed a multiple number of times for each measurement, and the average value reported, by including a number in the list of functions given to breakdown, e.g., BREAKDOWN(EDITCOM EDIT4F 10 EDIT4E EQP) means normal breakdown for editcom and edit4f but executes (the body of) edit4e and eqp 10 times each time they are called. Of course, the functions so measured must not cause any harmful side effects, since they are executed more than once for each call. The printout from results will look the same as though each function were run only once, except that the measurement will be more accurate. 10 21.3 Edita Edita is an editor for arrays. However, its most frequent application is in editing compiled functions (which are also arrays in INTERLISP- 10), and a great deal of effort in implementing edita, and most of its special features, are in this area. For example, edita knows the format and conventions of INTERLISP-10 compiled code, and so, in addition to 11 decoding instructions a la DDT, edita can fill in the appropriate COREVALS, symbolic names for index registers, references to literals, linked function calls, etc. The following output shows a sequence of instructions in a compiled function first as they would be printed by DDT, and second by edita. ------------------------------------------------------------------------ 9 For more accurate measurement, the form for TIME in INTERLISP-10 is not (CLOCK 2) but (ASSEMBLE NIL (JSYS 206) (SUB 1 , GCTIM)). 10 edita was written by W. Teitelman, and modified by D. C. Lewis. That portion of edita relating to compiled code may or may not be available in implementations of INTERLISP other than INTERLISP-10. 11 DDT is one of the oldest debugging systems still around. For users unfamiliar with it, let us simply say that edita was patterned after it because so many people are familiar with it. 21.6 466716/ PUSH 16,LISP&KNIL 3/ PUSH PP,KNIL 466717/ PUSH 16,LISP&KNIL 4/ PUSH PP,KNIL 466720/ HRRZ 1,-12(16) 5/ HRRZ 1,-10(PP) 466721/ CAME 1,LISP&KNIL 6/ CAME 1,KNIL 12 466722/ JRST 466724 7/ JRST 9 466723/ HRRZ 1,@467575 8/ HRRZ 1,@'BRKFILE 466724/ PUSH 16,1 9/ PUSH PP,1 466725/ LISP&IOFIL,,467576 10/ PBIND 'BRKZ 466726/ -3,,-3 11/ -524291 466727/ HRRZ 1,-14(16) 12/ HRRZ 1,-12(PP) 466730/ CAMN 1,467601 13/ CAMN 1,'OK 466731/ JRST 466734 14/ JRST 17 466732/ CAME 1,467602 15/ CAME 1,'STOP 466733/ JRST 466740 16/ JRST 21 466734/ PUSH 16,467603 17/ PUSH PP,'BREAK1 466735/ PUSH 16,467604 18/ PUSH PP,'(ERROR!) 466736/ LISP&FILEN,,467605 19/ CCALL 2,'RETEVAL 466737/ JRST 467561 20/ JRST 422 466740/ CAME 1,467606 21/ CAME 1,'GO 466741/ JRST 466754 22/ JRST 33 466742/ HRRZ 1,@-12(16) 23/ HRRZ 1,@-10(PP) 466743/ PUSH 16,1 24/ PUSH PP,1 Therefore, rather than presenting edita as an array editor with some extensions for editing compiled code, we prefer to consider it as a facility for editing compiled code, and point out that it can also be used for editing arbitrary arrays. Overview To the user, edita looks very much like DDT with INTERLISP-10 extensions. It is a function of one argument, the name of the function 13 to be edited. Individual registers or cells in the function may be 14 examined by typing their address followed by a slash, e.g. 6/ HRRZ 1,-10(PP) ------------------------------------------------------------------------ 12 Note that edita prints the addresses of cells contained in the function relative to the origin of the function. 13 An optional second argument can be a list of commands for edita. These are then executed exactly as though they had come from the teletype. 14 Underlined characters were typed by the user. edita uses its own read program, so that it is unnecessary to type a space before the slash or to type a carriage return after the slash. 21.7 15 The slash is really a command to edita to open the indicated register. Only one register at a time can be open, and only open registers can be changed. To change the contents of a register, the user first opens it, types the new contents, and then closes the register with a 16 carriage-return, e.g. 7/ CAME 1,'^ CAMN1,'^C If the user closes a register without specifying the new contents, the contents are left unchanged. Similarly, if an error occurs or the user types control-E, the open register, if any, is closed without being changed. Input Protocol Edita processes all inputs not recognized as commands in the same way. If the input is the name of an instruction (i.e., an atom with a numeric OPD property), the corresponding number is added to the input value 17 being assembled, and a flag is set which specifies that the input context is that of an instruction. The general form of a machine instruction is (opcode ac , @ address (index)) as described in Section 18. Therefore, in instruction context, edita evaluates all atoms (if the atom has a COREVAL property, the value 18 of the COREVAL is used), and then if the atom corresponds to an ac, shifts it left 23 bits and adds it to the input value, otherwise adds it directly to the input value, but performs the arithmetic in the low 18 19 bits. Lists are interpreted as specifying index registers, and the ------------------------------------------------------------------------ 15 edita also converts absolute addresses of cells within the function to relative address on input. Thus, if the definition of foo begins at 85660, typing 6/ is exactly the same as typing 85666/. 16 Since carriage-return has a special meaning, edita indicates the balancing of parentheses by typing a space. 17 The input value is initially 0. 18 i.e., if a "," has not been seen, and the value of the atom is less than 16, and the low 18 bits of the input value are all zero. 19 If the absolute value of the atom is greater than 1000000Q, full word arithmetic is used. For example, the indirect bit is handled by simply binding @ to 20000000Q. 21.8 value of car of the list (again COREVALs are permitted) is shifted left 18 bits. Examples: PUSH PP, KNIL HRRZ 1,-10(PP) CAME 1, 'GO 20 JRST 33 ORG The user can also specify the address of a literal via the ' command, see page 21.11. For example, if the literal " UNBROKEN" is in cell 85672, HRRZ 1,'" UNBROKEN" is equivalent to HRRZ 1, 85672. When the input context is not that of an instruction, i.e., no OPD has been seen, all inputs are evaluated (the value of an atom with a COREVAL property is the COREVAL.) Then numeric values are simply added to the 21 previous input value; non-numeric values become the input value. The only exception to the entire procedure occurs when a register is open that is in the pointer region of the function, i.e., literal table. In this case, atomic inputs are not evaluated. For example, the user can change the literal FOO to FIE by simply opening that register and then typing FIE followed by carriage-return, e.g. 'FOO/ FOO FIEC Note that this is equivalent to 'FOO/ FOO (QUOTE FIE)C Edita Commands and Variables C (carriage-return) If a register is open and an input was typed, 22 store the input in the register and close it. If a register is open and nothing was typed, close the register without changing it. If a register is not open and input was typed, type its value. ------------------------------------------------------------------------ 20 edita cannot in general know whether an address field in an instruction that is typed in is relative or absolute. Therefore, the user must add ORG, the origin of the function, to the address field himself. Note that edita would print this instruction, JRST 53 ORG, as JRST 53. 21 Presumably there is only one input in this case. 22 If the register is in the unboxed region of the function, the unboxed value is stored in the register. 21.9 ORG Has the value of the address of the first instruction in the function. i.e., loc of getd of the function. / Opens the register specified by the low 18 bits of the quantity to the left of the /, and types its contents. If nothing has been typed, it uses the last thing typed by edita, e.g., 35/ JRST 53 / CAME 1,'RETURN / RETURN If a register was open, / closes it without changing its contents. After a / command, edita returns to that state of no input having been typed. tab (control-I) Same as carriage-return, followed by the address of the quantity to the left of the tab, e.g., 35/ JRST 53 tab 53/ CAME 1,'RETURN Note that if a register was open and input was typed, tab will change the open register before closing it, e.g., 35/ JRST 53 JRST54 tab 54/ JRST 70 C 35/ JRST 54 . (period) has the value of the address of the current (last) register examined. line-feed same as carriage-return followed by (ADD1 .)/ i.e. closes any open register and opens the next register. ^ same as carriage-return followed by (SUB1 .)/ $Q (alt-modeQ) has as its value the last quantity typed by edita e.g. 35/ JRST 53 $Q1C ./ JRST 54 LITS has as value the (relative) address of the first literal. BOXED same as LITS 21.10 $ (dollar) has as value the relative address of the last literal in the function. = Sets radix to -8 and types the quantity to the left of the = sign, i.e., if anything has been typed, types the input value, otherwise, types $Q, e.g. 35/ JRST 54 =254000241541Q JRST54=254000000066Q Following =, radix is restored and edita returns to the no input state. OK leave edita ? return to "no input" state. ? is a "weak" control-E, i.e., it negates any input typed, but does not close any registers. 23 address1, address2/ prints the contents of registers address1 through address2. . is set to address2 after the completion. 'x corresponds to the ' in LAP. The next expression is read, and if it is a small number, the appropriate offset is added to it. Otherwise, the literal table is searched for x, and the value of 'x is the (absolute) address of that cell. An error is generated if the literal is not found, i.e., ' cannot be used to create literals. :atom defines atom to an address (1) the value of $Q if a register is open, (2) the input if any input was typed, otherwise 24 (3) the value of ".". For example: ------------------------------------------------------------------------ 23 output goes to file, initially set to T. The user can also set file (while in edita) to the name of a disc file to redirect the output. (The user is responsible for opening and closing file.) Note that file only affects output for the address1, address2/ command. 24 Only the low 18 bits are used and converted to a relative address whenever possible. 21.11 35/ JRST 54 :FOOC :FIEC FIE/ JRST FOO .=35 Edita keeps its symbol tables on two free variables, usersyms and symlst. Usersyms is a list of elements of the form (name . value) and is used for encoding input, i.e., all variables on usersyms are bound to their corresponding values during evaluation of any expression inside edita. Symlst is a list of elements of the form (value . name) and is used for decoding addresses. Usersyms is initially NIL, while symlst is set to a list of all the corevals. Since the : command adds the appropriate information to both these two lists, new definitions will remain in effect even if the user exits from edita and then reenters it later. Note that the user can effectively define symbols without using the : command by appropriately binding usersyms and/or symlst before calling edita. Also, he can thus use different symbol tables for different applications. $W (alt-modeW) search command. Searching consists of comparing the object of the search with the contents of each register, and printing those that match, e.g., HRRZ@$WC 8/ HRRZ 1,@'BRKFILE 23/ HRRZ 1,@-10(PP) 28/ HRRZ 1,@-12(PP) The $W command can be used to search either the unboxed portion of a function, i.e., instructions, or the pointer region, i.e., literals, depending on whether or not the object of the search is a number. If any input was typed before the $W, it will be the object of the search, 25 otherwise the next expression is read and used as the object. The user can specify a starting point for the search by typing an address followed by a "," before calling $W, e.g., 1, JRST $W. If no starting point is specified, the search will begin at 0 if the object is a 26 number, otherwise at LITS, the address of the first literal. After the search is completed, "." is set to the address of the last register that matched. ------------------------------------------------------------------------ 25 Note that inputs typed before the $W will have been processed according to the input protocol, i.e., evaluated; inputs typed after the $W will not. Therefore, the latter form is usually used to specify searching the literals, e.g., $W FOO is equivalent to (QUOTE FOO) $W. 26 Thus the only way the user can search the pointer region for a number is to specify the starting point via ",". 21.12 If the search is operating in the unboxed portion of the function, only those fields (i.e., instruction, ac, indirect, index, and address) of 27 the object that contain one bits are compared. For example, HRRZ @ $W will find all instances of HRRZ indirect, regardless of ac, index, and address fields. Similarly, 'PRINT $W will find all instructions that 28 reference the literal PRINT. If the search is operating in the pointer region, a "match" is as defined in the editor. For example, $W (&) will find all registers that contain a list consisting of a single expression. $C (alt-modeC) like $W except only prints the first match, then prints the number of matches when the search finishes. Editing Arrays Edita is called to edit a function by giving it the name of the function. Edita can also be called to edit an array by giving it the 29 array as its first argument, in which case the following differences are to be noted: 1. decoding - The contents of registers in the unboxed region are boxed and printed as numbers, i.e., they are never interpreted as instructions, as when editing a function. 2. addressing convention - Whereas 0 corresponds to the first instruction of a function, the first element of an array by convention is element number 1. 3. input protocols - If a register is open, lists are evaluated, atoms are not evaluated (except for $Q which is always evaluated). If no register is open, all inputs are evaluated, and if the value is a number, it is added to the "input value". ------------------------------------------------------------------------ 27 Alternately, the user can specify his own mask by setting the variable mask (while in edita), to the appropriate bit pattern. 28 The user may need to establish instruction context for input without giving a specific instruction. For example, suppose the user wants to find all instructions with ac=1 and index=PP. In this case, the user can give & as a pseudo-instruction, e.g., type & 1, (PP). 29 the array itself, not a variable whose value is an array, e.g., (EDITA FOO), not EDITA(FOO). 21.13 4. left half - If the left half of an element in the pointer region of an array is not all 0's or NIL, it is printed followed by a ;, e.g. 10/ (A B) ; T Similarly, if a register is closed, either its left half, right half, or both halves can be changed, depending on the presence or absence, and position of the ; e.g. 10/ (A B) ; T B;C changes left ./ B ; T NILC changes right ./ B ; NIL A;CC changes both ./ A ; C If ; is used in the unboxed portion of an array, an error will be generated. The $W command will look at both halves of elements in the pointer region, and match if either half matches. Note that $W A ; B is not allowed. This ends the section on edita. 21.4 Interfork Communication in INTERLISP-10 The functions described below permit two forks (one or both of them INTERLISP-10) to have a common area of address space for communication by providing a means of assigning a block of storage guaranteed not to move during garbage collections. getblk[n] Creates a block n pages in size (512 words per page). Value is the address of the first word in the block, which is a multiple of 512 since the block will always begin at a page boundary. If not enough pages are available, generates the error ILLEGAL OR IMPOSSIBLE BLOCK. Note: the block can be used for storing unboxed numbers only. To store a number in the block, the following function could be used: [SETBLOCK (LAMBDA (START N X) (CLOSER (IPLUS (LOC START) N) X] Some boxing and unboxing can be avoided by making this function compile open via a substitution macro. 21.14 Note: getblk should be used sparingly since several unmovable regions of memory can make it difficult or impossible for the garbage collector to find a contiguous region large enough for expanding array space. relblk[address;n] releases a block of storage beginning at address and extending for n pages. Causes an error ILLEGAL OR IMPOSSIBLE BLOCK if any of the range is not a block. Value is address. 30 21.5 Subsys This section describes a function, subsys, which permits the user to run a TENEX subsystem, such as SNDMSG, SRCCOM, TECO, or even another INTERLISP, from inside of an INTERLISP without destroying the latter. In particular, SUBSYS(EXEC) will start up a lower exec, which will print the TENEX herald, followed by @. The user can then do anything at this exec level that he can at the top level, without affecting his superior INTERLISP. For example, he can start another INTERLISP, perform a sysin, run for a while, type a control-C returning him to the lower exec, RESET, do a SNDMSG, etc. The user exits from the lower exec via the command QUIT, which will return control to subsys in the higher INTERLISP. Thus with subsys, the user need not perform a sysout to save the state of his INTERLISP in order to use a TENEX capability which would otherwise clobber the core image. Similarly, subsys provides a way of checking out a sysout file in a fresh INTERLISP without having to commandeer another teletype or detach a job. While subsys can be used to run any TENEX subsystem directly, without going through an intervening exec, this procedure is not recommended. The problem is that control-C always returns control to the next highest exec. Thus if the user is running an INTERLISP in which he performs SUBSYS(LISP), and then types control-C to the lower INTERLISP, control will be returned to the exec above the first INTERLISP. The natural 31 REENTER command would then clear the lower INTERLISP, but any files opened by it would remain open (until the next @RESET). If the user elects to call a subsystem directly, he must therefore know how it is 32 normally exited and always exit from it that way. ------------------------------------------------------------------------ 30 subsys was written by J.W. Goodwin. It is TENEX dependent and may not be available in implementations of INTERLISP other than INTERLISP-10. 31 A CONTINUE command however will return to the subordinate program, i.e., control-C followed by CONTINUE is safe at any level. 32 INTERLISP is exited via the function logout, TECO via the command ;H, SNDMSG via control-Z, and EXEC via QUIT. 21.15 Starting a lower exec does not have this disadvantage, since it can only be exited via QUIT, i.e., the lower exec is effectively "errorset protected" against control-C. subsys[file/fork;incomfile;outcomfile;entrypointflg] If file/fork=EXEC, starts up a lower exec, otherwise runs system, e.g. subsys[SNDMSG], subsys[TECO] etc. subsys[] is same as subsys[EXEC]. Control-C always returns control to next higher exec. Note that more than one INTERLISP can be stacked, but there is no backtrace to help you figure out where you are. incomfile and outcomfile provide a way of specifying files for input and output. incomfile can also be a string, in which case a temporary file is created, and the string printed on it. entrypointflg may be START, REENTER, or CONTINUE. NIL is equivalent to START, except when file/fork is a handle (see below) in which case NIL is equivalent to CONTINUE. The value of subsys is a large integer which is a handle to the lower fork. The lower fork is not reset unless the user specifically does so 33 using kfork, described below. If subsys is given as its first argument 34 the value of a previous call to subsys, , it continues the subsystem run by that call. For example, the user can do (SETQ SOURCES (SUBSYS TECO)), load up the TECO with a big source file, massage the file, leave TECO with ;H, run INTERLISP for awhile (possibly including other calls to subsys) and then perform (SUBSYS SOURCES) to return to TECO, where he will find his file loaded and even the TECO pointer position preserved. Note that if the user starts a lower EXEC, in which he runs an INTERLISP, control-C's from the INTERLISP, then QUIT from the EXEC, if he subsequently continues this EXEC with subsys, he can reenter or continue the INTERLISP. ------------------------------------------------------------------------ 33 The fork is also reset when the handle is no longer accessible, i.e., when nothing in the INTERLISP system points to it. Note that the fork is accessible while the handle remains on the history list. 34 Must be the exact same large number, i.e., eq. Note that if the user neglects to set a variable to the value of a call to subsys, (and has performed an intervening call so that subsys[T] will not work), he can still continue this subsystem by obtaining the value of the call to subsys for the history list using the function valueof, described in Section 22. 21.16 Note also that calls to subsys can be stacked. For example, using subsys, the user can run a lower INTERLISP, and within that INTERLISP, yet another, etc., and ascend the chain of INTERLISPs using logout, and then descend back down again using subsys. For convenience, subsys[T] continues the last subsystem run. SNDMSG, LISP, TECO, and EXEC, are all LISPXMACROS which perform the corresponding calls to subsys. CONTIN is a LISPXMACRO which performs subsys[T], thereby continuing the last subsys. kfork[fork] accepts a value from subsys and kills it (RESET in TENEX terminology). If subsys[fork] is subsequently performed, an error is generated. kfork[T] kills all outstanding forks (from this INTERLISP). 35 21.6 Miscellaneous Tenex Functions in INTERLISP-10 fildir[filegroup] filegroup is a TENEX file group descriptor, i.e., it can contain stars. fildir returns a list of the files which match filegroup, a la the TENEX DIRECTORY command, e.g., (FILDIR (QUOTE *.COM;0)). loadav[] returns TENEX current load average as a floating point number (this number is the first of the three printed by the TENEX SYSTAT command). erstr[ern] ern is an error number from a JSYS fail return. ern=NIL means most recent error. erstr returns the TENEX error diagnostic as a string (from ERROR.MNEMONICS). jsys[n;ac1;ac2;ac3;resultac] loads (unboxed) values of ac1, ac2, and ac3 into appropriate accumulaters, and executes TENEX JSYS number N. If ac1, ac2, or ac3=NIL, 0 is used. Value of jsys is the (boxed) contents of the accumulator specified by resultac, i.e., 1 means ac1, 2 means ac2, and 3 means ac3, with NIL equivalent to 1. username[a] If a=NIL, returns login directory name; if a=T, ------------------------------------------------------------------------ 35 All of the functions in this section, except for tenex, were written by J.W. Goodwin. 21.17 returns connected directory name; if a is a number, username returns the user name corresponding to that user number. In all cases, the value is a string. usernumber[a] If a=NIL, returns login user number; if a=T, returns connected user number; if a is a literal atom or string, usernumber returns the number of the corresponding user, or NIL if no such user exists. Note: greeting (see Section 22) sets the variable username to the login user name, and firstname to the name used in the greeting. tenex[str;fileflg] Starts up a lower exec (without a message) using subsys, and then if fileflg=NIL unreads str, followed by "QUIT" (using bksysbuf, described in Section 14). The value of tenex is T if all of str is actually processed/read by the lower exec, NIL if the user control-C's and manually QUIT's back to LISP. If fileflg=T, tenex passes the string as the second argument to subsys, instead of unreading it. This has the advantage that str can be of any length, and also that typeahead will not interfere with the call to the lower exec. The disadvantage is that tenex cannot tell whether the commands to the lower exec terminated successfully, or were aborted. Thus, if fileflg=T, the value of tenex is always T. For example, listfiles (Section 14) is implemented using tenex, with fileflg=NIL, so listfiles can tell if listings actually were completed. The lispxmacro SY, which does a SYSTAT, is implemented as TENEX["SY";T], so that the user can type ahead. Manipulating Tenex File Directories from INTERLISP-10 The following function allows the user to conveniently specify and/or program a variety of directory operations: 36 directory[filegroup;commands;defaultext;defaultvers] filegroup is either [1] NIL (which is equivalent to *.*;*); or [2] an atom which can contain $'s or *'s (equivalent) which match any number of ------------------------------------------------------------------------ 36 directory was written by L.M. Masinter. 21.18 37 characters or ?'s which match a single character, or else [3] filegroup is a list of the form (filegroup + filegroup), (filegroup - 38 filegroup), or (filegroup * filegroup), e.g., (T$ + $L) will match with any file beginning with T or ending in L, (T$ - *.COM) matches all files that begin with T and are not .COM files. For each file that matches, each command in commands is executed with the following interpretation: @ fn apply fn to the JFN for each file; if fn returns NIL, abort command processing for this file. P print file name. PAUSE wait for user to type any char (good for display if you want to ponder). PROMPT mess prompts with mess; if user responds with No, abort command processing for this file. SIZE print file size. TRIMTO n deletes all but n versions of file (n > 0) OUT file directs output to file. COLLECT adds file on value list. In this case the value of directory will be the list of files collected. DATE prints date the file was last written. DELETE deletes file. The value of directory is NIL if no COLLECT command is specified, otherwise the list of files "collected". directory uses dircommands to correct spelling, which also provides a way of defining abbreviations and synonyms (see Section 17 on spelling lists). Currently the following abbreviations are recognized: TI same as DATE ------------------------------------------------------------------------ 37 not necessarily trailing characters, e.g., F$1 matches FOO1 and FIE1. 38 OR can be used for +, and AND for *. 21.19 DEL same as DELETE DEL? same as PROMPT "delete?" DELETE COLLECT? same as PROMPT "?" COLLECT There is also a lispxmacro DIR which calls the function directory: DIR group commands calls the function directory with (P . commands) as the command list and * and * as the default extension and default version respectively. For example, to DELVER only those files which you ok, do DIR group PROMPT "?" TRIMTO 1. 21.7 Printing Reentrant and Circular List Structures A reentrant list structure is one that contains more than one occurrence of the same (eq) structure. For example, tconc (Section 6) makes uses of reentrant list structure so that it does not have to search for the end of the list each time it is called. Thus, if x is a list of 3 elements, (A B C), being constructed by tconc, the reentrant list structure used by tconc for this purpose is: Figure 21-1 21.20 This structure would be printed by print as ((A B C) C). Note that print would produce the same output for the non-reentrant structure: Figure 21-2 In other words, print does not indicate the fact that portions of the structure in Figure 21-1 are identical. Similarly, if print is applied to a circular list structure (a special type of reentrant structure) it will never terminate. For example, if print is called on the structure: Figure 21-3 it will print an endless sequence of left parentheses, and if applied to: Figure 21-4 will print a left parenthesis followed by an endless sequence of A's. The function circlprint described below produces output that will exactly describe the structure of any circular or reentrant list 21.21 39 structure. This output may be in either single or double-line formats. Below are a few examples of the expressions that circlprint would produce to describe the structures discussed above. expression in Figure 21-1: single-line: ((A B *1* C) {1}) double-line: ((A B C) . {1}) 1 expression in Figure 21-3: single-line: (*1* {1}) double-line: ({1}) 1 expression in Figure 21-4: single-line: (*1* A . {1}) double-line: (A . {1}) 1 The more complex structure: Figure 21-5 is printed as follows: single-line: (*2* (*1* {1} *3* {2} A *4* B . {3}) . {4}) double-line: ( ({1} {2} A B . {3}) . {4}) 2 1 3 4 In both formats, the reentrant nodes in the list structure are labeled by numbers. (A reentrant node is one that has two or more pointers ------------------------------------------------------------------------ 39 Circlprint and circlmaker were written by P. C. Jackson. 21.22 coming into it.) In the single-line format, the label is printed between asterisks at the beginning of the node (list or tail) that it identifies. In the double-line format, the label is printed below the beginning of the node it identifies. An occurrence of a reentrant node that has already been identified is indicated by printing its label in brackets. circlprint[list;printflg;rlknt] prints an expression describing list. If printflg=NIL, double-line format is used, otherwise single-line format. circlprint first calls circlmark[list;rlknt], and then calls either rlprin1[list] or rlprin2[list], depending on the value of printflg (T or NIL, respectively). Finally, rlrestore[list] is called, which restores list to its unmarked state. Value is list. circlmark[list;rlknt] marks each reentrant node in list with a unique number, starting at rlknt+1 (or 1, if rlknt is NIL). Value is (new) rlknt. Marking list physically alters it. However, the marking is performed undoably. In addition, list can always be restored by specifically calling rlrestore. rlprin1[list] prints an expression describing list in the single-line format. Does not restore list to its uncirclmarked state. list must previously have been circlmarked or an error is generated. rlprin2[list] same as rlprin1, except that the expression describing list is printed in the double-line format. rlrestore[list] physically restores list to its original, unmarked state. Note that the user can mark and print several structures which together share common substructures, e.g., several property lists, by making several calls to circlmark, followed by calls to rlprin1 or rlprin2, and finally to rlrestore. circlmaker[list] list may contain labels and references following the convention used by circlprint for printing reentrant structures in single line format, e.g., (*1* . {1}). circlmaker performs the necessary rplaca's and rplacd's to make list correspond to the indicated structure. Value is (altered) list. 21.23 circlmaker1[list] Does the work for circlmaker. Uses free variables labelst and reflst. labelst is a list of dotted pairs of labels and corresponding nodes. reflst is a list of nodes containing references to labels not yet seen. Circlmaker operates by initializing labelst and reflst to NIL, and then calling circlmaker1. It generates an error if reflst is not NIL when circlmaker1 returns. The user can call circlmaker1 directly to "connect up" several structures that share common substructures, e.g., several property lists. 21.8 Dumping Unusual Data Structures The circlprint package is designed primarily for displaying complex list structures, i.e. printing them so that the user can look at them (although circlmaker can be used in conjunction with read for dumping 40 and reloading re-entrant list structures). Hprint is a package for printing and reading back in more general data structures that cannot normally be dumped and loaded easily, e.g., (possibly re-entrant or circular) structures containing user datatypes, arrays, hash tables, as 41 well as list structures. Hprint will correctly print and read back in any structure containing any or all of the above, chasing all pointers down to the level of literal atoms, numbers or strings. Hprint operates by simulating the INTERLISP print routine for normal list structures. When it encounters a user datatype (see Section 23), or an array or hash array, it prints the data contained therein, surrounded by special characters defined as read-macro characters (see Section 14). While chasing the pointers of a list structure, it also keeps a hash table of those items it encounters, and if any item is encountered a second time, another read-macro character is inserted before the first 42 occurrence, and all subsequent occurrences are printed as a back reference using an appropriate macro character. Thus the inverse function, hread merely calls the INTERLISP read routine with the appropriate readtable, so that reading time is only a function of the complexity of the structure. hprint[x;file;uncircular] ------------------------------------------------------------------------ 40 for Horrible PRINT. The hprint package was written by L. M. Masinter. 41 Hprint currently cannot handle compiled code arrays, stack positions, or arbitrary unboxed numbers. 42 by resetting the file pointer using setfileptr. 21.24 43 prints x on file. If uncircular=T, hprint does no checking for any circularities in x (but is still useful for dumping arbitrary structures of arrays, hash arrays, lists, user data types, etc., that do not contain circularities), which results in a large speed and internal-storage advantage. hread[file] reads an hprint-ed expression from file. HORRIBLEVARS is a prettydef macro for saving and loading the value of "horrible" variables. A prettydef command of the form (HORRIBLEVARS var1 ... varn) will cause appropriate expressions to be written which will restore the values of vari ... varn when the file is loaded. The values of vari ... varn are all printed by the same operation, so that they may contain cross references to common structures. 21.9 Typescript Files A typescript file is a "transcript" of all of the input and output on a terminal. The following function enables transcript files for INTERLISP. 44 dribble[filename;appendflg] Opens filename and begins recording the typescript. If appendflg=T, the typescript will 45 be appended to the end of filename. dribble[] ------------------------------------------------------------------------ 43 Note: hprint is intended primarily for output to disk files, since the algorithm depends on being able to reset the file pointer. If file is not a disk file (and uncircular = NIL), a temprary file is opened, x is hprinted on it, and then that file is copied to the final output file. 44 dribble was written by D. C. Lewis. 45 dribble also takes an extra argument, thawedflg. If thawedflg=T, the file will be opened in "thawed" mode. 21.25 46 closes the typescript file. dribble processes a line buffer at a time. Thus, the typescript produced is somewhat neater than that generated by TELNET because it does not show characters that were erased via control-A or control-Q. Note that the typescript file is not included in the list of files returned by openp[], nor will it be closed by a call to closeall or closef. Only dribble[] closes the typescript file. dribblefile[] returns name of current typescript file, if any, otherwise NIL. 21.10 Display Terminals The value of the variable displaytermflg indicates whether the user is running on a display terminal or not. displaytermflg is used in various places in the system, e.g., prettyprint, helpsys, etc., primarily to decide how much information to present to the user (more on a display terminal than on a hard copy terminal.) displaytermflg is initialized to the value of displaytermp[], whenever INTERLISP is (re)-entered, and after returning from a sysout. displaytermp[] value is T if user is on a display terminal, NIL otherwise. In INTERLISP-10, displaytermp is defined to invoke the appropriate jsys to check the user's terminal type. ------------------------------------------------------------------------ 46 Only one typescript file can be active at any one point; i.e., dribble[file1] followed by dribble[file2] will cause file1 to be closed. 21.26