1 /*======================================================================*\
2 |*		Editor mined						*|
3 |*		Function key dispatcher					*|
4 \*======================================================================*/
5 
6 #include "mined.h"
7 #include "charprop.h"
8 #include "io.h"
9 
10 
11 /*======================================================================*\
12 |*	Debug function dispatching					*|
13 \*======================================================================*/
14 
15 #define dont_debug_dispatch
16 
17 #ifdef debug_dispatch
18 #define trace_dispatch(func)	do_debug_dispatch (func)
19 #else
20 #define trace_dispatch(func)
21 #endif
22 
23 #ifdef debug_dispatch
24 static
25 void
do_debug_dispatch(func)26 do_debug_dispatch (func)
27   char * func;
28 {
29   if (keyshift & applkeypad_mask) {
30 	printf ("appl-keypad ");
31   }
32   if (keyshift & ctrl_mask) {
33 	printf ("ctrl-");
34   }
35   if (keyshift & alt_mask) {
36 	printf ("alt-");
37   }
38   if (keyshift & shift_mask) {
39 	printf ("shift-");
40   }
41   printf ("%s\n", func);
42 }
43 #endif
44 
45 
46 /*======================================================================*\
47 |*	Dispatch function keys and some keypad keys to mined functions	*|
48 \*======================================================================*/
49 
50 /**
51    HOMEkey () is invoked by a Home key on the right (application) keypad;
52    function configurable
53  */
54 void
HOMEkey()55 HOMEkey ()
56 {
57   trace_dispatch ("Home");
58 
59   if (! (keyshift & altctrlshift_mask) && home_selection) {
60 	MARK ();
61 	return;
62   }
63 
64   if (shift_selection) {
65 	if (keyshift & alt_mask) {
66 		keyshift &= ~ alt_mask;
67 	} else if (apply_shift_selection) {
68 		trigger_highlight_selection ();
69 
70 		if (keyshift & ctrl_mask) {
71 			keyshift = 0;
72 			BFILE ();
73 		} else {
74 			BLINEORUP ();
75 		}
76 		return;
77 	}
78   }
79 
80 #ifdef weird_stuff_disabling_alt_in_xterm
81   if ((keyshift & (alt_mask | applkeypad_mask)) == alt_mask) {
82 	smallHOMEkey ();
83 	return;
84   }
85 #endif
86 
87   if (keyshift & shift_mask) {
88 	keyshift = 0;
89 	MARK ();
90   } else if (keyshift & ctrl_mask) {
91 	keyshift = 0;
92 	BLINEORUP ();
93   } else {
94 	if (mined_keypad == ! (keyshift & alt_mask)) {
95 		MARK ();
96 	} else {
97 		BLINEORUP ();
98 	}
99   }
100 }
101 
102 /**
103    smallHOMEkey () is invoked by a Home key on the small keypad;
104    function configurable
105  */
106 void
smallHOMEkey()107 smallHOMEkey ()
108 {
109   trace_dispatch ("smallHome");
110 
111   if (! (keyshift & altctrlshift_mask) &&
112       (small_home_selection || (shift_selection && ! mined_keypad))) {
113 	MARK ();
114 	return;
115   }
116 
117   if (shift_selection) {
118 	if (keyshift & alt_mask) {
119 		keyshift &= ~ alt_mask;
120 	} else if (apply_shift_selection) {
121 		trigger_highlight_selection ();
122 
123 		if (keyshift & ctrl_mask) {
124 			keyshift = 0;
125 			BFILE ();
126 		} else {
127 			BLINEORUP ();
128 		}
129 		return;
130 	}
131   }
132 
133   if (keyshift & shift_mask) {
134 	keyshift = 0;
135 	MARK ();
136   } else if (keyshift & ctrl_mask) {
137 	keyshift = 0;
138 	MOVLBEG ();
139   } else {
140 	if (mined_keypad == ! (keyshift & alt_mask)) {
141 		BLINEORUP ();
142 	} else {
143 		MARK ();
144 	}
145   }
146 }
147 
148 /**
149    ENDkey () is invoked by an End key on the right (application) keypad;
150    function configurable
151  */
152 void
ENDkey()153 ENDkey ()
154 {
155   trace_dispatch ("End");
156 
157   if (! (keyshift & altctrlshift_mask) && home_selection) {
158 	COPY ();
159 	return;
160   }
161 
162   if (shift_selection) {
163 	if (keyshift & alt_mask) {
164 		keyshift &= ~ alt_mask;
165 	} else if (apply_shift_selection) {
166 		trigger_highlight_selection ();
167 
168 		if (keyshift & ctrl_mask) {
169 			keyshift = 0;
170 			EFILE ();
171 		} else {
172 			ELINEORDN ();
173 		}
174 		return;
175 	}
176   }
177 
178 #ifdef weird_stuff_disabling_alt_in_xterm
179   if ((keyshift & (alt_mask | applkeypad_mask)) == alt_mask) {
180 	smallENDkey ();
181 	return;
182   }
183 #endif
184 
185   if (keyshift & shift_mask) {
186 	keyshift = 0;
187 	COPY ();
188   } else if (keyshift & ctrl_mask) {
189 	keyshift = 0;
190 	ELINEORDN ();
191   } else {
192 	if (mined_keypad == ! (keyshift & alt_mask)) {
193 		COPY ();
194 	} else {
195 		ELINEORDN ();
196 	}
197   }
198 }
199 
200 /**
201    smallENDkey () is invoked by an End key on the small keypad;
202    function configurable
203  */
204 void
smallENDkey()205 smallENDkey ()
206 {
207   trace_dispatch ("smallEnd");
208 
209   if (! (keyshift & altctrlshift_mask) &&
210       (small_home_selection || (shift_selection && ! mined_keypad))) {
211 	COPY ();
212 	return;
213   }
214 
215   if (shift_selection) {
216 	if (keyshift & alt_mask) {
217 		keyshift &= ~ alt_mask;
218 	} else if (apply_shift_selection) {
219 		trigger_highlight_selection ();
220 
221 		if (keyshift & ctrl_mask) {
222 			keyshift = 0;
223 			EFILE ();
224 		} else {
225 			ELINEORDN ();
226 		}
227 		return;
228 	}
229   }
230 
231   if (keyshift & shift_mask) {
232 	keyshift = 0;
233 	COPY ();
234   } else if (keyshift & ctrl_mask) {
235 	keyshift = 0;
236 	MOVLEND ();
237   } else {
238 	if (mined_keypad == ! (keyshift & alt_mask)) {
239 		ELINEORDN ();
240 	} else {
241 		COPY ();
242 	}
243   }
244 }
245 
246 
247 #define not_alt_del_always_deletes_char
248 #define alt_alt_may_delete_char
249 
250 #ifdef alt_del_always_deletes_char
251 #define cut_cond (mined_keypad && ! (keyshift & (alt_mask | applkeypad_mask)))
252 #else
253 #ifdef alt_alt_may_delete_char
254 #define cut_cond (mined_keypad == ! (keyshift & alt_mask))
255 #else
256 #define cut_cond (mined_keypad == ! ((keyshift & (alt_mask | applkeypad_mask)) == (alt_mask | applkeypad_mask)))
257 #endif
258 #endif
259 
260 /**
261    DELkey () is invoked by a Delete function key,
262    often KP_Delete on right keypad;
263    function configurable
264  */
265 void
DELkey()266 DELkey ()
267 {
268   trace_dispatch ("Delete");
269 
270   if ((keyshift & ctrl_mask) && ! (keyshift & alt_mask)) {
271 	/*keyshift = 0;*/
272 	DCC ();
273   } else if ((keyshift & shift_mask) && ! (keyshift & alt_mask)) {
274 	keyshift = 0;
275 	CUT ();
276   } else {
277 #ifdef oldstyle_DELkey
278 	/*
279 	mined_keypad alt_mask applkeypad_mask	alt-KP	alt-DEL	alt-ALT
280 		1	0	0		cut !	cut	cut
281 		1	0	1		*	*	*
282 		1	1	0		cut	del	del
283 		1	1	1		del	del	del
284 		0	0	0		del !	del	del
285 		0	0	1		*	*	*
286 		0	1	0		del	del	cut
287 		0	1	1		cut	del	cut
288 	*/
289 	if (cut_cond) {
290 		CUT ();
291 	} else {
292 		DCC ();
293 	}
294 #else
295 	if (has_active_selection ()) {
296 		if (keyshift & alt_mask) {
297 			DCC ();
298 		} else {
299 			CUT ();
300 		}
301 	} else {
302 		if (keyshift & alt_mask) {
303 			CUT ();
304 		} else {
305 			DCC ();
306 		}
307 	}
308 #endif
309   }
310 }
311 
312 /**
313    smallDELkey () is invoked by the small keypad ("extended") Delete key
314  */
315 void
smallDELkey()316 smallDELkey ()
317 {
318   trace_dispatch ("smallDelete");
319 
320   keyshift |= ctrl_mask;
321   DELkey ();
322 }
323 
324 /**
325    DELchar () is invoked by a DEL character unless reconfigured to
326    map to DPC;
327    the key may be a Delete function key (on small keypad, if so configured),
328    or the Backarrow key (unless configured to emit the BackSpace character)
329  */
330 void
DELchar()331 DELchar ()
332 {
333   trace_dispatch ("DEL");
334 
335   if (keyshift & shift_mask) {
336 	keyshift = 0;
337 	CUT ();
338   } else if (keyshift & ctrl_mask) {
339 	DCC ();
340   } else {
341 	if (hop_flag > 0 || keyshift & alt_mask) {
342 		hop_flag = 0;
343 		CUT ();
344 	} else {
345 		DCC ();
346 	}
347   }
348 }
349 
350 void
INSkey()351 INSkey ()
352 {
353   trace_dispatch ("Insert");
354 
355   if (! hop_flag && (keyshift & alt_mask)) {
356 	keyshift = 0;
357 	YANKRING ();
358   } else if (keyshift & ctrl_mask) {
359 	PASTEstay ();
360   } else {
361 	PASTE ();
362   }
363 }
364 
365 /**
366    smallINSkey () is invoked by the small keypad ("extended") Insert key
367  */
368 void
smallINSkey()369 smallINSkey ()
370 {
371   trace_dispatch ("smallInsert");
372 
373   keyshift |= ctrl_mask;
374   INSkey ();
375 }
376 
377 
378 /**
379    Keypad keys with special functions
380  */
381 void
KP_plus()382 KP_plus ()
383 {
384   if (keyshift & (ctrl_mask | shift_mask)) {
385 	if ((keyshift & altctrlshift_mask) == altctrlshift_mask) {
386 		screenmorecols ();
387 	} else if ((keyshift & altctrlshift_mask) == ctrlshift_mask) {
388 		screenmorelines ();
389 	} else {
390 		fontbigger ();
391 	}
392   } else {
393 	SD ();
394   }
395 }
396 
397 void
KP_minus()398 KP_minus ()
399 {
400   if (keyshift & (ctrl_mask | shift_mask)) {
401 	if ((keyshift & altctrlshift_mask) == altctrlshift_mask) {
402 		screenlesscols ();
403 	} else if ((keyshift & altctrlshift_mask) == ctrlshift_mask) {
404 		screenlesslines ();
405 	} else {
406 		fontsmaller ();
407 	}
408   } else {
409 	SU ();
410   }
411 }
412 
413 /**
414    Display function key help lines
415  */
416 static voidfunc fhelp_func = F1;
417 static char fhelp_keyshift = 0;
418 
419 void
display_FHELP()420 display_FHELP ()
421 {
422 /* use the following screen attributes:
423 			unicode indicator (cyan background)
424 			highlight (red background)
425 			reverse
426 			plain (normal)
427 */
428   if (fhelp_func == COMPOSE) {
429 	status_uni ("ctrl- , ¸/ͺ . ˙/῾ :/\" ¨ ; ˛ ' ´tonos ´ oxia - ¯ <  ̌/᾿ ( ˘ )  ̑ & hook ^ ~ ` / °");
430   } else if (fhelp_func == key_1) {
431     if (fhelp_keyshift == altctrl_mask) {
432 	status_uni ("alt-ctrl- 1 ˘/ ̛ + ´ 2 ˘/ ̛ + ` 3 ˘/ ̛ +  ̉ 4 ˘/ ̛ + ~ 5 ˘/ ̛ +  ̣ 6 ῞ 7 ῝ 8 ῟ 9  0 ");
433     } else if (fhelp_keyshift == ctrl_mask) {
434 	status_uni ("ctrl- 1 ´ 2 ` 3  ̉ 4 ~ 5  ̣ 6 ^ 7 ˘ 8  ̛ 9 / 0 °");
435     } else {
436 	status_uni ("alt- 1 ^ + ´ 2 ^ + ` 3 ^ +  ̉ 4 ^ + ~ 5 ^ +  ̣ 6 ῎ 7 ῍ 8 ῏ 9  0 ");
437     }
438   } else {
439     if ((fhelp_keyshift & altctrlshift_mask) == altshift_mask) {
440 	status_uni ("alt-shift-               F4 Kill window! F5 ˘ F6 ˙");
441     } else if ((fhelp_keyshift & altctrlshift_mask) == ctrlshift_mask) {
442 	status_uni ("ctrl-shift-                         F5 ˛ F6 ¯    F8 LineRepl F9 FindChar");
443     } else if (fhelp_keyshift & alt_mask) {
444 	status_uni ("alt-   F2 SaveAs    F4 Kill window!       F8 SearchBack F9 NextBack F10 FlagMenu");
445     } else if (fhelp_keyshift & ctrl_mask) {
446 	status_uni ("ctrl- F2 SaveTo  F4 PastePrev F5 °/¸ F6 ^  F8 Subst/Confirm  F10 InfoMenu");
447     } else if (fhelp_keyshift & shift_mask) {
448 	status_uni ("shift- F2 Save! F3 CaseTog F4 WrtBuf F5 ~ F6 `  F8 Subst F9 FindIdf F10 Menu");
449     } else {
450 	status_uni ("       F2 Save F3 Edit F4 InsFile F5 \" F6 ´  F8 Search F9 Next F10 Filemenu");
451     }
452   }
453 }
454 
455 void
toggle_FHELP()456 toggle_FHELP ()
457 {
458   always_disp_help = ! always_disp_help;
459 }
460 
461 void
FHELP(func)462 FHELP (func)
463   voidfunc func;
464 {
465   fhelp_func = func;
466   fhelp_keyshift = keyshift;
467   if (hop_flag > 0) {
468 	hop_flag = 0;
469 	always_disp_help = ! always_disp_help;
470   } else {
471 	display_FHELP ();
472   }
473 }
474 
475 
476 void
F1()477 F1 ()
478 {
479   trace_dispatch ("F1");
480 
481   if (keyshift & altctrlshift_mask) {
482 	FHELP (F1);
483   } else {
484 	HELP ();
485   }
486 }
487 
488 void
F2()489 F2 ()
490 {
491   trace_dispatch ("F2");
492 
493   if (keyshift & ctrl_mask) {
494 	/* enforce creation of file info file */
495 	hop_flag = 1;
496   }
497 
498   if (keyshift & alt_mask) {
499 	keyshift = 0;
500 	SAVEAS ();
501   } else if (keyshift & shift_mask) {
502 	keyshift = 0;
503 	WTU ();
504   } else if (keyshift & ctrl_mask) {
505 	keyshift = 0;
506 	EXED ();
507   } else {
508 	WT ();
509   }
510 }
511 
512 void
F3()513 F3 ()
514 {
515   trace_dispatch ("F3");
516 
517   if (keyshift & alt_mask) {
518 	keyshift = 0;
519 	SELECTFILE ();
520   } else if (keyshift & ctrl_mask) {
521 	keyshift = 0;
522 	VIEW ();
523   } else if (keyshift & shift_mask) {
524 	keyshift = 0;
525 	LOWCAPWORD ();
526   } else {
527 	EDIT ();
528   }
529 }
530 
531 void
F4()532 F4 ()
533 {
534   trace_dispatch ("F4");
535 
536   if (keyshift & shift_mask) {
537 	keyshift = 0;
538 	WB ();
539   } else if (keyshift & ctrl_mask) {
540 	keyshift = 0;
541 	YANKRING ();
542   } else {
543 	INSFILE ();
544   }
545 }
546 
547 #ifdef dispatch_accents
548 /* accent prefix keys are now handled generically in edit.c:
549   insert_prefix (F5);
550   insert_prefix (F6);
551  */
552 void
F5()553 F5 ()
554 {
555   trace_dispatch ("F5");
556 
557   /*	standard function key assignment:
558 		F5	F6
559 		"	´
560 	shift	~	`
561 	ctrl	°	^
562   */
563   if (keyshift & ctrl_mask) {
564 	keyshift = 0;
565 	insert_angstrom ();
566   } else if (keyshift & shift_mask) {
567 	keyshift = 0;
568 	insert_tilde ();
569   } else {
570 	insert_diaeresis ();
571   }
572 }
573 
574 void
F6()575 F6 ()
576 {
577   trace_dispatch ("F6");
578 
579   /*	standard function key assignment:
580 		F5	F6
581 		"	´
582 	shift	~	`
583 	ctrl	°	^
584   */
585   if (keyshift & ctrl_mask) {
586 	keyshift = 0;
587 	insert_circumflex ();
588   } else if (keyshift & shift_mask) {
589 	keyshift = 0;
590 	insert_grave ();
591   } else {
592 	insert_acute ();
593   }
594 }
595 #endif
596 
597 void
FIND()598 FIND ()
599 {
600   if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
601 	keyshift = 0;
602 	if (hop_flag > 0) {
603 		Stag ();
604 	} else {
605 		LR ();
606 	}
607   } else if ((keyshift & altctrl_mask) == altctrl_mask) {
608 	keyshift = 0;
609 	if (hop_flag > 0) {
610 		hop_flag = 0;
611 		SCURCHAR (REVERSE);
612 	} else {
613 		REPL ();
614 	}
615   } else if (keyshift & ctrl_mask) {
616 	keyshift = 0;
617 	if (hop_flag > 0) {
618 		hop_flag = 0;
619 		SCURCHAR (FORWARD);
620 	} else {
621 		REPL ();
622 	}
623   } else if (keyshift & shift_mask) {
624 	keyshift = 0;
625 	if (hop_flag > 0) {
626 		hop_flag = 0;
627 		Stag ();
628 	} else {
629 		GR ();
630 	}
631   } else if (keyshift & alt_mask) {
632 	keyshift = 0;
633 	SRV ();
634   } else {
635 	SFW ();
636   }
637 }
638 
639 void
AGAIN()640 AGAIN ()
641 {
642   if ((keyshift & altctrlshift_mask) == altctrlshift_mask) {
643 	keyshift = 0;
644 	SCURCHAR (REVERSE);
645   } else if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
646 	keyshift = 0;
647 	SCURCHAR (FORWARD);
648   } else if ((keyshift & altshift_mask) == altshift_mask) {
649 	keyshift = 0;
650 	SIDF (REVERSE);
651   } else if (keyshift & shift_mask) {
652 	keyshift = 0;
653 	SIDFW ();
654   } else if (keyshift & alt_mask) {
655 	RESreverse ();
656   } else {
657 	RES ();
658   }
659 }
660 
661 void
F7()662 F7 ()
663 {
664   trace_dispatch ("F7");
665 
666   FIND ();
667 }
668 
669 void
F8()670 F8 ()
671 {
672   trace_dispatch ("F8");
673 
674   FIND ();
675 }
676 
677 void
F9()678 F9 ()
679 {
680   trace_dispatch ("F9");
681 
682   AGAIN ();
683 }
684 
685 void
F10()686 F10 ()
687 {
688   trace_dispatch ("F10");
689 
690   if (keyshift & alt_mask) {
691 	keyshift = 0;
692 	handleFlagmenus ();
693   } else if (keyshift & shift_mask) {
694 	keyshift = 0;
695 	QUICKMENU ();
696   } else if (keyshift & ctrl_mask) {
697 	keyshift = 0;
698 	handleFlagmenus ();
699   } else {
700 	FILEMENU ();
701   }
702 }
703 
704 void
F11()705 F11 ()
706 {
707   trace_dispatch ("F11");
708 
709   if (keyshift & alt_mask) {
710 	if (keyshift & shift_mask) {
711 		hop_flag = 1;
712 	}
713 	search_wrong_enc ();
714   } else if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
715 	hop_flag = 1;
716 	changeuni ();
717   } else if (keyshift & shift_mask) {
718 	keyshift = 0;
719 	hop_flag = 1;
720 	LOWCAP ();
721   } else if (keyshift & ctrl_mask) {
722 	UML (' ');
723   } else {
724 	LOWCAP ();
725   }
726 }
727 
728 void
F12()729 F12 ()
730 {
731   trace_dispatch ("F12");
732 
733   if (keyshift & alt_mask) {
734 	if (keyshift & ctrl_mask) {
735 		toggleKEYMAP ();
736 	} else {
737 		switchAltScreen ();
738 	}
739   } else if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
740 	SAVPOS ();
741   } else if (keyshift & ctrl_mask) {
742 	setupKEYMAP ();
743   } else if (keyshift & shift_mask) {
744 	KEYREC ();
745   } else {
746 	KEYREC ();
747   }
748 }
749 
750 
751 /*======================================================================*\
752 |*			Generic command processing functions		*|
753 \*======================================================================*/
754 
755 /*
756  * return the mined command associated with the key value
757  */
758 voidfunc
command(c)759 command (c)
760   unsigned long c;
761 {
762   if (c == FUNcmd) {
763 	return keyproc;
764   } else if (/* c >= 0 && */ c < arrlen (key_map)) {
765 	return key_map [c];
766   } else {
767 	return Scharacter;
768   }
769 }
770 
771 
772 /**
773    Invoke function associated with the key.
774  */
775 void
invoke_key_function(key)776 invoke_key_function (key)
777   unsigned long key;
778 {
779   (command (key)) (key);
780 }
781 
782 
783 /*
784  * BAD complains about unknown command characters.
785  */
786 static
787 void
BAD(c,tag)788 BAD (c, tag)
789   unsigned long c;
790   char * tag;
791 {
792   char cmdbuf [34];
793   char * cbuf = cmdbuf;
794 
795   strcpy (cmdbuf, "Unknown command: ");
796   if (tag) {
797 	strcat (cmdbuf, tag);
798   }
799   while (* cbuf) {
800 	cbuf ++;
801   }
802 
803   if (no_char (c)) {
804 	strcpy (cbuf, "<unknown character>");
805   } else if (c < ' ') {
806 	cbuf [0] = '^';
807 	cbuf [1] = c + '@';
808 	cbuf [2] = '\0';
809   } else {
810 	(void) utfencode (c, cbuf);
811   }
812 
813   ring_bell ();
814   status_uni (cmdbuf);
815 }
816 
817 void
BADch(c)818 BADch (c)
819   unsigned long c;
820 {
821   BAD (c, "");
822 }
823 
824 /*
825  * Ignore this keystroke.
826  */
827 void
I()828 I ()
829 {
830 }
831 
832 /*
833  * 'HOP' key function expander.
834  */
835 void
HOP()836 HOP ()
837 {
838   if (shift_selection && (keyshift & shift_mask)) {
839 	keyshift &= ~ shift_mask;
840 	COPY ();
841 	return;
842   }
843 
844   hop_flag = 2;
845   if (MENU) {
846 	displayflags ();
847 	set_cursor_xy ();
848 	flush ();
849   }
850   if (! char_ready_within (500, NIL_PTR)) {
851 	status_uni ("HOP: type command (to amplify/expand) ...");
852   }
853 }
854 
855 /*
856  * Cancel prefix function.
857  */
858 void
CANCEL()859 CANCEL ()
860 {
861   hop_flag = 0;
862   clear_status ();
863 }
864 
865 
866 /**
867    Read ANSI escape sequence that has been started with ESC [
868    (but too slowly to be recognised on keyboard input level);
869    swallow [, ? or > (for reports), 0-9, and ; characters
870    plus final letter, @ or ~
871  */
872 static
873 void
CSI()874 CSI ()
875 {
876   character c;
877   FLAG msg_shown = False;
878 
879   if (stat_visible) {
880 	ring_bell ();
881   } else {
882 	status_uni ("... absorbing delayed terminal escape sequence ... - Press Enter to abort");
883 	msg_shown = True;
884   }
885   flush ();
886 
887   while ((c = read1byte ()) == '[' || c == '?' || c == '>' || c == ';'
888 	|| (c >= '0' && c <= '9')) {
889   }
890 
891   if (msg_shown) {
892 	error ("(Discarded slow escape sequence) - Re-enter function key");
893   }
894 }
895 
896 /**
897    Read escape sequence that has been started with ESC O
898    (but too slowly to be recognised on keyboard input level);
899    swallow 0-9, and ; characters plus final character
900  */
901 static
902 void
ESCO()903 ESCO ()
904 {
905   character c;
906   FLAG msg_shown = False;
907 
908   if (stat_visible) {
909 	ring_bell ();
910   } else {
911 	status_uni ("... Absorbing delayed terminal escape sequence ... - Press Enter to abort");
912 	msg_shown = True;
913   }
914   flush ();
915 
916   while ((c = read1byte ()) == ';' || (c >= '0' && c <= '9')) {
917   }
918 
919   if (msg_shown) {
920 	error ("(Discarded slow escape sequence) - Re-enter function key");
921   }
922 }
923 
924 /**
925    Read terminal report string which has been started with ESC ]
926  */
927 void
OSC()928 OSC ()
929 {
930   character c;
931   FLAG msg_shown = False;
932 
933   if (stat_visible) {
934 	ring_bell ();
935   } else {
936 	status_uni ("... Absorbing delayed terminal report string ... - Press Enter to abort");
937 	msg_shown = True;
938   }
939   flush ();
940 
941   while ((c = read1byte ()) >= ' ') {
942   }
943   if (c == '\033') {
944 	read1byte ();	/* '\\' */
945   }
946 
947   if (msg_shown) {
948 	clear_status ();
949   }
950 }
951 
952 
953 #define cmd_char(c)	(c < '\040' ? c + '\100' : (c >= '\140' ? c - '\040' : c))
954 
955 /*
956  * Interpret control-Q commands. Most can be implemented with the Hop function.
957  */
958 void
ctrlQ()959 ctrlQ ()
960 {
961   unsigned long c;
962   voidfunc func;
963 
964   if (! char_ready_within (500, NIL_PTR)) {
965 	status_uni ("^Q: blockBegin Find replAce goto<n>mark HOP...");
966   }
967   if (quit) {
968 	return;
969   }
970 
971   c = readcharacter_unicode ();
972   if (quit) {
973 	return;
974   }
975 
976   clear_status ();
977   if ('0' <= c && c <= '9') {
978 	GOMAn ((int) c - (int) '0');
979 	return;
980   }
981   if (c == '\033' || c == quit_char) {
982 	CANCEL ();
983 	return;
984   }
985   switch (cmd_char (c)) {
986 	case 'B' : {GOMA () ; return;}
987 	case 'K' : { ; return;}		/* not exactly WS function */
988 	case 'P' : { ; return;}		/* not exactly WS function */
989 	case 'V' : { ; return;}		/* not exactly WS function */
990 	case 'W' :			/* not exactly WS function */
991 	case 'Z' :			/* not exactly WS function */
992 	case 'Y' :
993 	case '\177' : {
994 			func = command (c);
995 			hop_flag = 1;
996 			(* func) (c);
997 			return;
998 		      }
999 	case 'F' : {if (hop_flag > 0) {
1000 			SRV ();
1001 		    } else {
1002 			SFW ();
1003 		    }
1004 		    return;
1005 		   }
1006 	case 'A' : {if (hop_flag > 0) {
1007 			REPL ();
1008 		    } else {
1009 			GR ();
1010 		    }
1011 		    return;
1012 		   }
1013 	case 'Q' : {REPT (' '); return;}	/* not exactly WS function */
1014 	case 'L' :			/* not exactly WS function */
1015 /*
1016 ^Q: B/K top/bottom block
1017     P last position
1018     W/Z continuous scroll
1019     V last find or block
1020     Y/DEL delete line right/left
1021     0-9 marker
1022     F find
1023     A replace
1024     Q repeat next key/command
1025     L find misspelling
1026 */
1027 	default : {
1028 		func = command (c);
1029 		if (func != Scharacter) {
1030 			hop_flag = 1;
1031 			keyshift |= alt_mask;
1032 			(* func) (c);
1033 		} else {
1034 			BAD (c, "^Q ");
1035 		}
1036 		return;
1037 	}
1038   }
1039 }
1040 
1041 /*
1042  * Interpret control-K commands.
1043  */
1044 void
ctrlK()1045 ctrlK ()
1046 {
1047   unsigned long c;
1048 
1049   if (! char_ready_within (500, NIL_PTR)) {
1050 	status_msg ("^K: Save Done eXit Quit Read Log <n>mark / block: B/K mark Cop Ydel moV Wr...");
1051   }
1052   if (quit) {
1053 	return;
1054   }
1055 
1056   c = readcharacter_unicode ();
1057   if (quit) {
1058 	return;
1059   }
1060 
1061   clear_status ();
1062   if ('0' <= c && c <= '9') {
1063 	MARKn ((int) c - (int) '0');
1064 	return;
1065   }
1066   if (c == '\033' || c == quit_char) {
1067 	CANCEL ();
1068 	return;
1069   }
1070   switch (cmd_char (c)) {
1071 	case 'S' : {WTU (); return;}
1072 	case 'D' : {EXFILE (); return;}
1073 	case 'X' : {EXMINED (); return;}
1074 	case 'Q' : {QUED (); return;}
1075 	case 'B' : {setMARK (True) ; return;}
1076 	case 'K' : {COPY () ; return;}	/* not exactly WS function */
1077 	case 'H' : { ; return;}		/* not exactly WS function */
1078 	case 'C' : {PASTE () ; return;}	/* not exactly WS function */
1079 	case 'Y' : {CUT () ; return;}	/* not exactly WS function */
1080 	case 'V' : {PASTE (); return;}	/* not exactly WS function */
1081 	case 'W' : {WB (); return;}	/* not exactly WS function */
1082 	case 'N' : { ; return;}		/* not exactly WS function */
1083 	case 'R' : {INSFILE (); return;}
1084 	case 'L' : {CHDI (); return;}
1085 /*
1086 ^K  0-9 set/hide marker
1087     B/K block begin/end
1088     H block hide
1089     C/Y/V/W block copy/delete/move/write
1090     N column block
1091 */
1092 	default : {
1093 		BAD (c, "^K ");
1094 		return;
1095 	}
1096   }
1097 }
1098 
1099 /*
1100  * Interpret control-O commands.
1101  */
1102 void
ctrlO()1103 ctrlO ()
1104 {
1105   unsigned long c;
1106 
1107   if (! char_ready_within (500, NIL_PTR)) {
1108 	status_msg ("^O: L/R left/right margins...");
1109   }
1110   if (quit) {
1111 	return;
1112   }
1113 
1114   c = readcharacter_unicode ();
1115   if (quit) {
1116 	return;
1117   }
1118 
1119   clear_status ();
1120   if ('0' <= c && c <= '9') {
1121 	return;
1122   }
1123   if (c == '\033' || c == quit_char) {
1124 	CANCEL ();
1125 	return;
1126   }
1127   switch (cmd_char (c)) {
1128 	case 'L' : {ADJLM (); return;}
1129 	case 'R' : {ADJRM (); return;}
1130 	case 'G' : {ADJFLM () /* actually paragraph tab */; return;}
1131 /*
1132 ^O  L/R/M set left/right margin /release
1133     I/N set/clear tab
1134     F ruler from line
1135     C center line
1136     S set line spacing
1137     W toggle word wrap
1138     T toggle ruler line
1139     J toggle justify
1140     V     vari-tabs
1141     H     hyph-help
1142     E     soft hyph
1143     D     print display
1144     P     page break
1145 */
1146 	default : {
1147 		BAD (c, "^O ");
1148 		return;
1149 	}
1150   }
1151 }
1152 
1153 /*
1154  * Set marker / go to marker.
1155  */
1156 void
MARKER()1157 MARKER ()
1158 {
1159   unsigned long c;
1160 
1161   status_msg ("0..9: set marker / , or blank: default marker");
1162   c = readcharacter_unicode ();
1163   if (quit) {
1164 	return;
1165   }
1166 
1167   clear_status ();
1168   if (c == '\033' || c == quit_char) {
1169 	CANCEL ();
1170   } else if ('0' <= c && c <= '9') {
1171 	MARKn ((int) c - (int) '0');
1172   } else if ('a' <= c && c <= 'f') {
1173 	MARKn ((int) c - (int) 'a' + 10);
1174   } else if (c == ',' || c == '\'' || c == ' ' || c == ']' || c == '\035') {
1175 	setMARK (True);
1176   } else {
1177 	BAD (c, "mark ");
1178   }
1179 }
1180 
1181 void
GOMARKER()1182 GOMARKER ()
1183 {
1184   unsigned long c;
1185 
1186   status_msg ("0..9: go marker / blank: default marker");
1187   c = readcharacter_unicode ();
1188   if (quit) {
1189 	return;
1190   }
1191 
1192   clear_status ();
1193   if (c == '\033' || c == quit_char) {
1194 	CANCEL ();
1195   } else if ('0' <= c && c <= '9') {
1196 	GOMAn ((int) c - (int) '0');
1197   } else if ('a' <= c && c <= 'f') {
1198 	GOMAn ((int) c - (int) 'a' + 10);
1199   } else if (c == ',' || c == '.' || c == 'g' || c == 'G' || c == '\'' ||
1200       c == ' ' || c == ']' || c == '\035') {
1201 	GOMA ();
1202   } else {
1203 	BAD (c, "go mark ");
1204   }
1205 }
1206 
1207 
1208 /*
1209    Toggle TAB width.
1210  */
1211 void
toggle_tabsize()1212 toggle_tabsize ()
1213 {
1214   static int prev_tabsize = 2;
1215 
1216   if (hop_flag > 0) {
1217 	toggle_tab_expansion ();
1218 	return;
1219   }
1220 
1221   if (tabsize == 2 || tabsize == 8) {
1222 	prev_tabsize = tabsize;
1223 	tabsize = 4;
1224   } else if (prev_tabsize == 8) {
1225 	tabsize = 2;
1226   } else {
1227 	tabsize = 8;
1228   }
1229   RDwin ();
1230 }
1231 
1232 /*
1233  * Interpret Escape commands.
1234  */
1235 void
ESCAPE()1236 ESCAPE ()
1237 {
1238   unsigned long c;
1239   voidfunc func;
1240   FLAG quick_alt = False;
1241 
1242   if (char_ready_within (50, NIL_PTR)) {
1243 	/* try to distinguish Alt- from explicit ESC */
1244 	quick_alt = True;
1245   }
1246   if (! char_ready_within (450, NIL_PTR)) {
1247 	status_uni ("ESCexit SPACEmenu quit /search \\backw (match replace goto justify =rept help ...");
1248   }
1249   if (quit) {
1250 	return;
1251   }
1252 
1253   c = readcharacter_unicode ();
1254   if (quit) {
1255 	return;
1256   }
1257 
1258   clear_status ();
1259 
1260   if ('0' <= c && c <= '9') {
1261 	REPT (c);
1262 	return;
1263   }
1264 
1265   trace_keytrack ("ESCAPE", c);
1266   switch (c) {
1267 	case '[' : {CSI (); return;}
1268 	case 'O' : {ESCO (); return;}
1269 	case ']' : {OSC (); return;}
1270 	case '\'' :
1271 		{
1272 			if (quick_alt) { /* detect Alt-' vs. ESC ' */
1273 				break;
1274 			}
1275 			if (keyshift & alt_mask) {
1276 				break;
1277 			} else {
1278 				GOMARKER (); return;
1279 			}
1280 		}
1281 	case '\033' : {EXED (); return;}
1282 	case '\r' :
1283 	case '\n' :
1284 		if (keyshift & ctrlshift_mask) {
1285 			break;
1286 		} else {
1287 			Popmark ();
1288 			return;
1289 		}
1290 	case '.' :
1291 		{
1292 			show_vt100_graph = False;
1293 			if (hop_flag > 0) {
1294 				RDcenter ();
1295 			} else {
1296 				RDwin ();
1297 			}
1298 			return;
1299 		}
1300 	case 'q' : {QUED (); return;}
1301 	case '/' : {SFW (); return;}
1302 	case '\\' : {SRV (); return;}
1303 	case 'R' : {LR (); return;}
1304 	case 'r' : {REPL (); return;}
1305 	case 'w' : {WT (); return;}
1306 	case 'W' : {WTU (); return;}
1307 	case 'v' : {VIEW (); return;}
1308 	case 'V' : {toggle_VIEWmode (); return;}
1309 	case 'g' : {GOTO (); return;}
1310 	case 'h' : {HELP (); return;}
1311 	case '?' : {FS (); return;}
1312 	case 'm' : {MARKER (); return;}
1313 	case 'i' : {INSFILE (); return;}
1314 	case 'b' : {WB (); return;}
1315 	case '=' : {REPT (' '); return;}
1316 	case 'z' : {SUSP (); return;}
1317 	case 'd' : {CHDI (); return;}
1318 	case '!' : {SH (); return;}
1319 	case '@' :
1320 	case '^' : {setMARK (True); return;}
1321 	case 'n' : {NN (); return;}
1322 	case 'c' : {CMD (); return;}
1323 	case 'u' : {display_code (); return;}
1324 	case 'U' : {changeuni (); return;}
1325 	case 'X' : {changehex (); return;}
1326 	case 'A' : {changeoct (); return;}
1327 	case 'D' : {changedec (); return;}
1328 	case '+' : {NXTFILE (); return;}
1329 	case '-' : {PRVFILE (); return;}
1330 	case '#' : {SELECTFILE (); return;}
1331 	case '%' : {screensmaller (); return;}
1332 	case '&' : {screenbigger (); return;}
1333 	case 'l' : {screenlesslines (); return;}
1334 	case 'L' : {screenmorelines (); return;}
1335 	case 'J' : {JUS (); return;}
1336 	case 'j' : {JUSclever (); return;}
1337 	case '<' : {ADJLM (); return;}
1338 	case ';' : {ADJFLM (); return;}
1339 	case ':' : {ADJNLM (); return;}
1340 	case '>' : {ADJRM (); return;}
1341 	case 'P' : {ADJPAGELEN (); return;}
1342 	case 'T' : {toggle_tabsize (); return;}
1343 	case 'H' : {HTML (); return;}
1344 	case 'x' : {AltX (); return;}
1345 	case (character) '�' :	/* ä German */
1346 	case (character) '�' :	/* ö German */
1347 	case (character) '�' :	/* ü German */
1348 	case (character) '�' :	/* ß German */
1349 			{UML ('g'); return;}
1350 	case (character) '�' :	/* é French */
1351 	case (character) '�' :	/* è French */
1352 	case (character) '�' :	/* à French */
1353 	case (character) '�' :	/* ù French */
1354 	case (character) '�' :	/* ç French */
1355 	case (character) '�' :	/* ² French */
1356 			{UML ('f'); return;}
1357 	case (character) '�' :	/* æ Scandinavian */
1358 	case (character) '�' :	/* å Scandinavian */
1359 	case (character) '�' :	/* ø Scandinavian */
1360 			{UML ('d'); return;}
1361 	case (character) '�' :	/* ì Italian */
1362 	case (character) '�' :	/* ò Italian */
1363 			{UML ('i'); return;}
1364 	case 0x0142 :	/* ł Polish/Baltic */
1365 	case 0x0144 :	/* ń Polish/Baltic */
1366 	case 0x017A :	/* ź Polish/Baltic */
1367 	case 0x0107 :	/* ć Polish/Baltic/Croatian/Slovenian */
1368 			{UML ('e'); return;}
1369 	case 0x016F :	/* ů Slovak */
1370 	case 0x0151 :	/* ő Hungarian */
1371 	case 0x0171 :	/* ű Hungarian */
1372 	case 0x010D :	/* č Croatian/Slovenian */
1373 	case 0x017E :	/* ž Croatian/Slovenian */
1374 	case 0x0161 :	/* š Croatian/Slovenian */
1375 	case 0x0111 :	/* đ Croatian/Slovenian/Vietnamese */
1376 	case 0x01A1 :	/* ơ Vietnamese */
1377 	case 0x01B0 :	/* ư Vietnamese */
1378 			{UML ('e'); return;}
1379 	case '_' :		/* generic */
1380 	case (character) '�' :	/* ° Norwegian */
1381 	case (character) '�' :	/* ª Portuguese */
1382 	case (character) '�' :	/* º Portuguese */
1383 	case (character) '�' :	/* « Portuguese */
1384 	case (character) '�' :	/* ñ Spanish */
1385 	case 0x011F :		/* ğ Turkish */
1386 	case 0x015F :		/* ş Turkish */
1387 	case 0x0131 :		/* ı Turkish */
1388 			{UML (language_tag); return;}
1389 	case 'C' : {LOWCAP (); return;}
1390 	case '(' :
1391 	case '{' : {SCORR (REVERSE); return;}
1392 	case ')' :
1393 	case '}' : {SCORR (FORWARD); return;}
1394 	case 't' : {Stag (); return;}
1395 	case 'a' : {toggle_append (); return;}
1396 	case 'k' : {toggleKEYMAP (); return;}
1397 	case 'I' :
1398 	case 'K' : {setupKEYMAP (); return;}
1399 	case 'Q' : if (smart_quotes) {
1400 			handleQuotemenu ();
1401 			displayflags ();
1402 		   } else {
1403 			error ("Smart quotes not enabled");
1404 		   }
1405 		   return;
1406 	case 'E' : if (hop_flag > 0) {
1407 			toggle_encoding ();
1408 		   } else {
1409 			handleEncodingmenu ();
1410 		   }
1411 		   return;
1412 	case ' ' : {QUICKMENU (); return;}
1413 	case 'f' : {FILEMENU (); return;}
1414 	case 'e' : {EDITMENU (); return;}
1415 	case 's' : {SEARCHMENU (); return;}
1416 	case 'p' : {PARAMENU (); return;}
1417 	case 'o' : {OPTIONSMENU (); return;}
1418 	case ',' : {GR (); return;}
1419 /*
1420 	case 'e' : {EDIT (); return;}
1421 	case 's' : {GR (); return;}
1422 	case 'p' : {PRINT (); return;}
1423 */
1424   }
1425 
1426   /* fallback */
1427   if (c == quit_char) {
1428 	CANCEL ();
1429 	return;
1430   }
1431   func = command (c);
1432   if (func != Scharacter) {
1433 	hop_flag = 1;
1434 	keyshift |= alt_mask;
1435 	(* func) (c);
1436   } else {
1437 	BAD (c, "ESC/Alt-");
1438   }
1439   return;
1440 }
1441 
1442 /*
1443  * Interpret emacs meta commands.
1444  */
1445 void
META()1446 META ()
1447 {
1448   unsigned long c;
1449   voidfunc func;
1450 
1451   if (! char_ready_within (500, NIL_PTR)) {
1452 	status_uni ("Meta ESC(exit) TAB,space(menu) /,\\(search) (match ...");
1453   }
1454   if (quit) {
1455 	return;
1456   }
1457 
1458   c = readcharacter_unicode ();
1459   if (quit) {
1460 	return;
1461   }
1462 
1463   clear_status ();
1464 
1465   if ('0' <= c && c <= '9') {
1466 	REPT (c);
1467 	return;
1468   }
1469 
1470   switch (c) {
1471 	/* emacs meta commands */
1472 	case 'v' : {MOVPU (); return;}
1473 	case 'f' : {MNW (); return;}
1474 	case 'b' : {MPW (); return;}
1475 	case 'a' : {BSEN (); return;}
1476 	case 'e' : {ESEN (); return;}
1477 	case '<' : {BFILE (); return;}
1478 	case '>' : {EFILE (); return;}
1479 	case 'd' : {DNW (); return;}
1480 	case 'k' : {setMARK (True); ESEN (); CUT (); return;}
1481 	case 'w' : {COPY (); return;}
1482 	case 'y' : {YANKRING (); return;}
1483 	case 'z' : {SUSP (); return;}
1484 	case '%' : {REPL (); return;}
1485 	case 'u' : {hop_flag = 1; UPPER (); return;}
1486 	case 'l' : {hop_flag = 1; LOWER (); return;}
1487 	case 'c' : {CAPWORD (); return;}
1488 	case '.' : {Stag (); return;}
1489 
1490 	case 'x' : {ESCAPE (); return;}
1491 	case '\033' : {ESCAPE (); return;}
1492 
1493 	default : {
1494 		if (c == quit_char) {
1495 			CANCEL ();
1496 			return;
1497 		}
1498 		func = command (c);
1499 		if (func != Scharacter) {
1500 			hop_flag = 1;
1501 			keyshift |= alt_mask;
1502 			(* func) (c);
1503 		} else {
1504 			BAD (c, "Meta-");
1505 		}
1506 		return;
1507 	}
1508   }
1509 }
1510 
1511 /*
1512  * Interpret emacs ^X commands.
1513  */
1514 void
EMAX()1515 EMAX ()
1516 {
1517   unsigned long c;
1518 
1519   if (! char_ready_within (500, NIL_PTR)) {
1520 	status_msg ("^X ...");
1521   }
1522   if (quit) {
1523 	return;
1524   }
1525 
1526   c = readcharacter_unicode ();
1527   if (quit) {
1528 	return;
1529   }
1530 
1531   clear_status ();
1532 
1533   if (command (c) == MARK) {
1534 	Popmark ();
1535 	return;
1536   }
1537   switch (c) {
1538 	case 'u' : {UNDO (); return;}
1539 	case '' : {QUED (); return;}
1540 	case '' : {WT (); return;}
1541 	case '' : {SAVEAS (); return;}
1542 	case '' : {PRVFILE (); return;}
1543 	case '' : {EDIT (); return;}
1544 	case '\032' : {SUSP (); return;}
1545 	case '\033' : {REPT (' '); return;}
1546 	case 'i' : {INSFILE (); return;}
1547 	case 's' : {WT (); return;}
1548 	case 'k' : {EDIT (); return;}
1549 	case '=' : {FS (); return;}
1550 	case '[' : {MOVPU (); return;}
1551 	case ']' : {MOVPD (); return;}
1552 	default : {
1553 		if (c == quit_char) {
1554 			CANCEL ();
1555 			return;
1556 		}
1557 		BAD (c, "^X ");
1558 	}
1559   }
1560 }
1561 
1562 
1563 /*
1564  * REPT () prompts for a count and wants a command after that. It repeats the
1565  * command count times. If a ^\ is given during repeating, stop looping and
1566  * return to main loop.
1567  */
1568 void
REPT(firstdigit)1569 REPT (firstdigit)
1570   char firstdigit;
1571 {
1572   int count;
1573   voidfunc func;
1574   long val;
1575   unsigned long cmd;
1576   int number;
1577 
1578   hop_flag = 0;
1579   if (firstdigit >= '0' && firstdigit <= '9') {
1580      val = get_number ("Please continue repeat count...", firstdigit, & number);
1581      if (firstdigit != '0' && number < 10) {
1582 	error ("Invalid repeat count after ESC <digit>");
1583 	return;
1584      }
1585   } else {
1586      val = get_number ("Please enter repeat count...", '\0', & number);
1587   }
1588   if (val == ERRORS) {
1589 	return;
1590   }
1591   cmd = val;
1592 
1593   func = command (cmd);
1594   if (func == I) {	/* no function assigned? */
1595 	clear_status ();
1596 	return;
1597   } else if (func == CTRLINS
1598 	  || func == COMPOSE
1599 	  || func == F5
1600 	  || func == F6
1601 	  || func == key_1
1602 	  || func == key_2
1603 	  || func == key_3
1604 	  || func == key_4
1605 	  || func == key_5
1606 	  || func == key_6
1607 	  || func == key_7
1608 	  || func == key_8
1609 	  || func == key_9
1610 	  || func == key_0
1611 	) {
1612 	/* handle ^V/compose input here to not only repeat the prefix */
1613 	if (func == CTRLINS) {
1614 		cmd = CTRLGET (False);
1615 	} else {
1616 		cmd = CTRLGET (True);
1617 	}
1618 	for (count = 0; count < number; count ++) {
1619 		Scharacter (cmd);
1620 	}
1621 	return;
1622   }
1623 
1624   count = number;
1625   while (count -- > 0 && quit == False) {
1626 	char save_keyshift = keyshift;
1627 	if (stat_visible) {
1628 		clear_status ();
1629 	}
1630 	reset_smart_replacement ();
1631 	(* func) (cmd);
1632 	keyshift = save_keyshift;
1633 	display_flush ();
1634 	flush ();
1635   }
1636   reset_smart_replacement ();
1637 
1638   if (quit) {		/* Abort has been given */
1639 	error ("Repeat aborted");
1640   } else {
1641 	clear_status ();
1642   }
1643 }
1644 
1645 
1646 /*
1647    Toggle TAB expansion.
1648  */
1649 void
toggle_tab_expansion()1650 toggle_tab_expansion ()
1651 {
1652   expand_tabs = ! expand_tabs;
1653 }
1654 
1655 /*
1656  * Toggle insert/overwrite mode.
1657  */
1658 void
TOGINS()1659 TOGINS ()
1660 {
1661   if (insert_mode) {
1662 	insert_mode = False;
1663   } else {
1664 	insert_mode = True;
1665   }
1666 }
1667 
1668 void
UNDO()1669 UNDO ()
1670 {
1671   error ("Undo not implemented");
1672 }
1673 
1674 void
SPELL()1675 SPELL ()
1676 {
1677   error ("Spell checking not implemented");
1678 }
1679 
1680 
1681 /*======================================================================*\
1682 |*				End					*|
1683 \*======================================================================*/
1684