{Begin SubSec BREAK1}
{Title BREAK1}
{Text

{index *BEGIN* BREAK1 FN}

The basic function of the break package is {fn BREAK1}, which creates a break.  A break appears to be a regular executive, with the prompt "{lisp :}", but {fn BREAK1} also detects and interpretes break commands ({PageRef Tag BreakCommands}).


{FnDef {FnName BREAK1} {FnArgs BRKEXP BRKWHEN BRKFN BRKCOMS BRKTYPE ERRORN}
{Type NLAMBDA}
{Text
If {index BRKWHEN Var}{arg BRKWHEN} is {lisp NIL}, {index BRKEXP Var}{arg BRKEXP} is evaluated and returned as the value of {fn BREAK1}. Otherwise a break occurs and commands are then taken from {index BRKCOMS Var}{arg BRKCOMS} or the terminal and interpreted.  All inputs not recognized by {fn BREAK1} are simply passed on to the programmer's assistant.

When a break occurs, if {arg ERRORN} is a list whose {lisp CAR} is a number, {fn ERRORMESS} is called to print an identifying message.  If {arg ERRORN} is a list whose {lisp CAR} is not a number, {fn ERRORMESS1} is called.  Otherwise, no preliminary message is printed.  Following this, the message {lisp ({arg BRKFN} broken)}{index *PRIMARY* BRKFN Var} is printed.

{note above is unclear}


Since {fn BREAK1} itself calls functions, when one of these is broken, an infinite loop would occur.  {fn BREAK1} detects this situation, and prints {lisp Break within a break on {arg FN}}, and then simply calls the function without going into a break.

{indexX {Name Break within a break on}
        {Type (Printed by System)}
        {Info *PRIMARY*}
        {Text {lisp Break within a break on {arg FN}}}
}


The commands {breakcom GO}, {breakcom !GO}, {breakcom OK}, {breakcom !OK}, {breakcom RETURN} and {breakcom ↑} are the only ways to leave {fn BREAK1}.  The command {breakcom EVAL} causes {index BRKEXP Var}{arg BRKEXP} to be evaluated, and saves the value on the variable {var !VALUE}.{index !VALUE Var}  Other commands can be defined for {fn BREAK1} via {index BREAKMACROS Var}{var BREAKMACROS} (below).

{arg BRKTYPE}{index BRKTYPE Var} is {lisp NIL} for user breaks, {atom INTERRUPT}{index INTERRUPT Litatom} for control-H breaks, and {atom ERRORX}{index ERRORX Litatom} for error breaks.  For breaks when {arg BRKTYPE} is not {lisp NIL}, {fn BREAK1} will clear and save the input buffer.{index input buffer}  If the break returns a value (i.e., is not aborted via {breakcom ↑}{index ↑ BreakCom} or {index control-D}control-D) the input buffer will be restored.
}}



The fourth argument to {fn BREAK1} is {index *PRIMARY* BRKCOMS Var}{arg BRKCOMS}, a list of break commands{index break commands} that {fn BREAK1} interprets and executes as though they were keyboard input.  One can think of {arg BRKCOMS} as another input file which always has priority over the keyboard.  Whenever {arg BRKCOMS}={lisp NIL}, {fn BREAK1} reads its next command from the keyboard.  Whenever {arg BRKCOMS} is not {lisp NIL}, {fn BREAK1} takes {lisp (CAR BRKCOMS)} as its next command and sets {arg BRKCOMS} to {lisp (CDR BRKCOMS)}.  For example, suppose the user wished to see the value of the variable {lisp X} {it after} a function was evaluated.  He could set up a break with {arg BRKCOMS}={lisp (EVAL (PRINT X) OK)}, which would have the desired effect.  Note that if {arg BRKCOMS} is not {lisp NIL}, the value of a break command is not printed.  If you desire to see a value, you must print it yourself, as in the above example.  The function {index TRACE FN}{fn TRACE} ({PageRef Fn TRACE}) uses {arg BRKCOMS}: it sets up a break with two commands; the first one prints the arguments of the function, or whatever the user specifies, and the second is the command {breakcom GO}, which causes the function to be evaluated and its value printed.

Note:  If an error occurs while interpreting the {arg BRKCOMS} commands, {arg BRKCOMS} is set to {lisp NIL}, and a full interactive break occurs.



The break package has a facility for redirecting ouput to a file.  All output resulting from {index BRKCOMS Var}{arg BRKCOMS} will be output to the value of the variable {var BRKFILE},{index *PRIMARY* BRKFILE Var} which should be the name of an open file.  Output due to user typein is not affected, and will always go to the terminal.  {var BRKFILE} is initially {lisp T}.


{VarDef {Name BREAKMACROS}
{Text
{var BREAKMACROS} is a list of the form {lisp ( ({arg NAME{sub 1}} {arg COM{sub 11}} {ellipsis} {arg COM{sub 1n}}) ({arg NAME{sub 2}} {arg COM{sub 21}} {ellipsis} {arg COM{sub 2n}}) {ellipsis})}.  Whenever an atomic command is given to {fn BREAK1}, it first searches the list {var BREAKMACROS} for the command.  If the command is equal to {arg NAME{sub i}}, {fn BREAK1} simply appends the corresponding commands to the front of {arg BRKCOMS}, and goes on.  If the command is not found on {var BREAKMACROS}, {fn BREAK1} then checks to see if it is one of the built in commands, and finally, treats it as a function or variable as before.{foot 
If the command is not the name of a defined function, bound variable, or {fn LISPX} command, {fn BREAK1} will attempt spelling correction using {var BREAKCOMSLST}{index BREAKCOMSLST Var} as a spelling list.{index spelling lists}{index spelling correction}  If spelling correction is unsuccessful, {fn BREAK1} will go ahead and call {fn LISPX} anyway, since the atom may also be a misspelled history command.
}{comment endfootnote}

Example:  The command {breakcom ARGS} could be defined by including on {var BREAKMACROS} the form: {lisp (ARGS (PRINT (VARIABLES LASTPOS T)))}
}}

{FnDef {FnName BREAKREAD} {FnArgs TYPE}
{Text
Useful within {var BREAKMACROS} for reading arguments.  If {var BRKCOMS} is non-{lisp NIL} (the command in which the call to {fn BREAKREAD} appears was not typed in), returns the next break command from {var BRKCOMS}, and sets {var BRKCOMS} to {lisp (CDR BRKCOMS)}.

If {var BRKCOMS} is {lisp NIL} (the command was typed in), then {fn BREAKREAD} returns either the rest of the commands on the line as a list (if {arg TYPE}={lisp LINE}) or just the next command on the line (if {arg TYPE} is not {lisp LINE}).

For example, the {breakcom BT} command is defined as {lisp (BAKTRACE LASTPOS NIL (BREAKREAD 'LINE) 0 T)}.  Thus, if the user types {breakcom BT}, the third argument to {fn BAKTRACE} will be {lisp NIL}.  If the user types {lisp BT SUBRP}, the third argument will be {lisp (SUBRP)}.
}}




{VarDef {Name BREAKRESETFORMS}
{Text
If the user is developing programs that change the way a user and Interlisp normally interact (e.g., change or disable the interrupt or line-editing characters, turn off echoing, etc.), debugging them by breaking or tracing may be difficult, because Interlisp might be in a "funny" state at the time of the break.  {var BREAKRESETFORMS} is designed to solve this problem.  The user puts on {var BREAKRESETFORMS} expressions suitable for use in conjunction with {fn RESETFORM} or {fn RESETSAVE} ({PageRef Fn RESETSAVE}).  When a break occurs, {fn BREAK1} evaluates each expression on {var BREAKRESETFORMS} {it before} any interaction with the terminal, and saves the values.  When the break expression is evaluated via an {breakcom EVAL}, {breakcom OK}, or {breakcom GO}, {fn BREAK1} first restores the state of the system with respect to the various expressions on {var BREAKRESETFORMS}.  When (if) control returns to {fn BREAK1}, the expressions on {var BREAKRESETFORMS} are {it again} evaluated, and their values saved.  When the break is exited with an {breakcom OK}, {breakcom GO}, {breakcom RETURN}, or {breakcom ↑} command, by typing control-D, or by a {fn RETFROM} or {fn RETEVAL} typed in by the user,{foot
All user type-in is scanned in order to make the operations undoable as described on {PageRef Tag SlashifyingTypedInFns}.
At this point, {fn RETFROM}s and {fn RETEVAL}s are also noticed.
However, if the user types in an expression which calls a function that then does a {fn RETFROM}, this {fn RETFROM} will not be noticed, and the effects of {var BREAKRESETFORMS} will {it not} be reversed.
}{comment endfootnote}
{fn BREAK1} again restores state.
Thus the net effect is to make the break invisible with respect to the user's programs, but nevertheless allow the user to interact in the break in the normal fashion.

As mentioned earlier, {fn BREAK1} detects "Break within a break" situations, and avoids infinite loops.  If the loop occurs because of an error, {fn BREAK1} simply rebinds {var BREAKRESETFORMS} to {lisp NIL}, and calls {fn HELP}.  This situation most frequently occurs when there is a bug in a function called by {var BREAKRESETFORMS}.

Note:  {fn SETQ} expressions can also be included on {var BREAKRESETFORMS} for saving and restoring system parameters, e.g. {lisp (SETQ LISPXHISTORY NIL)}, {lisp (SETQ DWIMFLG NIL)}, etc.
These are handled specially by {fn BREAK1} in that the current value of the variable is saved before the {fn SETQ} is executed, and upon restoration, the variable is set back to this value.
}}


{index *END* BREAK1 FN}

}{End SubSec BREAK1}




{Begin SubSec Error Functions}
{Title Error Functions}
{Text


{FnDef {FnName ERRORX} {FnArgs ERXM}
{Text
The entry to the error routines.  If {arg ERXM}={lisp NIL}, {lisp (ERRORN)} is used to determine the error-message.  Otherwise, {lisp (SETERRORN (CAR {arg ERXM}) (CADR {arg ERXM}))} is performed, "setting" the error number and argument.  Thus following either {lisp (ERRORX '(10 T))} or {lisp (PLUS T)}, {lisp (ERRORN)} is {lisp (10 T)}.  {fn ERRORX} calls {index BREAKCHECK FN}{fn BREAKCHECK}, and either induces a break or prints the message and unwinds to the last {index ERRORSET FN}{fn ERRORSET} ({PageRef Fn BREAKCHECK}).  Note that {fn ERRORX} can be called by any program to intentionally induce an error of any type.  However, for most applications, the function {fn ERROR} will be more useful.
}}



{FnDef {FnName ERROR} {FnArgs MESS1 MESS2 NOBREAK}
{Text
Prints {arg MESS1} (using {fn PRIN1}), followed by a space if {arg MESS1} is an atom, otherwise a carriage return.  Then {arg MESS2} is printed (using {fn PRIN1} if {arg MESS2} is a string, otherwise {fn PRINT}).  For example, {lisp (ERROR "NON-NUMERIC ARG" T)} prints

{lispcode
NON-NUMERIC ARG
T}

and {lisp (ERROR 'FOO "NOT A FUNCTION")} prints {lisp FOO NOT A FUNCTION}.  If both {arg MESS1} and {arg MESS2} are {lisp NIL}, the message printed is simply {lisp ERROR}.{index ERROR Error}

If {arg NOBREAK}={lisp T}, {index ERROR FN}{fn ERROR} prints its message and then calls {fn ERROR!}.{foot
unless the value of {var HELPFLAG} is {lisp BREAK!}, in which case a break will always occur (see {PageRef Var HELPFLAG}).
}{comment endfootnote}
Otherwise it calls {lisp (ERRORX '(17 ({arg MESS1} . {arg MESS2})))}, i.e., generates error number 17, in which case the decision as to whether or not to break, and whether or not to print a message, is handled as per any other error.
}}



{FnDef {FnName HELP} {FnArgs MESS1 MESS2 BRKTYPE}
{Text
Prints {arg MESS1} and {arg MESS2} similar to {fn ERROR}, and then calls {index BREAK1 FN}{fn BREAK1} passing {arg BRKTYPE} as the {lisp BRKTYPE} argument.  If both {arg MESS1} and {arg MESS2} are {lisp NIL}, {lisp Help!}{index Help! Error} is used for the message.  {fn HELP} is a convenient way to program a default condition, or to terminate some portion of a program which the computation is theoretically never supposed to reach. 
}}



{FnDef {FnName SHOULDNT} {FnArgs MESS}
{Text
Useful in those situations when a program detects a condition that should never occur.  Calls {fn HELP} with the message arguments {arg MESS} and {lisp "Shouldn't happen!"} and a {lisp BRKTYPE} argument of {lisp 'ERRORX}.{index Shouldn't happen! Error}
}}



{FnDef {FnName ERROR!} {FnArgs}
{Text
Programmable control-E;{index control-E} immediately returns from last {index ERRORSET FN}{fn ERRORSET} or resets.

{note FROM the last ERRORSET, or TO ???}
}}



{FnDef {FnName RESET} {FnArgs}
{Text
Programmable control-D;{index control-D} immediately returns to the top level.
}}



{FnDef {FnName ERRORN} {FnArgs}
{Text
Returns information about the last error in the form {lisp ({arg NUM} {arg EXP})} where {arg NUM} is the error number ({PageRef Tag ErrorNumbers}) and {arg EXP} is the expression which was (would have been) printed out after the error message.  For example, following {lisp (PLUS T)}, {lisp (ERRORN)} would return {lisp (10 T)}.
}}



{FnDef {FnName SETERRORN} {FnArgs NUM MESS}
{Text
Sets the value returned by {lisp ERRORN} to {lisp ({arg NUM} {arg MESS})}.
}}



{FnDef {FnName ERRORMESS} {FnArgs U}
{Text
Prints message corresponding to an {fn ERRORN} that yielded {arg U}.
For example, {lisp (ERRORMESS '(10 T))} would print

{lispcode
NON-NUMERIC ARG
T}
}}


{FnDef {FnName ERRORMESS1} {FnArgs MESS1 MESS2 MESS3}
{Text
Prints the message corresponding to a {fn HELP} or {fn ERROR} break.
}}


{FnDef {FnName ERRORSTRING} {FnArgs N}
{Text
Returns as a new string the message corresponding to error number {arg N}, e.g., {lisp (ERRORSTRING 10)}={lisp "NON-NUMERIC ARG"}.
}}


{FnDef {FnName ERRORSET} {FnArgs FORM FLAG {anonarg}}
{Text
Performs {lisp (EVAL {arg FORM})}.  If no error occurs in the evaluation of {arg FORM}, the value of {fn ERRORSET} is a list containing one element, the value of {lisp (EVAL {arg FORM})}.
If an error did occur, the value of {fn ERRORSET} is {lisp NIL}.

Note that {fn ERRORSET} is a lambda function, so its arguments are evaluated {it before} it is entered, i.e., {lisp (ERRORSET X)} means {fn EVAL} is called with the {it value} of {lisp X}.  In most cases, {fn ERSETQ} and {fn NLSETQ} (described below) are more useful.


The argument {arg FLAG} controls the printing of error messages if an error occurs:

If {arg FLAG}={lisp T}, the error message is printed; if {arg FLAG}={lisp NIL} it is not (unless {var NLSETQGAG} is {lisp NIL}, see below).  Note that if a {it break} occurs below an {fn ERRORSET}, the message is printed regardless of the value of {arg FLAG}.

If {arg FLAG}={lisp INTERNAL}, this {fn ERRORSET} is ignored for the purpose of deciding whether or not to break or print a message (see {PageRef Tag WhenToBreak}).  However, the {fn ERRORSET} is in effect for the purpose of flow of control, i.e., if an error occurs, this {fn ERRORSET} returns {lisp NIL}.

If {arg FLAG}={lisp NOBREAK}, no break will occur, even if the time criterion for breaking is met.  Note that {arg FLAG}={lisp NOBREAK} will {it not} prevent a break from occurring if the error occurs more than {var HELPDEPTH} function calls below the errorset, since {fn BREAKCHECK} will stop searching before it reaches the {fn ERRORSET}.  To guarantee that no break occurs, the user would also either have to reset {var HELPDEPTH} or {var HELPFLAG}.
}}



{FnDef {FnName ERSETQ} {FnArgs FORM}
{Type NLAMBDA}
{Text
Performs {lisp (ERRORSET '{arg FORM} T)}, evaluating {arg FORM} and printing error messages.
}}



{FnDef {FnName NLSETQ} {FnArgs FORM}
{Type NLAMBDA}
{Text
Performs {lisp (ERRORSET '{arg FORM} NIL)}, evaluating {arg FORM} without printing error messages.
}}


{VarDef {Name NLSETQGAG}
{Text
If {var NLSETQGAG} is {lisp NIL}, error messages will print, regardless of the {arg FLAG} argument of {fn ERRORSET}.  {var NLSETQGAG} effectively changes all {fn NLSETQ}s to {fn ERSETQ}s.
{var NLSETQGAG} is initially {lisp T}.
}}




{Begin Note}
Date:  9 NOV 1981 1858-PST
From: KAPLAN.PA
Subject: ERROR, NLSETQ, and FNCHECK

Tracking down a user-bug led me into a thicket of non-features in the error machinery.


NLSETQ

First, as is well known, NLSETQ does not guarantee that an error message won't be printed and a break not happen.  The reason is that it doesn't consult the flag that affects the behavior if it doesn't find it after scanning HELPDEPTH frames.  This is obviously a bug, which I propose to fix by not bounding FINDERSET inside BREAKCHECK by HELPDEPTH, letting the search go all the way to the top if necessary, then using the number of frames scanned and HELPDEPTH only to determine what should then happen.

A much more efficient implementation would be possible if the compiler for ERRORSETs bound a SPECVAR ERRORSET.FLG in that frame, so a simple free-variable lookup instead of a stack scan can be used as an initial filter.  Can this be done uniformly in the various implementations??

Second,  the HELPDEPTH criterion currently seems to count all frames on the stack.  Since the value of HELPDEPTH is a heuristic set up for the 10 implementation, it is not really appropriate for other implementations where the number of internal frames on the stack differs wildly, particularly when there isn't a block compiler. I propose changing this to count only REALFRAMEP's.


ERROR

ERROR has a third argument, NOBREAK, which if T currently means print out the error message and call ERROR!.  The bug in this is that BREAKCHECK is not called to determine, e.g., whether this is an error under an NLSETQ or an ERSETQ.  Thus, a client of a function that has this kind of call to ERROR cannot suppress the error message or  printing, even if he is prepared to handle the error situation himself. I think this is also a bug, and propose changing ERROR to first call BREAKCHECK to determine whether there is an NLSETQ above (or alternatively, to look at ERRORSET.FLG), and if so, let the behavior appropriate to NLSETQ prevail.  IN other words, the NOBREAK argument to ERROR would operate only if under an ERSETQ.

FNCHECK

The reason I got into this is that SMARTARGLIST calls FNCHECK with a flag indicating that the FNCHECK error should not be suppressed.  FNCHECK then calls ERROR, with a 3rd argument of T (NOBREAK) just in case neither LOAD nor LOADFROM frames exist on the stack.  This is obviously a crock.  Does anyone know what's so special about LOAD and LOADFROM??

In my particular case, with LOAD not on the stack, embedding the SMARTARGLIST in an NLSETQ did not suppress the error, but did suppress the user.


-----
Are there any reasonable objections to my proposal to change FINDERSET and ERROR as above?  Can the compiler guys install the specvar? Any hypotheses about LOAD/LOADFROM in FNCHECK?

{End Note}



}{End SubSec Error Functions}



{Begin SubSec Error Handling by Error Type}
{Title Error Handling by Error Type}
{Text

Occasionally the user may want to treat certain types of errors differently from others, e.g., always break, never break, or perhaps take some corrective action.  This can be accomplished via {var ERRORTYPELST}:


{VarDef {Name ERRORTYPELST}
{Text
{var ERRORTYPELST} is a list of elements of the form
{lisp ({arg NUM} {arg FORM{sub 1}} {ellipsis} {arg FORM{sub N}})}, where {arg NUM} is one of the error numbers ({PageRef Tag ErrorNumbers}).  During an error, after {index BREAKCHECK FN}{fn BREAKCHECK} has been completed, but before any other action is taken, {var ERRORTYPELST} is searched for an element with the same error number as that causing the error.  If one is found, the corresponding forms are evaluated, and if the last one produces a non-{lisp NIL} value, this value is substituted for the offender, and the function causing the error is reentered.
}}


{note offender?? how substituted?  how reentered? (by EVALing BRKEXP??)}


Within {var ERRORTYPELST} entries, the following variables may be useful:


{VarDef {Name ERRORMESS}
{Text
{fn CAR} is the error number, {fn CADR} the "offender", e.g.,
{lisp (10 NIL)} corresponds to a {lisp NON-NUMERIC ARG NIL} error.
}}


{VarDef {Name ERRORPOS}
{Text
Stack pointer to the function in which the error occurred, e.g., {lisp (STKNAME ERRORPOS)} might be {fn IPLUS}, {fn RPLACA}, {fn INFILE}, etc.

Note:  If the error is going to be handled by a {fn RETFROM}, {fn RETTO}, or a {fn RETEVAL} in the {var ERRORTYPELST} entry, it probably is a good idea to first release the stack pointer {var ERRORPOS}, e.g. by performing {lisp (RELSTK ERRORPOS)}.
}}


{VarDef {Name BREAKCHK}
{Text
Value of {fn BREAKCHECK}, i.e., {lisp T} means a break will occur, {lisp NIL} means one will not.  This may be reset within the {var ERRORTYPELST} entry.
}}


{VarDef {Name PRINTMSG}
{Text
If {lisp T}, means print error message, if {lisp NIL}, don't print error message, i.e., corresponds to second argument to {fn ERRORSET}.  The user can force or suppress the printing of error message for various errortypes by including on {var ERRORTYPELST} an expression which explicitly sets {var PRINTMSG}.
}}


For example, putting


{lispcode
[10 (AND (NULL (CADR ERRORMESS))
         (SELECTQ (STKNAME ERRORPOS)
                  ((IPLUS ADD1 SUB1) 0)
                  (ITIMES 1)
                  (PROGN (SETQ BREAKCHK T) NIL]}


on {var ERRORTYPELST} would specify that whenever a {lisp NON-NUMERIC ARG - NIL} error occurred, and the function in question was {fn IPLUS}, {fn ADD1}, or {fn SUB1}, {lisp 0} should be used for the {lisp NIL}.  If the function was {fn ITIMES}, {lisp 1} should be used.  Otherwise, always break.
Note that the latter case is achieved not by the value returned, but by the {it effect} of the evaluation, i.e., setting {var BREAKCHK} to {lisp T}.  Similarly, {lisp (16 (SETQ BREAKCHK NIL))} would prevent {lisp END OF FILE} errors from ever breaking.




{var ERRORTYPELST} is initially {lisp ((23 (SPELLFILE (CADR ERRORMESS) NIL NOFILESPELLFLG)))}, which causes {index SPELLFILE FN}{fn SPELLFILE} to be called in case of a {lisp FILE NOT FOUND} error (see {PageRef Fn SPELLFILE}).  If {fn SPELLFILE} is successful, the operation will be reexecuted with the new (corrected) file name.


}{End SubSec Error Handling by Error Type}




{Begin SubSec Interrupt Characters}
{Title Interrupt Characters}
{Text


{index *PRIMARY* interrupt characters}

{Tag InterruptChars}

Errors and breaks can be caused by errors within functions, or by explicitly breaking a function.  The user can also indicate his desire to go into a break at while a program is running by typing certain control characters known as "interrupt characters".  The interrupt characters in Interlisp-D are listed on {PageRef Tag InterruptCharsD}; those in Interlisp-10 are listed on {PageRef Tag InterruptChars10}.

{Tag UserInterruptChars}

{index *PRIMARY* user interrupt characters}

{note the info below needs to be revised for Interlisp-D. 

Note to manual formatter: can we change "atom" to something else?}


The user can disable and/or redefine Interlisp interrupt characters, as well as define new interrupt characters.  Interlisp-10 is initialized with 9 interrupt channels:  {atom RESET} (control-D), {atom ERROR} (control-E), {atom BREAK} (control-B), {atom HELP} (control-H), {atom PRINTLEVEL} (control-P), {atom CONTROL-T} (control-T), {atom RUBOUT} (del), {atom STORAGE} (control-S), and {atom OUTPUTBUFFER} (control-O).  Interlisp-D does not have the {atom STORAGE} and {atom OUTPUTBUFFER} interrupt channels, and has the additional channel {atom RAID} (control-C).  Each of these channels independently can be disabled, or have a new interrupt character assigned to it via the function {fn INTERRUPTCHAR} described below.  In addition, the user can enable new interrupt channels, and associate with each channel an interrupt character and an expression to be evaluated when that character is typed. (In Interlisp-10, a maximum of 9 interrupt channels can be enabled.)

{index RESET Litatom}
{index ERROR Litatom}
{index BREAK Litatom}
{index HELP Litatom}
{index PRINTLEVEL Litatom}
{index CONTROL-T Litatom}
{index RUBOUT Litatom}
{index STORAGE Litatom}
{index OUTPUTBUFFER Litatom}
{index RAID Litatom}

User interrupts can be either "hard" or "soft".  A "hard" interrupt is like control-E or control-D: it takes place as soon as it is typed.  A soft interrupt is like control-H; it does not occur until the next function call.  Soft interrupts can always be safely continued from.  Hard interrupts rip the system out of the function currently being executed and unwind back to the last function call, i.e. part of the computation that was interrupted is lost and cannot be continued.

Hard interrupts are implemented by generating error number 43, and retrieving the corresponding form from the list {var USERINTERRUPTS}{index USERINTERRUPTS Var} once inside of {fn ERRORX}.  Soft interrupts are implemented by calling {fn INTERRUPT} with an appropriate third argument, and then obtaining the corresponding form from {var USERINTERRUPTS}.  As soon as a soft interrupt character is typed, Interlisp clears and saves the input buffers, and then rings the bell.  After the interrupt form is evaluated, the input buffers are restored.  In either case, if a character is enabled as a user interrupt, but for some reason it is not found on {var USERINTERRUPTS}, an {lisp UNDEFINED USER INTERRUPT}{index UNDEFINED USER INTERRUPT Error} error will be generated.


{FnDef {FnName INTERRUPTCHAR} {FnArgs CHAR TYP/FORM HARDFLG}
{Text
Defines {arg CHAR} as an interrupt character.  If {arg CHAR} was previously defined as an interrupt character, that interpretation is disabled.

{arg CHAR} is either a character or a character code (as returned by {fn CHCON1}).  TENEX requires that interrupt characters be one of control-A, B,...,Z, space, esc(alt-mode), rubout(delete), or break.

{note does Interlisp-D have any restrictions??}

If {arg TYP/FORM}={lisp NIL}, {arg CHAR} is disabled.

If {arg TYP/FORM}={lisp T}, the current state of {arg CHAR} is returned without changing or disabling it.

If {arg TYP/FORM} is one of the 8 literal atoms {atom HELP}, {atom PRINTLEVEL}, {atom STORAGE}, {atom RUBOUT}, {atom ERROR}, {atom RESET}, {atom OUTPUTBUFFER}, or {atom BREAK}, then {fn INTERRUPTCHAR} assigns {arg CHAR} to the indicated Interlisp interrupt channel, (reenabling the channel if previously disabled).

If {arg TYP/FORM} is any other literal atom, {arg CHAR} is enabled as an interrupt character that when typed causes the atom {arg TYP/FORM} to be {it immediately} set to {lisp T}.

If {arg TYP/FORM} is a list, {arg CHAR} is enabled as a user interrupt character, and {arg TYP/FORM} is the form that is evaluated when {arg CHAR} is typed.  The interrupt will be hard if {arg HARDFLG}={lisp T}, otherwise soft.

{lisp (INTERRUPTCHAR T)} restores all Interlisp channels to their original state, and disables all user interrupts.


{fn INTERRUPTCHAR} returns an expression which, when given as an argument to {fn INTERRUPTCHAR}, will restore things as they were before the call to {fn INTERRUPTCHAR}.  Therefore, {fn INTERRUPTCHAR} can be used in conjunction with {fn RESETFORM} or {fn RESETLST} ({PageRef Fn RESETFORM}).

{fn INTERRUPTCHAR} is undoable.
}}



{FnDef {Name RESET.INTERRUPTS} {Args PERMITTEDINTERRUPTS SAVECURRENT?}
{text
{arg PERMITTEDINTERRUPTS} is a list of interrupt character settings to be performed, each of the form {lisp ({arg CHAR}  {arg TYP/FORM})}.  The effect of {fn RESET.INTERRUPTS} is as if {lisp (INTERRUPTCHAR {arg CHAR} {arg TYP/FORM})} were performed for each item on {arg PERMITTEDINTERRUPTS}, and {lisp (INTERRUPTCHAR {arg OTHERCHAR} NIL)} were performed on every other existing interrupt character.

If {arg SAVECURRENT?} is non-{lisp NIL}, then {fn RESET.INTERRUPTS} returns the current state of the interrupts in a form that could be passed to {fn RESET.INTERRUPTS}, otherwise it returns {lisp NIL}.  This can be used with a {fn RESET.INTERRUPTS} that appears in a {fn RESETFORM}, so that the list is built at "entry", but not upon "exit".

{note what about specifying "hard" interrupts?? -- who asked this question????
Answer: "hard" interrupts have now gone away from Interlisp-D -- see AR 615 -- JonL 23-Sep-84}
}}





{FnDef {FnName INTERRUPTABLE} {FnArgs FLAG}
{Text
if {arg FLAG}={lisp NIL}, turns interrupt off.
If {arg FLAG}={lisp T}, turns interrupt on.
Value is previous setting.  
{fn INTERRUPTABLE} compiles open.
}}


Note:  Any interrupt character typed while interrupts are off is treated the same as any other character, i.e. placed in the input buffer, and will not cause an interrupt when interrupts are turned back on.



{FnDef {FnName INTERRUPTABLEP} {FnArgs}
{Text
(Interlisp-10)  Returns {lisp T} if interrupts are enabled; {lisp NIL} if disabled.
}}


{Begin Note}
doc UNINTERRUPTABLY, INTERRUPTED?   Where?  ---mjs

Date: 24 FEB 1981 2314-PST
From: MASINTER.PA

UNINTERRUPTABLY now works quite differently: it just binds \INTERRUPTABLE to NIL. INTERRUPTED checks if \INTERRUPTABLE is NIL, and if so, merely returns. \InterruptEnable, currently checked by Bcpl code, will always be T. If interrupts are off, \InterruptChar will stay set to the last interrupt character typed, so that INTERRUPTED will be repeatedly called until interrupts are back on. This puts the burden on the case where there is a pending interupt (where it should be).
{End Note}

}{End SubSec Interrupt Characters}