1 /*	This file is for functions having to do with key bindings,
2 	descriptions, help commands and startup file.
3 
4 	written 11-feb-86 by Daniel Lawrence
5 								*/
6 
7 #include	<stdio.h>
8 #include	"estruct.h"
9 #include	"eproto.h"
10 #include	"edef.h"
11 #include	"elang.h"
12 #include	"epath.h"
13 
help(f,n)14 PASCAL NEAR help(f, n)	/* give me some help!!!!
15 		   bring up a fake buffer and read the help file
16 		   into it with view mode			*/
17 
18 int f,n;	/* prefix flag and argument */
19 
20 {
21 	register BUFFER *bp;	/* buffer pointer to help */
22 	char *fname;		/* file name of help file */
23 
24 	/* first check if we are already here */
25 	bp = bfind("emacs.hlp", FALSE, BFINVS);
26 
27 	if (bp == NULL) {
28 		fname = flook(pathname[1], FALSE);
29 		if (fname == NULL) {
30 			mlwrite(TEXT12);
31 /*				"[Help file is not online]" */
32 			return(FALSE);
33 		}
34 	}
35 
36 	/* split the current window to make room for the help stuff */
37 	if (splitwind(FALSE, 1) == FALSE)
38 			return(FALSE);
39 
40 	if (bp == NULL) {
41 		/* and read the stuff in */
42 		if (getfile(fname, FALSE) == FALSE)
43 			return(FALSE);
44 	} else
45 		swbuffer(bp);
46 
47 	/* make this window in VIEW mode, update all mode lines */
48 	curwp->w_bufp->b_mode |= MDVIEW;
49 	curwp->w_bufp->b_flag |= BFINVS;
50 	upmode();
51 	return(TRUE);
52 }
53 
deskey(f,n)54 PASCAL NEAR deskey(f, n)	/* describe the command for a certain key */
55 
56 int f,n;	/* prefix flag and argument */
57 
58 {
59 	register int c; 	/* key to describe */
60 	register char *ptr;	/* string pointer to scan output strings */
61 	char outseq[NSTRING];	/* output buffer for command sequence */
62 
63 	/* prompt the user to type us a key to describe */
64 	mlwrite(TEXT13);
65 /*		": describe-key " */
66 
67 	/* get the command sequence to describe
68 	   change it to something we can print as well */
69 	/* and dump it out */
70 	ostring(cmdstr(c = getckey(FALSE), &outseq[0]));
71 	ostring(" ");
72 
73 	/* find the right ->function */
74 	if ((ptr = getfname(getbind(c))) == NULL)
75 		ptr = "Not Bound";
76 
77 	/* output the command sequence */
78 	ostring(ptr);
79 	return(TRUE);
80 }
81 
82 /* bindtokey:	add a new key to the key binding table		*/
83 
bindtokey(f,n)84 PASCAL NEAR bindtokey(f, n)
85 
86 int f, n;	/* command arguments [IGNORED] */
87 
88 {
89 	register unsigned int c;/* command key to bind */
90 	register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
91 	register KEYTAB *ktp;	/* pointer into the command table */
92 	register int found;	/* matched command flag */
93 	char outseq[80];	/* output buffer for keystroke sequence */
94 
95 	/* prompt the user to type in a key to bind */
96 	/* get the function name to bind it to */
97 	kfunc = getname(TEXT15);
98 /*			": bind-to-key " */
99 	if (kfunc == NULL) {
100 		mlwrite(TEXT16);
101 /*			"[No such function]" */
102 		return(FALSE);
103 	}
104 	if (clexec == FALSE) {
105 		ostring(" ");
106 		TTflush();
107 	}
108 
109 	/* get the command sequence to bind */
110 	c = getckey((kfunc == meta) || (kfunc == cex) ||
111 		    (kfunc == unarg) || (kfunc == ctrlg));
112 
113 	if (clexec == FALSE) {
114 
115 		/* change it to something we can print as well */
116 		/* and dump it out */
117 		ostring(cmdstr(c, &outseq[0]));
118 	}
119 
120 	/* if the function is a unique prefix key */
121 	if (kfunc == unarg || kfunc == ctrlg || kfunc == quote) {
122 
123 		/* search for an existing binding for the prefix key */
124 		ktp = &keytab[0];
125 		while (ktp->k_type != BINDNUL) {
126 			if (ktp->k_type == BINDFNC && ktp->k_ptr.fp == kfunc)
127 				unbindchar(ktp->k_code);
128 			++ktp;
129 		}
130 
131 		/* reset the appropriate global prefix variable */
132 		if (kfunc == unarg)
133 			reptc = c;
134 		if (kfunc == ctrlg)
135 			abortc = c;
136 		if (kfunc == quote)
137 			quotec = c;
138 	}
139 
140 	/* search the table to see if it exists */
141 	ktp = &keytab[0];
142 	found = FALSE;
143 	while (ktp->k_type != BINDNUL) {
144 		if (ktp->k_code == c) {
145 			found = TRUE;
146 			break;
147 		}
148 		++ktp;
149 	}
150 
151 	if (found) {	/* it exists, just change it then */
152 		ktp->k_ptr.fp = kfunc;
153 		ktp->k_type = BINDFNC;
154 	} else {	/* otherwise we need to add it to the end */
155 		/* if we run out of binding room, bitch */
156 		if (ktp >= &keytab[NBINDS]) {
157 			mlwrite(TEXT17);
158 /*				"Binding table FULL!" */
159 			return(FALSE);
160 		}
161 
162 		ktp->k_code = c;	/* add keycode */
163 		ktp->k_ptr.fp = kfunc;	/* and the function pointer */
164 		ktp->k_type = BINDFNC;	/* and the binding type */
165 		++ktp;			/* and make sure the next is null */
166 		ktp->k_code = 0;
167 		ktp->k_type = BINDNUL;
168 		ktp->k_ptr.fp = NULL;
169 	}
170 
171 	/* if we have rebound the meta key, make the
172 	   search terminators follow it			*/
173 	if (kfunc == meta) {
174 		sterm = c;
175 		isterm = c;
176 	}
177 
178 	return(TRUE);
179 }
180 
181 /* macrotokey:	Bind a key to a macro in the key binding table */
182 
macrotokey(f,n)183 PASCAL NEAR macrotokey(f, n)
184 
185 int f, n;	/* command arguments [IGNORED] */
186 
187 {
188 	register unsigned int c;/* command key to bind */
189 	register BUFFER *kmacro;/* ptr to buffer of macro to bind to key */
190 	register KEYTAB *ktp;	/* pointer into the command table */
191 	register int found;	/* matched command flag */
192 	register int status;	/* error return */
193 	char outseq[80];	/* output buffer for keystroke sequence */
194 	char bufn[NBUFN];	/* buffer to hold macro name */
195 
196 	/* get the buffer name to use */
197 	if ((status=mlreply(TEXT215, &bufn[1], NBUFN-2)) != TRUE)
198 /*		": macro-to-key " */
199 		return(status);
200 
201 	/* build the responce string for later */
202 	strcpy(outseq, TEXT215);
203 /*		   ": macro-to-key " */
204 	strcat(outseq, &bufn[1]);
205 
206 	/* translate it to a buffer pointer */
207 	bufn[0] = '[';
208 	strcat(bufn, "]");
209 	if ((kmacro=bfind(bufn, FALSE, 0)) == NULL) {
210 		mlwrite(TEXT130);
211 /*		"Macro not defined"*/
212 		return(FALSE);
213 	}
214 
215 	strcat(outseq, " ");
216 	mlwrite(outseq);
217 
218 	/* get the command sequence to bind */
219 	c = getckey(FALSE);
220 
221 	/* change it to something we can print as well */
222 	/* and dump it out */
223 	ostring(cmdstr(c, &outseq[0]));
224 
225 	/* search the table to see if it exists */
226 	ktp = &keytab[0];
227 	found = FALSE;
228 	while (ktp->k_type != BINDNUL) {
229 		if (ktp->k_code == c) {
230 			found = TRUE;
231 			break;
232 		}
233 		++ktp;
234 	}
235 
236 	if (found) {	/* it exists, just change it then */
237 		ktp->k_ptr.buf = kmacro;
238 		ktp->k_type = BINDBUF;
239 	} else {	/* otherwise we need to add it to the end */
240 		/* if we run out of binding room, bitch */
241 		if (ktp >= &keytab[NBINDS]) {
242 			mlwrite(TEXT17);
243 /*				"Binding table FULL!" */
244 			return(FALSE);
245 		}
246 
247 		ktp->k_code = c;	/* add keycode */
248 		ktp->k_ptr.buf = kmacro;	/* and the function pointer */
249 		ktp->k_type = BINDBUF;	/* and the binding type */
250 		++ktp;			/* and make sure the next is null */
251 		ktp->k_code = 0;
252 		ktp->k_type = BINDNUL;
253 		ktp->k_ptr.fp = NULL;
254 	}
255 
256 	return(TRUE);
257 }
258 
259 /* unbindkey:	delete a key from the key binding table */
260 
unbindkey(f,n)261 PASCAL NEAR unbindkey(f, n)
262 
263 int f, n;	/* command arguments [IGNORED] */
264 
265 {
266 	register int c; 	/* command key to unbind */
267 	char outseq[80];	/* output buffer for keystroke sequence */
268 
269 	/* prompt the user to type in a key to unbind */
270 	mlwrite(TEXT18);
271 /*		": unbind-key " */
272 
273 	/* get the command sequence to unbind */
274 	c = getckey(FALSE);		/* get a command sequence */
275 
276 	/* change it to something we can print as well */
277 	/* and dump it out */
278 	ostring(cmdstr(c, &outseq[0]));
279 
280 	/* if it isn't bound, bitch */
281 	if (unbindchar(c) == FALSE) {
282 		mlwrite(TEXT19);
283 /*			"[Key not bound]" */
284 		return(FALSE);
285 	}
286 	return(TRUE);
287 }
288 
unbindchar(c)289 PASCAL NEAR unbindchar(c)
290 
291 int c;		/* command key to unbind */
292 
293 {
294 	register KEYTAB *ktp;	/* pointer into the command table */
295 	register KEYTAB *sktp;	/* saved pointer into the command table */
296 	register int found;	/* matched command flag */
297 
298 	/* search the table to see if the key exists */
299 	ktp = &keytab[0];
300 	found = FALSE;
301 	while (ktp->k_type != BINDNUL) {
302 		if (ktp->k_code == c) {
303 			found = TRUE;
304 			break;
305 		}
306 		++ktp;
307 	}
308 
309 	/* if it isn't bound, bitch */
310 	if (!found)
311 		return(FALSE);
312 
313 	/* save the pointer and scan to the end of the table */
314 	sktp = ktp;
315 	while (ktp->k_type != BINDNUL)
316 		++ktp;
317 	--ktp;		/* backup to the last legit entry */
318 
319 	/* copy the last entry to the current one */
320 	sktp->k_code = ktp->k_code;
321 	sktp->k_type = ktp->k_type;
322 	if (sktp->k_type == BINDFNC)
323 		sktp->k_ptr.fp	 = ktp->k_ptr.fp;
324 	else if (sktp->k_type == BINDBUF)
325 		sktp->k_ptr.buf   = ktp->k_ptr.buf;
326 
327 	/* null out the last one */
328 	ktp->k_code = 0;
329 	ktp->k_type = BINDNUL;
330 	ktp->k_ptr.fp = NULL;
331 	return(TRUE);
332 }
333 
334 /* unbind all the keys bound to a buffer (which we can then delete */
335 
unbind_buf(bp)336 VOID PASCAL NEAR unbind_buf(bp)
337 
338 BUFFER *bp;	/* buffer to unbind all keys connected to */
339 
340 {
341 	register KEYTAB *ktp;	/* pointer into the command table */
342 
343 	/* search the table to see if the key exists */
344 	ktp = &keytab[0];
345 	while (ktp->k_type != BINDNUL) {
346 		if (ktp->k_type == BINDBUF) {
347 			if (ktp->k_ptr.buf == bp) {
348 				unbindchar(ktp->k_code);
349 				--ktp;
350 			}
351 		}
352 		++ktp;
353 	}
354 }
355 
356 /* Describe bindings:
357 
358 	   bring up a fake buffer and list the key bindings
359 	   into it with view mode
360 */
361 
desbind(f,n)362 PASCAL NEAR desbind(f, n)
363 
364 int f,n;	/* prefix flag and argument */
365 
366 {
367 	return(buildlist(TRUE, ""));
368 }
369 
apro(f,n)370 PASCAL NEAR apro(f, n)	/* Apropos (List functions that match a substring) */
371 
372 int f,n;	/* prefix flag and argument */
373 
374 {
375 	char mstring[NSTRING];	/* string to match cmd names to */
376 	int status;		/* status return */
377 
378 	status = mlreply(TEXT20, mstring, NSTRING - 1);
379 /*			 "Apropos string: " */
380 	if (status != TRUE)
381 		return(status);
382 
383 	return(buildlist(FALSE, mstring));
384 }
385 
buildlist(type,mstring)386 PASCAL NEAR buildlist(type, mstring)  /* build a binding list (limited or full) */
387 
388 int type;	/* true = full list,   false = partial list */
389 char *mstring;	/* match string if a partial list */
390 
391 {
392 	register KEYTAB *ktp;	/* pointer into the command table */
393 	register NBIND *nptr;	/* pointer into the name binding table */
394 	register BUFFER *listbuf;/* buffer to put binding list into */
395 	register BUFFER *bp;	/* buffer ptr for function scan */
396 	int cpos;		/* current position to use in outseq */
397 	char outseq[80];	/* output buffer for keystroke sequence */
398 	int first_entry;	/* is this the first macro listing? */
399 
400 	/* get a buffer for the binding list */
401 	listbuf = bfind(TEXT21, TRUE, BFINVS);
402 /*		   "Binding list" */
403 	if (listbuf == NULL || bclear(listbuf) == FALSE) {
404 		mlwrite(TEXT22);
405 /*			"Can not display binding list" */
406 		return(FALSE);
407 	}
408 
409 	/* let us know this is in progress */
410 	mlwrite(TEXT23);
411 /*		"[Building binding list]" */
412 
413 	/* build the contents of this window, inserting it line by line */
414 	nptr = &names[0];
415 	while (nptr->n_func != NULL) {
416 
417 		/* add in the command name */
418 		strcpy(outseq, nptr->n_name);
419 		cpos = strlen(outseq);
420 
421 		/* if we are executing an apropos command..... */
422 		if (type == FALSE &&
423 		    /* and current string doesn't include the search string */
424 		    strinc(outseq, mstring) == FALSE)
425 			goto fail;
426 
427 		/* search down any keys bound to this */
428 		ktp = &keytab[0];
429 		while (ktp->k_type != BINDNUL) {
430 			if (ktp->k_type == BINDFNC &&
431 			    ktp->k_ptr.fp == nptr->n_func) {
432 				/* padd out some spaces */
433 				while (cpos < 25)
434 					outseq[cpos++] = ' ';
435 
436 				/* add in the command sequence */
437 				cmdstr(ktp->k_code, &outseq[cpos]);
438 
439 				/* and add it as a line into the buffer */
440 				if (addline(listbuf, outseq) != TRUE)
441 					return(FALSE);
442 
443 				cpos = 0;	/* and clear the line */
444 			}
445 			++ktp;
446 		}
447 
448 		/* if no key was bound, we need to dump it anyway */
449 		if (cpos > 0) {
450 			outseq[cpos] = 0;
451 			if (addline(listbuf, outseq) != TRUE)
452 				return(FALSE);
453 		}
454 
455 fail:		/* and on to the next name */
456 		++nptr;
457 	}
458 
459 	/* scan all buffers looking for macroes and their bindings */
460 	first_entry = TRUE;
461 	bp = bheadp;
462 	while (bp) {
463 
464 		/* is this buffer a macro? */
465 		if (bp->b_bname[0] != '[')
466 			goto bfail;
467 
468 		/* add in the command name */
469 		strcpy(outseq, bp->b_bname);
470 		cpos = strlen(outseq);
471 
472 		/* if we are executing an apropos command..... */
473 		if (type == FALSE &&
474 		    /* and current string doesn't include the search string */
475 		    strinc(outseq, mstring) == FALSE)
476 			goto bfail;
477 
478 		/* search down any keys bound to this macro */
479 		ktp = &keytab[0];
480 		while (ktp->k_type != BINDNUL) {
481 			if (ktp->k_type == BINDBUF &&
482 			    ktp->k_ptr.buf == bp) {
483 				/* padd out some spaces */
484 				while (cpos < 25)
485 					outseq[cpos++] = ' ';
486 
487 				/* add in the command sequence */
488 				cmdstr(ktp->k_code, &outseq[cpos]);
489 
490 				/* and add it as a line into the buffer */
491 				if (addline(listbuf, outseq) != TRUE)
492 					return(FALSE);
493 
494 				cpos = 0;	/* and clear the line */
495 			}
496 			++ktp;
497 		}
498 
499 		/* add a blank line between the key and macro lists */
500 		if (first_entry == TRUE) {
501 			if (addline(listbuf, "") != TRUE)
502 				return(FALSE);
503 			first_entry = FALSE;
504 		}
505 
506 		/* if no key was bound, we need to dump it anyway */
507 		if (cpos > 0) {
508 			outseq[cpos] = 0;
509 			if (addline(listbuf, outseq) != TRUE)
510 				return(FALSE);
511 		}
512 
513 bfail:		/* and on to the next buffer */
514 		bp = bp->b_bufp;
515 	}
516 
517 	wpopup(listbuf);
518 	mlerase();	/* clear the mode line */
519 	return(TRUE);
520 }
521 
strinc(source,sub)522 PASCAL NEAR strinc(source, sub) /* does source include sub? */
523 
524 char *source;	/* string to search in */
525 char *sub;	/* substring to look for */
526 
527 {
528 	char *sp;	/* ptr into source */
529 	char *nxtsp;	/* next ptr into source */
530 	char *tp;	/* ptr into substring */
531 
532 	/* for each character in the source string */
533 	sp = source;
534 	while (*sp) {
535 		tp = sub;
536 		nxtsp = sp;
537 
538 		/* is the substring here? */
539 		while (*tp) {
540 			if (*nxtsp++ != *tp)
541 				break;
542 			else
543 				tp++;
544 		}
545 
546 		/* yes, return a success */
547 		if (*tp == 0)
548 			return(TRUE);
549 
550 		/* no, onward */
551 		sp++;
552 	}
553 	return(FALSE);
554 }
555 
556 /* get a command key sequence from the keyboard */
557 
getckey(mflag)558 unsigned int PASCAL NEAR getckey(mflag)
559 
560 int mflag;	/* going for a meta sequence? */
561 
562 {
563 	register unsigned int c;	/* character fetched */
564 	char tok[NSTRING];		/* command incoming */
565 
566 	/* check to see if we are executing a command line */
567 	if (clexec) {
568 		macarg(tok);	/* get the next token */
569 		return(stock(tok));
570 	}
571 
572 	/* or the normal way */
573 	if (mflag)
574 		c = get_key();
575 	else
576 		c = getcmd();
577 	return(c);
578 }
579 
580 /* execute the startup file */
581 
startup(sfname)582 PASCAL NEAR startup(sfname)
583 
584 char *sfname;	/* name of startup file (null if default) */
585 
586 {
587 	char *fname;	/* resulting file name to execute */
588 	char name[NSTRING];	/* name with extention */
589 
590 	/* look up the startup file */
591 	if (*sfname != 0) {
592 
593 	 	/* default the extention */
594 		strcpy(name, sfname);
595 		if (sindex(name, ".") == 0)
596 			strcat(name, ".cmd");
597 
598 		fname = flook(name, TRUE);
599 	} else
600 		fname = flook(pathname[0], TRUE);
601 
602 	/* if it isn't around, don't sweat it */
603 	if (fname == NULL)
604 		return(TRUE);
605 
606 	/* otherwise, execute the sucker */
607 	return(dofile(fname));
608 }
609 
610 /*	Look up the existance of a file along the normal or PATH
611 	environment variable.
612 
613 	LOOKUP ORDER:
614 
615 		if contains path:
616 
617 			absolute
618 
619 		else
620 
621 			HOME environment directory
622 			all directories along PATH environment
623 			directories in table from EPATH.H
624 */
625 
flook(fname,hflag)626 char *PASCAL NEAR flook(fname, hflag)
627 
628 char *fname;	/* base file name to search for */
629 int hflag;	/* Look in the HOME environment variable first? */
630 
631 {
632 	register char *home;	/* path to home directory */
633 	register char *path;	/* environmental PATH variable */
634 	register char *sp;	/* pointer into path spec */
635 	register int i; 	/* index */
636 	static char fspec[NFILEN];	/* full path spec to search */
637         char patha[NFILEN];
638 
639 	/* if we have an absolute path.. check only there! */
640 	sp = fname;
641 	while (*sp) {
642 		if (*sp == ':' || *sp == '\\' || *sp == '/') {
643 			if (ffropen(fname) == FIOSUC) {
644 				ffclose();
645 				return(fname);
646 			} else
647 				return(NULL);
648 		}
649 		++sp;
650 	}
651 
652 #if	ENVFUNC
653 
654 	if (hflag) {
655 #if WMCS
656 		home = getenv("SYS$HOME");
657 #else
658 		home = getenv("HOME");
659 #endif
660 		if (home != NULL) {
661 			/* build home dir file spec */
662 			strcpy(fspec, home);
663 #if WMCS
664 			strcat(fspec,fname);
665 #else
666 			strcat(fspec, DIRSEPSTR);
667 			strcat(fspec, fname);
668 #endif
669 
670 			/* and try it out */
671 			if (ffropen(fspec) == FIOSUC) {
672 				ffclose();
673 				return(fspec);
674 			}
675 		}
676 	}
677 #endif
678 
679 	/* current directory now overides everything except HOME var */
680 	if (ffropen(fname) == FIOSUC) {
681 		ffclose();
682 		return(fname);
683 	}
684 
685 #if	ENVFUNC
686 	/* get the PATH variable */
687 #if WMCS
688 	path = getenv("OPT$PATH");
689 #else
690 #if OS2
691 	path = getenv("DPATH");
692 #else
693 	strncpy(patha, getenv("PATH"), NFILEN - 25);
694         strcat(patha, ":/usr/local/share/uemacs");
695 #endif
696 #endif
697         path = patha;
698 	if (path != NULL)
699 		while (*path) {
700 
701 			/* build next possible file spec */
702 			sp = fspec;
703 #if	TOS
704 			while (*path && (*path != PATHCHR) && (*path != ','))
705 #else
706 			while (*path && (*path != PATHCHR))
707 #endif
708 				*sp++ = *path++;
709 
710 			/* add a terminating dir separator if we need it */
711 			if ((sp != fspec) && (*(sp-1) != DIRSEPCHAR))
712 				*sp++ = DIRSEPCHAR;
713 			*sp = 0;
714 			strcat(fspec, fname);
715 
716 			/* and try it out */
717 			if (ffropen(fspec) == FIOSUC) {
718 				ffclose();
719 				return(fspec);
720 			}
721 
722 #if	TOS && MWC
723 			if ((*path == PATHCHR) || (*path == ','))
724 #else
725 			if (*path == PATHCHR)
726 #endif
727 				++path;
728 		}
729 #endif
730 
731 	/* look it up via the old table method */
732 	for (i=2; i < NPNAMES; i++) {
733 		strcpy(fspec, pathname[i]);
734 		strcat(fspec, fname);
735 
736 		/* and try it out */
737 		if (ffropen(fspec) == FIOSUC) {
738 			ffclose();
739 			return(fspec);
740 		}
741 	}
742 
743 	return(NULL);	/* no such luck */
744 }
745 
746 /* Change a key command to a string we can print out.
747  * Return the string passed in.
748  */
cmdstr(c,seq)749 char *PASCAL NEAR cmdstr(c, seq)
750 
751 int c;		/* sequence to translate */
752 char *seq;	/* destination string for sequence */
753 
754 {
755 	char *ptr;	/* pointer into current position in sequence */
756 
757 	ptr = seq;
758 
759 	/* apply ^X sequence if needed */
760 	if (c & CTLX) {
761 		*ptr++ = '^';
762 		*ptr++ = 'X';
763 	}
764 
765 	/* apply ALT key sequence if needed */
766 	if (c & ALTD) {
767 		*ptr++ = 'A';
768 		*ptr++ = '-';
769 	}
770 
771 	/* apply Shifted sequence if needed */
772 	if (c & SHFT) {
773 		*ptr++ = 'S';
774 		*ptr++ = '-';
775 	}
776 
777 	/* apply MOUS sequence if needed */
778 	if (c & MOUS) {
779 		*ptr++ = 'M';
780 		*ptr++ = 'S';
781 	}
782 
783 	/* apply meta sequence if needed */
784 	if (c & META) {
785 		*ptr++ = 'M';
786 		*ptr++ = '-';
787 	}
788 
789 	/* apply SPEC sequence if needed */
790 	if (c & SPEC) {
791 		*ptr++ = 'F';
792 		*ptr++ = 'N';
793 	}
794 
795 	/* apply control sequence if needed */
796 	if (c & CTRL) {
797 
798 		/* non normal spaces look like @ */
799 		if (ptr == seq && ((c & 255) == ' '))
800 			c = '@';
801 
802 		*ptr++ = '^';
803 	}
804 
805 	c = c & 255;	/* strip the prefixes */
806 
807 	/* and output the final sequence */
808 	*ptr++ = c;
809 	*ptr = 0;	/* terminate the string */
810 	return (seq);
811 }
812 
813 /*	This function looks a key binding up in the binding table	*/
814 
getbind(c)815 KEYTAB *getbind(c)
816 
817 register int c;	/* key to find what is bound to it */
818 
819 {
820 	register KEYTAB *ktp;
821 
822 	/* scan through the binding table, looking for the key's entry */
823 	ktp = &keytab[0];
824 	while (ktp->k_type != BINDNUL) {
825 		if (ktp->k_code == c)
826 			return(ktp);
827 		++ktp;
828 	}
829 
830 	/* no such binding */
831 	return((KEYTAB *)NULL);
832 }
833 
834 /* getfname:	This function takes a ptr to KEYTAB entry and gets the name
835 		associated with it
836 */
837 
getfname(key)838 char *PASCAL NEAR getfname(key)
839 
840 KEYTAB *key;	/* key binding to return a name of */
841 
842 {
843 	int (PASCAL NEAR *func)(); /* ptr to the requested function */
844 	register NBIND *nptr;	/* pointer into the name binding table */
845 	register BUFFER *bp;	/* ptr to buffer to test */
846 	register BUFFER *kbuf;	/* ptr to requested buffer */
847 
848 	/* if this isn't a valid key, it has no name */
849 	if (key == NULL)
850 		return(NULL);
851 
852 	/* skim through the binding table, looking for a match */
853 	if (key->k_type == BINDFNC) {
854 		func = key->k_ptr.fp;
855 		nptr = &names[0];
856 		while (nptr->n_func != NULL) {
857 			if (nptr->n_func == func)
858 				return(nptr->n_name);
859 			++nptr;
860 		}
861 		return(NULL);
862 	}
863 
864 	/* skim through the buffer list looking for a match */
865 	if (key->k_type == BINDBUF) {
866 		kbuf = key->k_ptr.buf;
867 		bp = bheadp;
868 		while (bp) {
869 			if (bp == kbuf)
870 				return(bp->b_bname);
871 			bp = bp->b_bufp;
872 		}
873 		return(NULL);
874 	}
875 	return(NULL);
876 }
877 
878 /* fncmatch:	match fname to a function in the names table and return
879 		any match or NULL if none */
880 
881 #if	MSC
882 int (PASCAL NEAR *PASCAL NEAR fncmatch(char *fname))(void)
883 #else
884 int (PASCAL NEAR *PASCAL NEAR fncmatch(fname))()
885 
886 char *fname;	/* name to attempt to match */
887 #endif
888 
889 {
890 	int nval;
891 
892 	if ((nval = binary(fname, namval, numfunc, NSTRING)) == -1)
893 		return(NULL);
894 	else
895 		return(names[nval].n_func);
896 }
897 
namval(index)898 char *PASCAL NEAR namval(index)
899 
900 int index;	/* index of name to fetch out of the name table */
901 
902 {
903 	return(names[index].n_name);
904 }
905 
906 /*	stock() 	String key name TO Command Key
907 
908 	A key binding consists of one or more prefix functions followed by
909 	a keystroke.  Allowable prefixes must be in the following order:
910 
911 	^X	preceeding control-X
912 	A-	similtaneous ALT key (on PCs mainly)
913 	S-	shifted function key
914 	MS	mouse generated keystroke
915 	M-	Preceding META key
916 	FN	function key
917 	^	control key
918 
919 	Meta and ^X prefix of lower case letters are converted to upper
920 	case.  Real control characters are automatically converted to
921 	the ^A form.
922 */
923 
stock(keyname)924 unsigned int PASCAL NEAR stock(keyname)
925 
926 unsigned char *keyname;	/* name of key to translate to Command key form */
927 
928 {
929 	register unsigned int c;	/* key sequence to return */
930 
931 	/* parse it up */
932 	c = 0;
933 
934 	/* Do ^X prefix */
935 	if(*keyname == '^' && *(keyname+1) == 'X') {
936 		if(*(keyname+2) != 0) { /* Key is not bare ^X */
937 		    c |= CTLX;
938 		    keyname += 2;
939 		}
940 	}
941 
942 	/* and the ALT key prefix */
943 	if (*keyname == 'A' && *(keyname+1) == '-') {
944 		c |= ALTD;
945 		keyname += 2;
946 	}
947 
948 	/* and the SHIFTED prefix */
949 	if (*keyname == 'S' && *(keyname+1) == '-') {
950 		c |= SHFT;
951 		keyname += 2;
952 	}
953 
954 	/* and the mouse (MOUS) prefix */
955 	if (*keyname == 'M' && *(keyname+1) == 'S') {
956 		c |= MOUS;
957 		keyname += 2;
958 	}
959 
960 	/* then the META prefix */
961 	if (*keyname == 'M' && *(keyname+1) == '-') {
962 		c |= META;
963 		keyname += 2;
964 	}
965 
966 	/* next the function prefix */
967 	if (*keyname == 'F' && *(keyname+1) == 'N') {
968 		c |= SPEC;
969 		keyname += 2;
970 	}
971 
972 	/* a control char?  (NOT Always upper case anymore) */
973 	if (*keyname == '^' && *(keyname+1) != 0) {
974 		c |= CTRL;
975 		++keyname;
976 		if (*keyname == '@')
977 			*keyname = ' ';
978 	}
979 
980 	/* A literal control character? (Boo, hiss) */
981 	if (*keyname < 32) {
982 		c |= CTRL;
983 		*keyname += '@';
984 	}
985 
986 	/* make sure we are not lower case if used with ^X or M- */
987 	if(!(c & (MOUS|SPEC|ALTD|SHFT)))	/* If not a special key */
988 	    if( c & (CTLX|META))		/* If is a prefix */
989 		uppercase((unsigned char *)keyname);		/* Then make sure it's upper case */
990 
991 	/* the final sequence... */
992 	c |= *keyname;
993 	return(c);
994 }
995 
transbind(skey)996 char *PASCAL NEAR transbind(skey)	/* string key name to binding name.... */
997 
998 char *skey;	/* name of key to get binding for */
999 
1000 {
1001 	char *bindname;
1002 
1003 	bindname = getfname(getbind(stock(skey)));
1004 	if (bindname == NULL)
1005 		bindname = errorm;
1006 
1007 	return(bindname);
1008 }
1009 
execkey(key,f,n)1010 int PASCAL NEAR execkey(key, f, n)	/* execute a function bound to a key */
1011 
1012 KEYTAB *key;	/* key to execute */
1013 int f, n;	/* agruments to C function */
1014 
1015 {
1016 	register int status;	/* error return */
1017 
1018 	if (key->k_type == BINDFNC) {
1019 		undo_insert(OP_CMND, 1, obj);
1020 		return((*(key->k_ptr.fp))(f, n));
1021 	}
1022 	if (key->k_type == BINDBUF) {
1023 		while (n--) {
1024 			status = dobuf(key->k_ptr.buf);
1025 			if (status != TRUE)
1026 				return(status);
1027 		}
1028 	}
1029 	return(TRUE);
1030 }
1031 
1032 /* set a KEYTAB to the given name of the given type */
1033 
1034 #if	PROTO
set_key(KEYTAB * key,char * name)1035 int set_key(KEYTAB *key, char *name)
1036 #else
1037 set_key(key, name)
1038 
1039 KEYTAB *key;		/* ptr to key to set */
1040 char *name;		/* name of function or buffer */
1041 #endif
1042 {
1043 	int (PASCAL NEAR *ktemp)();	/* temp function pointer to assign */
1044 	register BUFFER *kmacro;	/* ptr to buffer of macro to bind to key */
1045 	char bufn[NBUFN];		/* buffer to hold macro name */
1046 
1047 	/* are we unbinding it? */
1048 	if (*name == 0) {
1049 		key->k_type = BINDNUL;
1050 		return(TRUE);
1051 	}
1052 
1053 	/* bind to a built in function? */
1054 	if ((ktemp = fncmatch(name)) != NULL) {
1055 		key->k_ptr.fp = ktemp;
1056 		key->k_type = BINDFNC;
1057 		return(TRUE);
1058 	}
1059 
1060 	/* is it a procedure/macro? */
1061 	strcpy(bufn, "[");
1062 	strcat(bufn, name);
1063 	strcat(bufn, "]");
1064 	if ((kmacro=bfind(bufn, FALSE, 0)) != NULL) {
1065 		key->k_ptr.buf = kmacro;
1066 		key->k_type = BINDBUF;
1067 		return(TRUE);
1068 	}
1069 
1070 	/* not anything we can bind to */
1071 	mlwrite(TEXT16);
1072 /*		"[No such function]" */
1073 	return(FALSE);
1074 }
1075 
1076