// CmdScanEdit.bcpl -- Command Scanner Package editing functions // Copyright Xerox Corporation 1979 // Last modified July 11, 1977 3:25 PM get "CmdScan.decl" external [ //outgoing procedures GetPhrase; AppendChar; EraseInput //incoming procedures NextPhrase; CurrentPhrase; BackupPhrase Gets; Puts; Errors; Wss DefaultArgs; Allocate; Free; Zero ] //--------------------------------------------------------------------------- let GetPhrase(cs, WordBreak, PhraseTerminator, Echo, Help, helpArg; numargs na) = valof //--------------------------------------------------------------------------- //Readies the next phrase to be taken, inputting one from the //keyboard if necessary and updating the phrase state. //Sets up cs>>CS.iChOut to point to the first character of the //phrase, and returns the number of characters in the phrase, //not including the terminator. [ DefaultArgs(lv na, -1, cs>>CS.pd^0.WordBreak, cs>>CS.pd^0.PhraseTerminator, cs>>CS.pd^0.Echo, 0, 0) let pd = NextPhrase(cs) pd>>PD.WordBreak = WordBreak pd>>PD.PhraseTerminator = PhraseTerminator pd>>PD.Echo = Echo if cs>>CS.iPhIn eq cs>>CS.iPhOut then cs>>CS.reparse = false unless cs>>CS.reparse % cs>>CS.reuse do EditPhrase(cs, Help, helpArg) cs>>CS.reuse = false cs>>CS.phraseRead = true cs>>CS.iChOut = pd>>PD.iFirst //ready to take first char resultis pd>>PD.iLast-pd>>PD.iFirst ] //--------------------------------------------------------------------------- and EditPhrase(cs, Help, helpArg) be //--------------------------------------------------------------------------- //Edits the current phrase (the current input and output phrases //are assumed to be the same), returning when a terminating //character is input. //The disposition of the existing contents of the phrase (if any) //is controlled by cs>>CS.editControl, which is one of: // editNew New phrase -- fill it in (this is the default). // editAppend Phrase exists -- remove terminating character // and append more characters to it. // editReplace Phrase exists -- if the first character input is // not a terminating character, erase it and start // over; if it is, just return the phrase as-is. //If cs>>CS.putbackChar is nonzero, it is used as the first //character of the phrase as if it had been typed in. [ let pd = CurrentPhrase(cs) [ //repeat let char = cs>>CS.putbackChar ne 0? cs>>CS.putbackChar, Gets(cs>>CS.keyS) cs>>CS.putbackChar = 0 if cs>>CS.editControl ne editNew & cs>>CS.iChIn gr pd>>PD.iLast then EraseInput(cs, pd>>PD.iLast, eraseTerminator) switchon char into [ case $*010: case $*001: //backspace, control-A [ EraseBackTo(cs, BackupChars(cs, false), eraseChar); endcase ] case $*027: //control-W [ EraseBackTo(cs, BackupChars(cs, true), eraseWord); endcase ] case $*177: //delete [ Errors(cs, ecCmdDelete); loop ] case $*022: //control-R [ RetypeCmd(cs); loop ] case $?: [ if Help ne 0 then [ Wss(cs>>CS.dspS, "? ") Help(cs>>CS.dspS, helpArg) RetypeCmd(cs) loop ] //if no help, fall thru to default case ] default: [ if cs>>CS.editControl eq editReplace & not pd>>PD.PhraseTerminator(cs, char) then EraseBackTo(cs, pd>>PD.iFirst, eraseWord) if AppendChar(cs, char, pd>>PD.Echo(cs, char)) & pd>>PD.PhraseTerminator(cs, char) then [ pd>>PD.iLast = cs>>CS.iChIn-1 test char eq $*s & pd>>PD.iLast eq pd>>PD.iFirst ifso pd>>PD.iFirst = pd>>PD.iFirst+1 ifnot [ cs>>CS.editControl = editNew; break ] ] ] ] cs>>CS.editControl = editNew ] repeat ] //--------------------------------------------------------------------------- and AppendChar(cs, char, echo) = valof //--------------------------------------------------------------------------- //Appends a character to the command buffer, and also echos it if echo //is true. If echo is false, #200 is added to the character as a signal //that the character is non-printing. Returns true normally. //If the buffer overflows, Errors is called; if Errors returns, AppendChar //returns false having done nothing. [ let i = cs>>CS.iChIn if i ge cs>>CS.maxChars then [ Errors(cs, ecCmdTooLong); resultis false ] test echo ifso Puts(cs>>CS.dspS, char) ifnot char = char+#200 cs>>CS.buf>>Buf^i = char cs>>CS.iChIn = i+1 resultis true ] //--------------------------------------------------------------------------- and BackupChars(cs, backupWord) = valof //--------------------------------------------------------------------------- //If backupWord is false, returns the index of the last character input. //if true, returns the index of the first character of the current word. [ let iCh = cs>>CS.iChIn let pd = lv cs>>CS.pd^(FindPhrase(cs, iCh)) let nonBreakSeen = false [ //repeat let iNew = iCh-1 if iCh eq pd>>PD.iFirst then [ //back up to previous phrase pd = pd-lenPD if pd eq lv cs>>CS.pd^0 then break iNew = pd>>PD.iLast ] unless backupWord resultis iNew test pd>>PD.WordBreak(cs, cs>>CS.buf>>Buf^iNew & #177) ifso if nonBreakSeen then break ifnot nonBreakSeen = true iCh = iNew ] repeat resultis iCh ] //--------------------------------------------------------------------------- and EraseInput(cs, i, context) be //--------------------------------------------------------------------------- //Erases characters from the one most recently input back to i //(inclusive) and resets the input pointer to the beginning of //the erased portion. [ if i ls cs>>CS.iChIn then cs>>CS.Erase(cs, i, cs>>CS.iChIn-1, context) cs>>CS.iChIn = i cs>>CS.iPhIn = FindPhrase(cs, i) ] //--------------------------------------------------------------------------- and EraseBackTo(cs, i, context) be //--------------------------------------------------------------------------- //Same as EraseInput; additionally, if characters before the current //phrase are erased, initiates a reparse of preceding phrases. [ EraseInput(cs, i, context) if cs>>CS.iPhIn ls cs>>CS.iPhOut then BackupPhrase(cs, cs>>CS.iPhOut-cs>>CS.iPhIn, editAppend) ] //--------------------------------------------------------------------------- and FindPhrase(cs, iCh) = valof //--------------------------------------------------------------------------- //Returns index of phrase to which character iCh belongs [ let iPh = cs>>CS.iPhIn while iCh ls cs>>CS.pd^iPh.iFirst do iPh = iPh-1 resultis iPh ] //--------------------------------------------------------------------------- and RetypeCmd(cs) be //--------------------------------------------------------------------------- [ Puts(cs>>CS.dspS, $*n) for i = 0 to cs>>CS.iChIn-1 do [ let char = cs>>CS.buf>>Buf^i if char ls #200 then Puts(cs>>CS.dspS, char) ] ]