!*-*-TECO-*-! !* A keyboard macro's data is represented using two ascii characters to describe each 9-bit character. The first ascii character holds the top 2 bits of the 9-bit character, and the second ascii character holds the low 7 bits. When the macro is run, pairs of characters are converted back into single 9-bit characters. Keyboard macros are encapsulated in TECO macros by putting the 37 chars "tempfm(m.m& Immediate Kbd Macro)" in front. This serves to make the TECO macro recognizable and to make it push the kbd macro when invoked. The keyboard macro call stack is a qvector in q..m. It has three slots for each call on the stack. The first slot contains the macro string. This string includes the call to & Immediate Kbd Macro. The second slot contains the index at which we are fetching from the string. The initial value of this slot is 37. The third slot contains the repeat count.! !~Filename~:! !Defining and calling keyboard macros.! KBDMAC !& Setup KBDMAC Library:! !S Put commands on keys. Create execution stack.! m.m ^R_Start_Kbd_Macro u:.x(() m.m ^R_End_Kbd_Macro u:.x()) m.m ^R_Call_Last u:.x(E) m.m ^R_Kbd_Macro_Query u:.x(Q) :i* m.v Last_Kbd_Macro :i*Kbd_Macro_Query m.v Kbd_Macro_Query 20.*5fs q vectoru..m q..m[..o hk  !& Run Kbd Macro:! !S This is the FS TYI SOURCE for running keyboard macros.! fq..m/5-3[2 q:..m(q2)[0 !* Get innermost running keyboard macro string.! q:..m(%2)[1 !* Fetch and increment the index at which we are fetching.! q1:g0*200.+(%1:g0) fs reread !* Form next input character, from next! !* two elements of string.! %1u:..m(q2) !* Store back index into string.! q1-fq0"l 0' !* If string is exhausted,! q:..m(%2)-1u0 !* decrement repeat count,! q0f"nf"gu:..m(q2)' !* If not exhausted, decrement count unless it was 0! 37u:..m(q2-1) 0' !* and anyway jump back to start of macro.! q..m[..o zj -15d !* Repeat count exhausted => pop it off the pdl,! z"e 0 fs tyi source' !* and if the pdl is empty we are no longer in a macro.! 0 !* Return value for ^R, in case ^R is reading the char.! !& Immediate Kbd Macro:! !S Push a kbd macro onto the execution stack of such. Supply the repeat count as pre-comma argument and a string pointer to the macro as post-comma argument.! q..m[..o fs tyi source"e hk' !* If an error turned off macro execution,! !* clear out macro call stack.! zj 15,0i !* Make three more words in the call stack.! -1fsback str,.-15fs word !* Fill them with string, starting index, and rpt count.! 37,.-10fs word ff"e 1' "#',.-5fs word m.m &_Run_Kbd_Macro fs tyi source 0 !^R Call Last Kbd Macro:! !^R Run the last defined temporary keyboard macro. A numeric argument is a repeat count.! f:mLast_Kbd_Macro !^R Start Kbd Macro:! !^R Begin defining keyboad macro. After you type this command, everything you type becomes part of the accumulating keyboard macro as well as being executed. Numeric arg means append to last defined keyboard macro, repeating its definition as if you typed it in from scratch.! 0 fs b cons[1 q1m.v Kbd_Macro_Defining_Body ff"n !* With arg, init it with contents! mLast_Kbd_Macro q1[..o !* of last kbd macro, sans the prefix! gLast_Kbd_Macro !* that makes it into a bona fide TECO program.! j 37d zj' m.m &_Define_Kbd_Macro fs tyi sink fsvers-751"g 1fs mode ch' 0 !& Define Kbd Macro:! !S FS TYI SINK for defining kbd macros. Takes characters typed and accumulates them in a buffer which will be formed into a keyboard macro when the definition is finished.! qKbd_Macro_Defining_Body[..o zj !* Select the buffer we use to accumulate.! /200.i &177.i 0 !* Insert the two characters for this command character.! !^R End Kbd Macro:! !^R Terminate definition of a keyboard macro. All that you have typed since starting the definition, except this command itself, becomes the definition. An argument means re-execute the macro immediately, with the argument as repeat count, counting defining the macro as once. ^R Call Last Kbd Macro re-executes the last macro defined.! fs tyi sink"e @fe nimfs err' !* Complain if not defining a macro.! 0 fs tyi sink qKbd_Macro_Defining_Body[..o zj fstyicount-(fs tyi beg)*2r .,zk j i !temp!fm(m.m&_Immediate_Kbd_Macro) !* Put command to call kbd macro at beginning.! hx* m.v Last_Kbd_Macro !* make mLast_Kbd_Macro call the macro.! Q..o( ]..o ) fs bkill !* Flush the data structures used in defining.! fsvers-751"g 1fs mode ch' -1"e 0' !* If arg is 1, we are done.! f"g-1'mLast_Kbd_Macro' !* Else, repeat appropriate number of extra times.! 0 !Name Kbd Macro:! !C Give a name to the last kbd macro defined. A function is created with the name you specify, which when invoked runs that keyboard macro. (Return means give no name.) Then, you are asked to give the command character to put the macro on. Type C-G if you don't want one. Combinations with prefix characters such as Altmode and C-X may be used. Call this function with any numeric argument to inhibit its asking for a key.! 1,f Function_name:_[1 !* Read name of MM command, and define it unless null.! fq1"g qLast_Kbd_Macro m.v MM_1' FF"E !* If user gave no numeric arg, then! @ft Put_kbd_macro_on_key:_ !* ask about a key to put it on.! m(m.m &_Read_Q-reg)[2 !* Ask what character to put it in.! @ft__Go_ahead 1m(m.m&_Yes_or_No)"e 0' qLast_Kbd_Macrou2' !* Redefine it.! 0 !View Kbd Macro:! !C Print definition of a keyboard macro. Supply a suffix string argument containing the command name. If the string argument is null, you will be asked to type the character which runs the macro.! 1,f Function_name:_[0 !* Get the string arg.! fq0"e @ftCommand_key:_ m(m.m &_Read_Q-reg)u0' !* If null, read character from terminal.! "# :i0 MM_0' !* Otherwise, attach MM to get name of variable.! q0[1 !* Get contents of string.! m.m ^R_Call_Last_Kbd-q1"e !* If it's the ^X^E command,! qLast_Kbd_Macrou1 !* get the macro it would run.! :i0Last_Kbd_Macro' f[b bind 1:< g1> !* Get the thing in a buffer.! j 6f~!temp!"n !* Prevent error in G cmd - get it here instead.! :i*Not_a_kbd_macro fs err' !* Complain if command is not a kbd macro.! j s) 0,.k !* Flush the call to & immediate kbd macro.! ft Definition_of_keyboard_macro:   m.m&_Charprint[2 j z/2< 1a*200.+(2c0a) m2 ft_> ft  0 !& Check Redefinition:! !S Verify that a certain command can be redefined. Do not allow a command to be redefined unless it is undefined, self-inserting, or a string starting with Temp. The command is specified with a q-register name in a string passed as a string pointer.! [1 q1[0 !* Q0 gets old definition.! fq0+1"g !* If old definition is a string not made by this macro,! f~(0,6:g0)!Temp!"n !* don't let user clobber useful command.! !lose! !"! :I*Can't_clobber_specified_character_1 fs err'' "# Afs^r init-q0"n 200.@ fs^rinit-q0"n !* Don't redefine built-ins except error and self-insert.! q0"n !* Undefined slots in dispatch prefix are 0.! o lose''''  !^R Kbd Macro Query:! !^R Query and continue macro execution. If called from a macro, reads a character: Space continues execution. Rubout terminates this repetition of the macro. Altmode terminates all repetitions of the macro. . terminates after the end of this repetition of the macro. ^R enters a recursive edit; when you exit, you are asked again. ^L clears the screen and asks you again. Anything else exits all macros and is reread as a command. With an argument, we unconditionally enter a recursive editing level and proceed with the macro when it is exited. This is the same as what we do with no argument if the user were to type ^R Space; but it is done even when the macro is being defined.! ff"n 0fstyi sink( !* With arg, temporarily turn of defining! 0fstyi source( !* and when re-running turn off that! [..j :i..j Kbd_Macro_Edit !* so that in either case the ^R reads things! 0 !* which are not part of the macro.! )fstyi source !* We DO NOT use F[ on these! )fstyi sink !* because an error coming out of the ^R! 0' !* must be from C-], and we want to stop in that case.! fs tyi source"e 0' !* if not from inside macro, do nothing! [0[1 QKbd_Macro_Query[..j !* Want the same string (eq) each time.! 0fstyi source( 0fs tyi sink( !* Turn off macros so we can read from kbd.! !Read! 2,m.i @:fiu1 fiu0 !* get character, really from tty! q0- "e f+ Oread' q0-"e 0 Oread' )fs tyi sink !* Pop the two flags. Don't use qreg pdl! )fs tyi source !* because we want them to remain 0 if we quit.! q0-32"e 0' !* space continues on! q0-."e 1u:..m(fq..m/5-1) 0' !* . ends after this, set rpt count to 1.! q0-"e 1u:..m(fq..m/5-1)' !* For Altmode, set rpt count to 1 so flush all repeats.! q0f:"l !* Both Rubout and Altmode end this repetition! fq:..m(fq..m/5-3)-2 !* by skipping up to the last char ! u:..m(fq..m/5-2) fi ' !* and reading it, causing macro to be popped.! 0fs tyi source !* Other characters quit macro execution! q1fs reread !* and are unread.! !Write Kbd Macro:! !C Write macro to file. Supply a suffix string argument containing the command name. If the string argument is null, you will be asked to type the character which runs the macro. The output file name will read from the terminal (default KMAC :EJ). To replay the macro, use the normal Load Library or Run Library functions.! 1,f Function_name:_ !* Get the string arg to F! QF[0 fq0"e @ftCommand_key:_ !* If null, read character from terminal.! m(m.m &_Read_Q-reg)u0' "# :i0 MM_F' !* Otherwise, attach MM to get name of variable.! q0[1 !* Get contents of string.! m.m ^R_Call_Last_Kbd-q1"e !* If it's the ^X^E command,! qLast_Kbd_Macrou1 !* get the macro it would run.! :i0Last_Kbd_Macro' f[b bind 1:< g1> !* Get the thing in a buffer.! j 6f~!temp!"n !* Prevent error in G cmd - get it here instead.! :i*Not_a_kbd_macro fs err' !* Complain if command is not a kbd macro.! f[d file 1f[ fnam syntax etKMAC_:EJ !* Default to KMAC :EJ.! 1m(m.m&_Read_Filename)Output_file[2 j m(m.mReplace_String)__ !* Space replacement.! j m(m.mReplace_String)   !* TAB replacement.! j m(m.mReplace_String)\\ !* Backslash replacement.! et2 !* Setup default filespec.! i!~Filename~:!_!Saved_keyboard_macro.!  fs d fn1:F6[3 !* Use FN1 for lib file name.! i3  14.i fqF"E Q3uF' !* Q3 contains name for macro invocation.! !* Either function name specified or! !* filename, if no function name give! !* in F.! i !:!_!F:!_!S_0_KBD_Macro.! [1 1,m(m.m&_Get_Library_Pointer)KBDMAC"e m(m.mLoad_Library)KBDMAC' @:i1\ zj i\ fm10 1,m.m &_File_PURIFY_Loaded+1"G !* Load PURIFY if not loaded already.! m(m.mLoad_Library)PURIFY' m(m.m&_Compress_Buffer) !* Compress and Purify into :EJ form.! m(m.m&_Purify_Buffer) et2 !* Get output filespec.! ei@hpef !* Write out file.! fso fileu2 !* Actual filespec written! @ft_Written:_2  0fs echo active 0 !Kbd Test End of Buffer:! !C Quit KBD macro execution if at end of buffer.! fs tyi source"n .-z"e 0fs tyi source'' !* Quit execution if at end of buffer.! 0