1 /* Input: Various input routines for MicroEMACS
2 written by Daniel Lawrence
3 (C)Copyright 1995 by Daniel M. Lawrence
4
5 Notes:
6
7 MicroEMACS's kernel processes two distinct forms of
8 characters. One of these is a standard unsigned character
9 which is used in the edited text. The other form, called
10 an EMACS Extended Character is a 2 byte value which contains
11 both an ascii value, and flags for certain prefixes/events.
12
13 Bit Usage
14 --- -----
15 0 -> 7 Standard 8 bit ascii character
16 8 Control key flag
17 9 META prefix flag
18 10 ^X prefix flag
19 11 Function key flag
20 12 Mouse prefix
21 13 Shifted flag (not needed on alpha shifted characters)
22 14 Alterate prefix (ALT key on PCs)
23
24 The machine dependent driver is responsible for returning
25 a byte stream from the various input devices with various
26 prefixes/events embedded as escape codes. Zero is used as the
27 value indicating an escape sequence is next. The format of
28 an escape sequence is as follows:
29
30 0 Escape indicator
31 <prefix byte> upper byte of extended character
32 {<col><row>} col, row position if the prefix byte
33 indicated a mouse event or a menu selection
34 in which case these form a 16 bit menu ID
35 <event code> value of event
36
37 A ^<space> sequence (0/1/32) is generated when an actual
38 null is being input from the control-space key under many
39 unix systems. These values are then interpreted by get_key()
40 to construct the proper extended character sequences to pass
41 to the MicroEMACS kernel.
42 */
43
44 #include <stdio.h>
45 #include "estruct.h"
46 #include "eproto.h"
47 #include "edef.h"
48 #include "elang.h"
49
50 #if USG | AIX | AUX | BSD | FREEBSD | SUN | HPUX8 | HPUX9
51 #include <pwd.h>
52 extern struct passwd *getpwnam();
53 #endif
54
55 /*
56 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
57 * ABORT. The ABORT status is returned if the user bumps out of the question
58 * with a ^G. Used any time a confirmation is required.
59 */
60
61 #if !WINDOW_MSWIN /* for MS Windows, mlyesno is defined in mswsys.c */
mlyesno(prompt)62 PASCAL NEAR mlyesno(prompt)
63
64 char *prompt;
65
66 {
67 int c; /* input character */
68 char buf[NPAT]; /* prompt to user */
69
70 for (;;) {
71 /* build and prompt the user */
72 strcpy(buf, prompt);
73 strcat(buf, TEXT162);
74 /* " [y/n]? " */
75 mlwrite(buf);
76
77 /* get the response */
78 c = getcmd(); /* getcmd() lets us check for anything that might */
79 /* generate a 'y' or 'Y' in case use screws up */
80
81 if (c == ectoc(abortc)) /* Bail out! */
82 return(ABORT);
83
84 if ((c == 'n') || (c == 'N')
85 || (c & (SPEC|ALTD|CTRL|META|CTLX|MOUS)))
86 return(FALSE); /* ONLY 'y' or 'Y' allowed!!! */
87
88 #if FRENCH
89 if (c=='o' || c=='O')
90 return(TRUE);
91 #endif
92
93 if (c=='y' || c=='Y')
94 return(TRUE);
95
96 return(FALSE);
97 }
98 }
99 #endif
100
101 /*
102 * Write a prompt into the message line, then read back a response. Keep
103 * track of the physical position of the cursor. If we are in a keyboard
104 * macro throw the prompt away, and return the remembered response. This
105 * lets macros run at full speed. The reply is always terminated by a carriage
106 * return. Handle erase, kill, and abort keys.
107 */
108
mlreply(prompt,buf,nbuf)109 PASCAL NEAR mlreply(prompt, buf, nbuf)
110
111 char *prompt;
112 char *buf;
113 int nbuf;
114
115 {
116 return(nextarg(prompt, buf, nbuf, ctoec((int) '\r')));
117 }
118
119 /* ectoc: expanded character to character
120 collapse the CTRL and SPEC flags back into an ascii code */
121
ectoc(c)122 PASCAL NEAR ectoc(c)
123
124 int c;
125
126 {
127 if (c == (CTRL | ' '))
128 c = 0;
129 if (c & CTRL)
130 c = c ^ (CTRL | 0x40);
131 if (c & SPEC)
132 c = c & 255;
133 return(c);
134 }
135
136 /* ctoec: character to extended character
137 pull out the CTRL and SPEC prefixes (if possible) */
138
ctoec(c)139 PASCAL NEAR ctoec(c)
140
141 int c;
142
143 {
144 if (c == 0)
145 c = CTRL | ' ';
146 else if ((c >= 0x00 && c <= 0x1F) || c == 0x7F)
147 c = CTRL | (c ^ 0x40);
148 return(c);
149 }
150
151 /* get a command name from the command line. Command completion means
152 that pressing a <SPACE> will attempt to complete an unfinished command
153 name if it is unique.
154 */
155
156 #if MSC
157 int (PASCAL NEAR *PASCAL NEAR getname(char *prompt))(void)
158 #else
159 int (PASCAL NEAR *PASCAL NEAR getname(prompt))()
160
161 char *prompt; /* string to prompt with */
162 #endif
163
164 {
165 char *sp; /* ptr to the returned string */
166
167 sp = complete(prompt, NULL, CMP_COMMAND, NSTRING);
168 if (sp == NULL)
169 return(NULL);
170
171 return(fncmatch(sp));
172 }
173
174 /* getcbuf: get a completion from the user for a buffer name.
175
176 I was goaded into this by lots of other people's
177 completion code.
178 */
179
getcbuf(prompt,defval,createflag)180 BUFFER *PASCAL NEAR getcbuf(prompt, defval, createflag)
181
182 char *prompt; /* prompt to user on command line */
183 char *defval; /* default value to display to user */
184 int createflag; /* should this create a new buffer? */
185
186 {
187 char *sp; /* ptr to the returned string */
188
189 sp = complete(prompt, defval, CMP_BUFFER, NBUFN);
190 if (sp == NULL)
191 return(NULL);
192
193 return(bfind(sp, createflag, 0));
194 }
195
gtfilename(prompt)196 char *PASCAL NEAR gtfilename(prompt)
197
198 char *prompt; /* prompt to user on command line */
199
200 {
201 #if MSDOS | OS2
202 char *scan;
203 #endif
204 #if WINDOW_MSWIN
205 static char sp[NFILEN];
206
207 if (!FILENAMEREPLY(prompt, sp, NFILEN))
208 return NULL;
209 #else
210 char *sp; /* ptr to the returned string */
211
212 /* get a file name, default to current buffer's */
213 if (curbp && curbp->b_fname && strcmp(curbp->b_fname, "") != 0)
214 sp = complete(prompt, curbp->b_fname, CMP_FILENAME, NFILEN);
215 else
216 sp = complete(prompt, NULL, CMP_FILENAME, NFILEN);
217 #endif
218 #if MSDOS | OS2
219 /* change forward slashes to back */
220 if (sp) {
221 scan = sp;
222 while (*scan) {
223 if (*scan == '/')
224 *scan = DIRSEPCHAR;
225 ++scan;
226 }
227 }
228 #endif
229 return(sp);
230 }
231
complete(prompt,defval,type,maxlen)232 char *PASCAL NEAR complete(prompt, defval, type, maxlen)
233
234 char *prompt; /* prompt to user on command line */
235 char *defval; /* default value to display to user */
236 int type; /* type of what we are completing */
237 int maxlen; /* maximum length of input field */
238
239 {
240 register int c; /* current input character */
241 register int ec; /* extended input character */
242 int cpos; /* current column on screen output */
243 char *home_ptr; /* pointer to home directory string */
244 char *ptr; /* string pointer */
245 char user_name[NSTRING]; /* user name for directory */
246 static char buf[NSTRING];/* buffer to hold tentative name */
247 #if USG | AIX | AUX | BSD | FREEBSD | SUN | HPUX8 | HPUX9
248 struct passwd *pwd; /* password structure */
249 #endif
250
251 /* if we are executing a command line get the next arg and match it */
252 if (clexec) {
253 if (macarg(buf) != TRUE)
254 return(NULL);
255 return(buf);
256 }
257
258 /* starting at the beginning of the string buffer */
259 cpos = 0;
260
261 /* if it exists, prompt the user for a buffer name */
262 if (prompt)
263 if (type == CMP_COMMAND)
264 mlwrite("%s", prompt);
265 else if (defval)
266 mlwrite("%s[%s]: ", prompt, defval);
267 else
268 mlwrite("%s: ", prompt);
269
270 /* build a name string from the keyboard */
271 while (TRUE) {
272
273 /* get the keystroke and decode it */
274 ec = get_key();
275 c = ectoc(ec);
276
277 /* if it is from the mouse, or is a function key, blow it off */
278 if ((ec & MOUS) || (ec & SPEC))
279 continue;
280
281 /* if we are at the end, just match it */
282 if (c == '\n' || c == '\r') {
283 if (defval && cpos==0)
284 return(defval);
285 else {
286 buf[cpos] = 0;
287 return(buf);
288 }
289
290 } else if (ec == abortc) { /* Bell, abort */
291 ctrlg(FALSE, 0);
292 TTflush();
293 return(NULL);
294
295 } else if (c == 0x7F || c == 0x08) { /* rubout/erase */
296 if (cpos != 0) {
297 mlout('\b');
298 mlout(' ');
299 mlout('\b');
300 --ttcol;
301 --cpos;
302 TTflush();
303 }
304
305 } else if (c == 0x15) { /* C-U, kill */
306 while (cpos != 0) {
307 mlout('\b');
308 mlout(' ');
309 mlout('\b');
310 --cpos;
311 --ttcol;
312 }
313 TTflush();
314
315 } else if ((c == ' ') || (ec == sterm) || (c == '\t')) {
316 /* attempt a completion */
317 switch (type) {
318 case CMP_BUFFER:
319 comp_buffer(buf, &cpos);
320 break;
321 case CMP_COMMAND:
322 comp_command(buf, &cpos);
323 break;
324 #if !WINDOW_MSWIN
325 case CMP_FILENAME:
326 comp_file(buf, &cpos);
327 break;
328 #endif
329 }
330
331 TTflush();
332 if (cpos > 0 && buf[cpos - 1] == 0)
333 return(buf);
334 goto clist;
335
336 #if ENVFUNC
337 } else if ((cpos > 0) &&
338 ((char)c == DIRSEPCHAR) &&
339 (type == CMP_FILENAME) &&
340 (buf[0] == '~') &&
341 ((home_ptr = getenv("HOME")) != (char *)NULL)) {
342
343 /* save the user name! */
344 buf[cpos] = 0;
345 strcpy(user_name, &buf[1]);
346
347 /* erase the chars on-screen */
348 while (cpos > 0) {
349 mlout('\b');
350 mlout(' ');
351 mlout('\b');
352 --cpos;
353 --ttcol;
354 }
355
356 #if USG | AIX | AUX | BSD | FREEBSD | SUN | HPUX8 | HPUX9
357 /* lookup someone else's home directory! */
358 if (user_name[0] != 0) {
359 pwd = getpwnam(user_name);
360 if (pwd != (struct passwd *)NULL) {
361 ptr = pwd->pw_dir;
362 while (*ptr) {
363 mlout(*ptr);
364 buf[cpos++] = *ptr++;
365 ++ttcol;
366 }
367 }
368 }
369 #endif
370 if (cpos == 0) {
371
372 /* output the home directory */
373 ptr = home_ptr;
374 while (*ptr) {
375 mlout(*ptr);
376 buf[cpos++] = *ptr++;
377 ++ttcol;
378 }
379
380 /* is this someone else's home directory */
381 if (user_name[0] != 0) {
382
383 /* backup to the last directory sep */
384 while ((cpos > 0) &&
385 (buf[cpos-1] != DIRSEPCHAR)) {
386 mlout('\b');
387 mlout(' ');
388 mlout('\b');
389 --cpos;
390 --ttcol;
391 }
392
393 /* and add the user's name */
394 ptr = user_name;
395 while (*ptr) {
396 mlout(*ptr);
397 buf[cpos++] = *ptr++;
398 ++ttcol;
399 }
400 }
401
402 }
403
404 /* and the last directory seperator */
405 if (buf[cpos-1] != DIRSEPCHAR) {
406 mlout(DIRSEPCHAR);
407 buf[cpos++] = DIRSEPCHAR;
408 ++ttcol;
409 }
410 TTflush();
411
412 } else if ((cpos > 1) &&
413 ((char)c == DIRSEPCHAR) &&
414 (type == CMP_FILENAME) &&
415 (buf[0] == '$')) {
416
417 /* expand an environment variable reference */
418 /* save the variable name! */
419 buf[cpos] = 0;
420 strcpy(user_name, &buf[1]);
421 #if MSDOS | OS2 | VMS
422 mkupper(user_name);
423 #endif
424
425 /* erase the chars on-screen */
426 while (cpos > 0) {
427 mlout('\b');
428 mlout(' ');
429 mlout('\b');
430 --cpos;
431 --ttcol;
432 }
433
434 ptr = getenv(user_name);
435 if (ptr != (char *)NULL) {
436 while (*ptr) {
437 mlout(*ptr);
438 buf[cpos++] = *ptr++;
439 ++ttcol;
440 }
441 }
442
443 /* and the last directory seperator */
444 if (buf[cpos-1] != DIRSEPCHAR) {
445 mlout(DIRSEPCHAR);
446 buf[cpos++] = DIRSEPCHAR;
447 ++ttcol;
448 }
449 TTflush();
450
451 #endif /* ENVFUNC */
452
453 } else if (c == '?') {
454
455 clist: /* make a completion list! */
456 switch (type) {
457 case CMP_BUFFER:
458 clist_buffer(buf, &cpos);
459 break;
460 case CMP_COMMAND:
461 clist_command(buf, &cpos);
462 break;
463 #if !WINDOW_MSWIN
464 case CMP_FILENAME:
465 clist_file(buf, &cpos);
466 break;
467 #endif
468 }
469 update(TRUE);
470
471 /* if it exists, reprompt the user */
472 if (prompt) {
473 buf[cpos] = 0;
474 if (type == CMP_COMMAND)
475 mlwrite("%s%s", prompt, buf);
476 else if (defval)
477 mlwrite("%s[%s]: %s", prompt, defval, buf);
478 else
479 mlwrite("%s: %s", prompt, buf);
480 }
481
482 } else {
483 if (cpos < maxlen && c > ' ') {
484 buf[cpos++] = c;
485 mlout(c);
486 ++ttcol;
487 TTflush();
488 }
489 }
490 }
491 }
492
493 /* comp_command: Attempt a completion on a command name */
494
comp_command(name,cpos)495 VOID PASCAL NEAR comp_command(name, cpos)
496
497 char *name; /* command containing the current name to complete */
498 int *cpos; /* ptr to position of next character to insert */
499
500 {
501 register NBIND *bp; /* trial command to complete */
502 register int index; /* index into strings to compare */
503 register int curbind; /* index into the names[] array */
504 register NBIND *match; /* last command that matches string */
505 register int matchflag; /* did this command name match? */
506 register int comflag; /* was there a completion at all? */
507
508 /* everything (or nothing) matches an empty string */
509 if (*cpos == 0)
510 return;
511
512 /* start attempting completions, one character at a time */
513 comflag = FALSE;
514 curbind = 0;
515 while (*cpos < NSTRING) {
516
517 /* first, we start at the first command and scan the list */
518 match = NULL;
519 curbind = 0;
520 while (curbind <= numfunc) {
521
522 /* is this a match? */
523 bp = &names[curbind];
524 matchflag = TRUE;
525 for (index = 0; index < *cpos; index++)
526 if (name[index] != bp->n_name[index]) {
527 matchflag = FALSE;
528 break;
529 }
530
531 /* if it is a match */
532 if (matchflag) {
533
534 /* if this is the first match, simply record it */
535 if (match == NULL) {
536 match = bp;
537 name[*cpos] = bp->n_name[*cpos];
538 } else {
539 /* if there's a difference, stop here */
540 if (name[*cpos] != bp->n_name[*cpos])
541 return;
542 }
543 }
544
545 /* on to the next command */
546 curbind++;
547 }
548
549 /* with no match, we are done */
550 if (match == NULL) {
551 /* beep if we never matched */
552 if (comflag == FALSE)
553 TTbeep();
554 return;
555 }
556
557 /* if we have completed all the way... go back */
558 if (name[*cpos] == 0) {
559 (*cpos)++;
560 return;
561 }
562
563 /* remember we matched, and complete one character */
564 comflag = TRUE;
565 TTputc(name[(*cpos)++]);
566 TTflush();
567 }
568
569 /* don't allow a completion past the end of the max command name length */
570 return;
571 }
572
573 /* clist_command: Make a completion list based on a partial name */
574
clist_command(name,cpos)575 VOID PASCAL NEAR clist_command(name, cpos)
576
577 char *name; /* command containing the current name to complete */
578 int *cpos; /* ptr to position of next character to insert */
579
580 {
581 register NBIND *bp; /* trial command to complete */
582 register int curbind; /* index into the names[] array */
583 register int name_len; /* current length of input string */
584 register BUFFER *listbuf;/* buffer to put completion list into */
585
586 /* get a buffer for the completion list */
587 listbuf = bfind("[Completion list]", TRUE, BFINVS);
588 if (listbuf == NULL || bclear(listbuf) == FALSE) {
589 ctrlg(FALSE, 0);
590 TTflush();
591 return;
592 }
593
594 name_len = *cpos;
595
596 /* first, we start at the first command and scan the list */
597 for (curbind = 0; curbind <= numfunc; curbind++) {
598
599 /* is this a match? */
600 bp = &names[curbind];
601 if (strncmp(name, bp->n_name, name_len) == 0)
602 addline(listbuf, bp->n_name);
603 }
604
605 wpopup(listbuf);
606 return;
607 }
608
609 /* comp_buffer: Attempt a completion on a buffer name */
610
comp_buffer(name,cpos)611 VOID PASCAL NEAR comp_buffer(name, cpos)
612
613 char *name; /* buffer containing the current name to complete */
614 int *cpos; /* ptr to position of next character to insert */
615
616 {
617 register BUFFER *bp; /* trial buffer to complete */
618 register int index; /* index into strings to compare */
619 register BUFFER *match; /* last buffer that matches string */
620 register int matchflag; /* did this buffer name match? */
621 register int comflag; /* was there a completion at all? */
622
623 /* everything (or nothing) matches an empty string */
624 if (*cpos == 0)
625 return;
626
627 /* start attempting completions, one character at a time */
628 comflag = FALSE;
629 while (*cpos < NBUFN) {
630
631 /* first, we start at the first buffer and scan the list */
632 match = NULL;
633 bp = bheadp;
634 while (bp) {
635
636 /* is this a match? */
637 matchflag = TRUE;
638 for (index = 0; index < *cpos; index++)
639 if (name[index] != bp->b_bname[index]) {
640 matchflag = FALSE;
641 break;
642 }
643
644 /* if it is a match */
645 if (matchflag) {
646
647 /* if this is the first match, simply record it */
648 if (match == NULL) {
649 match = bp;
650 name[*cpos] = bp->b_bname[*cpos];
651 } else {
652 /* if there's a difference, stop here */
653 if (name[*cpos] != bp->b_bname[*cpos])
654 return;
655 }
656 }
657
658 /* on to the next buffer */
659 bp = bp->b_bufp;
660 }
661
662 /* with no match, we are done */
663 if (match == NULL) {
664 /* beep if we never matched */
665 if (comflag == FALSE)
666 TTbeep();
667 return;
668 }
669
670 /* if we have completed all the way... go back */
671 if (name[*cpos] == 0) {
672 (*cpos)++;
673 return;
674 }
675
676 /* remember we matched, and complete one character */
677 comflag = TRUE;
678 TTputc(name[(*cpos)++]);
679 TTflush();
680 }
681
682 /* don't allow a completion past the end of the max buffer name length */
683 return;
684 }
685
686 /* clist_buffer: Make a completion list based on a partial buffer name */
687
clist_buffer(name,cpos)688 VOID PASCAL NEAR clist_buffer(name, cpos)
689
690 char *name; /* command containing the current name to complete */
691 int *cpos; /* ptr to position of next character to insert */
692
693 {
694 register int name_len; /* current length of input string */
695 register BUFFER *listbuf;/* buffer to put completion list into */
696 register BUFFER *bp; /* trial buffer to complete */
697
698 /* get a buffer for the completion list */
699 listbuf = bfind("[Completion list]", TRUE, BFINVS);
700 if (listbuf == NULL || bclear(listbuf) == FALSE) {
701 ctrlg(FALSE, 0);
702 TTflush();
703 return;
704 }
705
706 /* first, we start at the first buffer and scan the list */
707 name_len = *cpos;
708 bp = bheadp;
709
710 while (bp) {
711
712 /* is this a match? */
713 if (strncmp(name, bp->b_bname, name_len) == 0)
714 addline(listbuf, bp->b_bname);
715
716 /* on to the next buffer */
717 bp = bp->b_bufp;
718 }
719
720 wpopup(listbuf);
721 return;
722 }
723
724 #if !WINDOW_MSWIN
725 /* comp_file: Attempt a completion on a file name */
726
comp_file(name,cpos)727 VOID PASCAL NEAR comp_file(name, cpos)
728
729 char *name; /* file containing the current name to complete */
730 int *cpos; /* ptr to position of next character to insert */
731
732 {
733 register char *fname; /* trial file to complete */
734 register int index; /* index into strings to compare */
735
736 register int matches; /* number of matches for name */
737 char longestmatch[NSTRING]; /* temp buffer for longest match */
738 int longestlen; /* length of longest match (always > *cpos) */
739
740 /* everything (or nothing) matches an empty string */
741 if (*cpos == 0)
742 return;
743
744 /* first, we start at the first file and scan the list */
745 matches = 0;
746 name[*cpos] = 0;
747 fname = getffile(name);
748 while (fname) {
749
750 /* is this a match? */
751 if (strncmp(name,fname,*cpos) == 0) {
752
753 /* count the number of matches */
754 matches++;
755
756 /* if this is the first match, simply record it */
757 if (matches == 1) {
758 strcpy(longestmatch,fname);
759 longestlen = strlen(longestmatch);
760 } else {
761
762 /* if there's a difference, stop here */
763 if (longestmatch[*cpos] != fname[*cpos])
764 return;
765
766 for (index = (*cpos) + 1; index < longestlen; index++)
767 if (longestmatch[index] != fname[index]) {
768 longestlen = index;
769 longestmatch[longestlen] = 0;
770 }
771 }
772 }
773
774 /* on to the next file */
775 fname = getnfile();
776 }
777
778 /* beep if we never matched */
779 if (matches == 0) {
780 TTbeep();
781 return;
782 }
783
784 /* the longestmatch array contains the longest match so copy and print it */
785 for ( ; (*cpos < (NSTRING-1)) && (*cpos < longestlen); (*cpos)++) {
786 name[*cpos] = longestmatch[*cpos];
787 TTputc(name[*cpos]);
788 }
789
790 name[*cpos] = 0;
791
792 /* if only one file matched then increment cpos to signal complete() */
793 /* that this was a complete match. If a directory was matched then */
794 /* last character will be the DIRSEPCHAR. In this case we do NOT */
795 /* want to signal a complete match. */
796 if ((matches == 1) && (name[(*cpos)-1] != DIRSEPCHAR))
797 (*cpos)++;
798
799 TTflush();
800
801 return;
802 }
803
804 /* clist_file: Make a completion list based on a partial file name */
805
clist_file(name,cpos)806 VOID PASCAL NEAR clist_file(name, cpos)
807
808 char *name; /* command containing the current name to complete */
809 int *cpos; /* ptr to position of next character to insert */
810
811 {
812 register int name_len; /* current length of input string */
813 register BUFFER *listbuf;/* buffer to put completion list into */
814 register char *fname; /* trial file to complete */
815
816 /* get a buffer for the completion list */
817 listbuf = bfind("[Completion list]", TRUE, BFINVS);
818 if (listbuf == NULL || bclear(listbuf) == FALSE) {
819 ctrlg(FALSE, 0);
820 TTflush();
821 return;
822 }
823
824 /* first, we start at the first file and scan the list */
825 name_len = *cpos;
826 name[*cpos] = 0;
827 fname = getffile(name);
828
829 /* first, we start at the first file and scan the list */
830 while (fname) {
831
832 /* is this a match? */
833 if (strncmp(name, fname, name_len) == 0)
834 addline(listbuf, fname);
835
836 /* on to the next file */
837 fname = getnfile();
838 }
839
840 wpopup(listbuf);
841 return;
842 }
843 #endif
844
845 /* tgetc: Get a key from the terminal driver, resolve any keyboard
846 macro action */
847
tgetc()848 int PASCAL NEAR tgetc()
849
850 {
851 int c; /* fetched character */
852
853 /* if we are playing a keyboard macro back, */
854 if (kbdmode == PLAY) {
855
856 /* if there is some left... */
857 if (kbdptr < kbdend)
858 return((int)*kbdptr++);
859
860 /* at the end of last repitition? */
861 if (--kbdrep < 1) {
862 kbdmode = STOP;
863 #if VISMAC == 0
864 /* force a screen update after all is done */
865 update(FALSE);
866 #endif
867 } else {
868
869 /* reset the macro to the begining for the next rep */
870 kbdptr = &kbdm[0];
871 return((int)*kbdptr++);
872 }
873 }
874
875 /* if no pending character */
876 if (cpending == FALSE) {
877
878 /* fetch a character from the terminal driver */
879 c = TTgetc();
880
881 } else {
882
883 c = charpending;
884 cpending = FALSE;
885 }
886
887 /* record it for $lastkey */
888 lastkey = c;
889
890 /* save it if we need to */
891 if (kbdmode == RECORD) {
892 *kbdptr++ = c;
893 kbdend = kbdptr;
894
895 /* don't overrun the buffer */
896 if (kbdptr == &kbdm[NKBDM - 1]) {
897 kbdmode = STOP;
898 TTbeep();
899 }
900 }
901
902 /* and finally give the char back */
903 return(c);
904 }
905
906 /* get_key: Get one keystroke. The legal prefixs here
907 are the SPEC, MOUS and CTRL prefixes.
908 */
909
get_key()910 int PASCAL NEAR get_key()
911
912 {
913 int c; /* next input character */
914 int upper; /* upper byte of the extended sequence */
915
916 /* get a keystroke */
917 c = tgetc();
918
919 /* if it exists, process an escape sequence */
920 if (c == 0) {
921
922 /* get the event type */
923 upper = tgetc();
924
925 /* mouse events need us to read in the row/col */
926 if (upper & (MOUS >> 8)) {
927 /* grab the x/y position of the mouse */
928 xpos = tgetc();
929 ypos = tgetc();
930 }
931
932 /* get the event code */
933 c = tgetc();
934
935 /* if it is a function key... map it */
936 c = (upper << 8) | c;
937 }
938
939 /* yank out the control prefix */
940 if (((c & 255) >=0x00 && (c & 255) <= 0x1F) || (c & 255) == 0x7F)
941 c = CTRL | (c ^ 0x40);
942
943 /* return the character */
944 return(c);
945 }
946
947 /* GETCMD: Get a command from the keyboard. Process all applicable
948 prefix keys
949 */
getcmd()950 int PASCAL NEAR getcmd()
951
952 {
953 int c; /* fetched keystroke */
954 KEYTAB *key; /* ptr to a key entry */
955
956 /* get initial character */
957 c = get_key();
958 key = getbind(c);
959
960 /* resolve META and CTLX prefixes */
961 if (key) {
962 if (key->k_ptr.fp == meta) {
963 c = get_key();
964 #if SMOS
965 c = upperc(c&255) | (c & ~255); /* Force to upper */
966 #else
967 c = upperc(c) | (c & ~255); /* Force to upper */
968 #endif
969 c |= META;
970 } else if (key->k_ptr.fp == cex) {
971 c = get_key();
972 #if SMOS
973 c = upperc(c&255) | (c & ~255); /* Force to upper */
974 #else
975 c = upperc(c) | (c & ~255); /* Force to upper */
976 #endif
977 c |= CTLX;
978 }
979 }
980
981 /* return it */
982 return(c);
983 }
984
985 /* A more generalized prompt/reply function allowing the caller
986 to specify the proper terminator. If the terminator is not
987 a return('\r'), return will echo as "<NL>"
988 */
getstring(buf,nbuf,eolchar)989 int PASCAL NEAR getstring(buf, nbuf, eolchar)
990
991 unsigned char *buf;
992 int nbuf;
993 int eolchar;
994
995 {
996 register int cpos; /* current character position in string */
997 register int c; /* current input character */
998 register int ec; /* extended current input character */
999 register int quotef; /* are we quoting the next char? */
1000 char *kp; /* pointer into key_name */
1001 char key_name[10]; /* name of a quoted key */
1002
1003 cpos = 0;
1004 quotef = FALSE;
1005
1006 for (;;) {
1007 /* get a character from the user */
1008 ec = get_key();
1009
1010 /* if they hit the line terminate, wrap it up */
1011 if (ec == eolchar && quotef == FALSE) {
1012 buf[cpos++] = 0;
1013
1014 /* clear the message line */
1015 mlerase();
1016
1017 /* if we default the buffer, return FALSE */
1018 if (buf[0] == 0)
1019 return(FALSE);
1020
1021 return(TRUE);
1022 }
1023
1024 /* change from command form back to character form */
1025 c = ectoc(ec);
1026
1027 if ((ec == abortc) && (quotef == FALSE)) {
1028 /* Abort the input? */
1029 ctrlg(FALSE, 0);
1030 TTflush();
1031 return(ABORT);
1032 }
1033
1034 /* if it is from the mouse, or is a function key, blow it off */
1035 if ((quotef == FALSE) && ((ec & MOUS) || (ec & SPEC)))
1036 continue;
1037
1038 /* rubout/erase */
1039 if ((c==0x7F || c==0x08) && quotef==FALSE) {
1040 if (cpos != 0) {
1041 outstring("\b \b");
1042 --ttcol;
1043
1044 if (buf[--cpos] < 0x20) {
1045 outstring("\b \b");
1046 --ttcol;
1047 }
1048
1049 if (buf[cpos] == '\r') {
1050 outstring("\b\b \b\b");
1051 ttcol -= 2;
1052 }
1053 TTflush();
1054 }
1055 continue;
1056 }
1057
1058 /* C-K, kill default buffer and return null */
1059 if (c == 0x0b && quotef == FALSE) {
1060
1061 /* clear the buffer */
1062 buf[0] = 0;
1063
1064 /* clear the message line and return */
1065 mlwrite("");
1066 TTflush();
1067 return(TRUE);
1068 }
1069
1070 /* C-U, kill */
1071 if (c == 0x15 && quotef == FALSE) {
1072
1073 while (cpos != 0) {
1074 outstring("\b \b");
1075 --ttcol;
1076
1077 if (buf[--cpos] < 0x20) {
1078 outstring("\b \b");
1079 --ttcol;
1080 }
1081 if (buf[cpos] == '\r') {
1082 outstring("\b\b \b\b");
1083 ttcol -= 2;
1084 }
1085 }
1086 TTflush();
1087 continue;
1088 }
1089
1090 /* quoting next character? */
1091 if ((ec == quotec) && (quotef == FALSE)) {
1092 quotef = TRUE;
1093 continue;
1094 }
1095
1096 quotef = FALSE;
1097
1098 /* if it is from the mouse, or is a function key,
1099 insert it's name since it was quoted */
1100 if ((ec & MOUS) || (ec & SPEC)) {
1101 cmdstr(ec, key_name);
1102 kp = key_name;
1103 while (*kp) {
1104 if (cpos < nbuf - 1) {
1105 if (disinp)
1106 mlout(*kp);
1107 buf[cpos++] = *kp++;
1108 ++ttcol;
1109 }
1110 }
1111 TTflush();
1112 continue;
1113 }
1114
1115 /* insert the character in the string! */
1116 if (cpos < nbuf-1) {
1117
1118 buf[cpos++] = c;
1119
1120 if ((c < ' ') && (c != '\r')) {
1121 outstring("^");
1122 ++ttcol;
1123 c ^= 0x40;
1124 }
1125
1126 if (c != '\r') {
1127 if (disinp)
1128 mlout(c);
1129 } else { /* put out <NL> for <ret> */
1130 outstring("<NL>");
1131 ttcol += 3;
1132 }
1133 ++ttcol;
1134 TTflush();
1135 }
1136 }
1137 }
1138
outstring(s)1139 PASCAL NEAR outstring(s) /* output a string of input characters */
1140
1141 char *s; /* string to output */
1142
1143 {
1144 if (disinp)
1145 while (*s)
1146 mlout(*s++);
1147 }
1148
ostring(s)1149 PASCAL NEAR ostring(s) /* output a string of output characters */
1150
1151 char *s; /* string to output */
1152
1153 {
1154 if (discmd)
1155 while (*s)
1156 mlout(*s++);
1157 }
1158
1159 /*
1160 * mlprompt -- Display a prompt [with optional default] and the
1161 * input terminator.
1162 */
mlprompt(prompt,dflt,iterm)1163 int PASCAL NEAR mlprompt(prompt, dflt, iterm)
1164 char *prompt;
1165 char *dflt;
1166 int iterm;
1167 {
1168 register int tcol;
1169 char buf[NSTRING];
1170
1171 /* don't bother displaying if we are't currently */
1172 if (discmd == FALSE)
1173 return(0);
1174
1175 /* show the passed in prompt */
1176 mlwrite(prompt);
1177 tcol = strlen(prompt);
1178
1179 /* If there's a default, put it in brackets and show it. */
1180 if (dflt != NULL && *dflt != '\0') {
1181 mlout('[');
1182 tcol = 1 + echostring(dflt, tcol + 1, NPAT/2);
1183 mlout(']');
1184 }
1185
1186 /* Display the proper current search terminator character. */
1187 mlout('<');
1188 switch (iterm) {
1189 case CTRL | '[':
1190 mlputs("META"); tcol += 8; break;
1191 case CTRL | 'M':
1192 mlputs("NL"); tcol += 6; break;
1193 default:
1194 mlputs(cmdstr(iterm, buf));
1195 tcol += strlen(buf) + 4;
1196 }
1197 mlputs(">: ");
1198 movecursor(term.t_nrow, tcol); /* Position the cursor */
1199 TTflush();
1200 return(tcol);
1201 }
1202
1203 /*
1204 * echostring -- Use echochar() to put out a string. Checks for NULL.
1205 */
echostring(str,tcol,uptocol)1206 int PASCAL NEAR echostring(str, tcol, uptocol)
1207 char *str; /* characters to be echoed */
1208 int tcol; /* column to be echoed in */
1209 int uptocol; /* last column to be echoed in */
1210 {
1211 if (str != NULL) {
1212 while (*str) {
1213 movecursor(term.t_nrow, tcol); /* Position the cursor */
1214 tcol += echochar(*str++);
1215 if (tcol >= uptocol) {
1216 mlout('$');
1217 tcol++;
1218 break;
1219 }
1220 }
1221 }
1222 movecursor(term.t_nrow, tcol); /* Position the cursor */
1223 return(tcol);
1224 }
1225
1226 /*
1227 * Routine to echo i-search and message-prompting characters.
1228 */
echochar(c)1229 int PASCAL NEAR echochar(c)
1230
1231 unsigned char c; /* character to be echoed */
1232
1233 {
1234 int col = 0; /* column to be echoed in */
1235
1236 if (c == '\r') { /* Newline character? */
1237 mlout('<');
1238 mlout('N');
1239 mlout('L');
1240 mlout('>');
1241 col = 3;
1242 }
1243 #if 0
1244 else if (c == '\t') { /* Tab character? */
1245 mlout('<');
1246 mlout('T');
1247 mlout('A');
1248 mlout('B');
1249 mlout('>');
1250 col = 4;
1251 }
1252 #endif
1253 else if ((c < ' ') || (c == 0x7F)) { /* Vanilla control char and Rubout: */
1254 mlout('^'); /* Yes, output prefix */
1255 mlout(c ^ 0x40);/* Make it "^X" */
1256 col++; /* Count this char */
1257 }
1258 else
1259 mlout(c); /* Otherwise, output raw char */
1260 TTflush(); /* Flush the output */
1261 return(++col); /* return the new column number */
1262 }
1263