// CmdScan.bcpl -- Command Scanner Package, main module // Copyright Xerox Corporation 1979 // Last modified July 13, 1977 5:47 PM get "CmdScan.decl" external [ //outgoing procedures InitCmd; ErasePhrase; BackupPhrase DefaultPhrase; BeginDefaultPhrase; EndDefaultPhrase EnableCatch; DisableCatch; EndCatch CurrentPhrase; NextPhrase; TerminatingChar; CmdErrorCode //for OEP declaration only CSGets; CSPuts; CSEndofs; CSCloses; CSResets //incoming procedures DefErase; DefError; DefBreak; DefEcho; AppendChar; EraseInput Gets; Puts; Errors; Wss; Closes DefaultArgs; Allocate; Free; Zero CallersFrame; GotoLabel //incoming statics sysZone; keys; dsp ] //--------------------------------------------------------------------------- let InitCmd(maxChars, maxPhrases, WordBreak, PhraseTerminator, Echo, keyS, dspS, Erase, Error, zone; numargs na) = valof //--------------------------------------------------------------------------- //Creates and initializes a Command State (cs) structure. //Required arguments are maxChars, the maximum number of characters //permitted in the command (including noise words), and maxPhrases, //the maximum number of phrases. WordBreak, PhraseTerminator, and //Echo are the default word break, phrase terminator, and echo //predicates for the command (they may be overridden on a //per-phrase basis). keyS and dspS are the input and output //streams for the command scanner. Erase is a procedure for //erasing characters from the display. Error is the Errors //procedure for the command stream. //returns a pointer to the CS structure. [ DefaultArgs(lv na, -2, DefBreak, DefBreak, DefEcho, keys, dsp, DefErase, DefError, sysZone) let cs = Allocate(zone, lenCS+maxPhrases*lenPD) Zero(cs, lenCS) cs>>CS.buf = Allocate(zone, maxChars rshift 1 +1) cs>>CS.maxChars = maxChars cs>>CS.maxPhrases = maxPhrases cs>>CS.pd↑0.WordBreak = WordBreak cs>>CS.pd↑0.PhraseTerminator = PhraseTerminator cs>>CS.pd↑0.Echo = Echo cs>>CS.keyS = keyS cs>>CS.dspS = dspS cs>>CS.Erase = Erase cs>>CS.zone = zone cs>>CS.gets = CSGets cs>>CS.puts = CSPuts cs>>CS.endof = CSEndofs cs>>CS.close = CSCloses cs>>CS.reset = CSResets cs>>CS.error = Error cs>>CS.pd↑0.catchFrame = CallersFrame() cs>>CS.pd↑0.catchPC = (cs>>CS.pd↑0.catchFrame)!1 +1 cs>>CS.phraseRead = true //don't use dummy phrase cs>>CS.editControl = editNew resultis cs ] //--------------------------------------------------------------------------- and CSCloses(cs) be //--------------------------------------------------------------------------- [ Free(cs>>CS.zone, cs>>CS.buf) Free(cs>>CS.zone, cs) ] //--------------------------------------------------------------------------- and CSGets(cs) = valof //--------------------------------------------------------------------------- //Returns the next character from the current phrase. Calls Errors if //the phrase is exhausted. [ let i = cs>>CS.iChOut if i ge CurrentPhrase(cs)>>PD.iLast then resultis Errors(cs, ecEndOfPhrase) cs>>CS.iChOut = i+1 resultis cs>>CS.buf>>Buf↑i & #177 ] //--------------------------------------------------------------------------- and CSEndofs(cs) = cs>>CS.iChOut ge CurrentPhrase(cs)>>PD.iLast //--------------------------------------------------------------------------- //Returns true if the current phrase is exhausted //--------------------------------------------------------------------------- and CSPuts(cs, char) be unless cs>>CS.reparse do AppendChar(cs, char, true) //--------------------------------------------------------------------------- //Outputs char to the stream dspS, and also puts it in the command //buffer (to facilitate retyping and backspacing over). //--------------------------------------------------------------------------- and CSResets(cs) be [ cs>>CS.phraseRead = false; cs>>CS.reuse = true ] //--------------------------------------------------------------------------- //Resets the output pointer to the beginning of the current phrase //such that the next GetPhrase will return the same phrase as before. //--------------------------------------------------------------------------- and NextPhrase(cs) = valof //--------------------------------------------------------------------------- //advances the output pointer to the next unread phrase, unless //the current phrase hasn't been read yet. //returns pointer to phrase's PD. [ if cs>>CS.phraseRead then [ if cs>>CS.iPhOut eq cs>>CS.maxPhrases then Errors(cs, ecTooManyPhrases) cs>>CS.iPhOut = cs>>CS.iPhOut+1 cs>>CS.phraseRead = false if cs>>CS.iPhOut gr cs>>CS.iPhIn then [ //not rescanning - really create new phrase cs>>CS.iPhIn = cs>>CS.iPhOut let pd = CurrentPhrase(cs) Zero(pd, lenPD) pd>>PD.iFirst = cs>>CS.iChIn ] ] resultis CurrentPhrase(cs) ] //--------------------------------------------------------------------------- and DefaultPhrase(cs, string, char; numargs na) be //--------------------------------------------------------------------------- //Creates a new phrase containing the default value "string", //and sets the editControl to editReplace. The string should not //contain a terminating character. //The idea is that one then calls GetPhrase to input the phrase //after giving the user a chance to replace or edit it. //If char is supplied, it is used as the terminating character and //the user is not given a chance to edit the phrase. [ DefaultArgs(lv na, -2, 0) BeginDefaultPhrase(cs) Wss(cs, string) EndDefaultPhrase(cs, char) ] //--------------------------------------------------------------------------- and BeginDefaultPhrase(cs) be NextPhrase(cs) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- and EndDefaultPhrase(cs, char; numargs na) be //--------------------------------------------------------------------------- unless cs>>CS.reparse do [ CurrentPhrase(cs)>>PD.iLast = cs>>CS.iChIn cs>>CS.editControl = editReplace if na gr 1 & char ne 0 then cs>>CS.putbackChar = char ] //--------------------------------------------------------------------------- and EnableCatch(cs) = valof //--------------------------------------------------------------------------- [ let pd = NextPhrase(cs) pd>>PD.catchFrame = CallersFrame() pd>>PD.catchPC = (pd>>PD.catchFrame)!1 + 1 resultis false ] //--------------------------------------------------------------------------- and DisableCatch(cs) be CurrentPhrase(cs)>>PD.catchFrame = 0 //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- and EndCatch(cs) be //--------------------------------------------------------------------------- [ if cs>>CS.iPhOut gr cs>>CS.iPhTarget then [ cs>>CS.iPhOut = cs>>CS.iPhOut-1; DoBackup(cs) ] cs>>CS.iChOut = CurrentPhrase(cs)>>PD.iFirst cs>>CS.phraseRead = cs>>CS.iPhOut eq 0 cs>>CS.errorCode = 0 ] //--------------------------------------------------------------------------- and ErasePhrase(cs, nPh, editControl, char; numargs na) be //--------------------------------------------------------------------------- //Sends control back nPh phrases relative to CS.iPhOut after //erasing all intervening phrases. [ DefaultArgs(lv na, -1, 0, editReplace, 0) let pd = lv cs>>CS.pd↑(cs>>CS.iPhOut-nPh) EraseInput(cs, (editControl eq editNew? pd>>PD.iFirst, pd>>PD.iLast), eraseWord) BackupPhrase(cs, nPh, editControl, char) ] //--------------------------------------------------------------------------- and BackupPhrase(cs, nPh, editControl, char; numargs na) be //--------------------------------------------------------------------------- //Backs up the command scanner nPh phrases relative to CS.iPhOut, //and stores editControl into CS.editControl and char into //CS.putbackChar. //All intervening enabled catch phrases are executed, including //the one associated with the target phrase if any. This procedure //does not actually erase any characters from the command, it //merely sends control back to an earlier point of interpretation. [ DefaultArgs(lv na, -1, 0, editReplace, 0) cs>>CS.iPhTarget = cs>>CS.iPhOut-nPh cs>>CS.editControl = editControl cs>>CS.putbackChar = char cs>>CS.reparse = true DoBackup(cs) ] //--------------------------------------------------------------------------- and DoBackup(cs) be //--------------------------------------------------------------------------- [ let pd = CurrentPhrase(cs) let frame, pc = pd>>PD.catchFrame, pd>>PD.catchPC if frame ne 0 then [ if cs>>CS.iPhOut eq 0 then test cs>>CS.destroy ifso [ Closes(cs); cs = 0 ] ifnot EndCatch(cs) GotoLabel(frame, pc, cs) ] cs>>CS.iPhOut = cs>>CS.iPhOut-1 ] repeat //--------------------------------------------------------------------------- and CurrentPhrase(cs) = lv cs>>CS.pd↑(cs>>CS.iPhOut) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- and TerminatingChar(cs) = //--------------------------------------------------------------------------- cs>>CS.buf>>Buf↑(CurrentPhrase(cs)>>PD.iLast) & #177 //--------------------------------------------------------------------------- and CmdErrorCode(cs) = cs>>CS.errorCode //---------------------------------------------------------------------------