1 /* @(#)map.c	1.42 20/02/27 Copyright 1986-2020 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)map.c	1.42 20/02/27 Copyright 1986-2020 J. Schilling";
6 #endif
7 /*
8  *	The map package for BSH & VED
9  *
10  *	Copyright (c) 1986-2020 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 /*
27  * If a map is found, it is loaded into 'mapstr' and then read from 'mapstr'.
28  * No recursion is allowed on maps.
29  *
30  * The following external routines are available:
31  *
32  *	map_init	- Init the package and load the map file.
33  *	rxmap		- Look for a map and put it into 'mapstr'.
34  *			  Also sets 'mapflag' as an intication that 'gmap()'
35  *			  should be used to read further characters.
36  *			  The macro 'rmap()' should be used in favor of rxmap()
37  *	gmap		- Get the next character from the macro string.
38  *			  Returns '0' and resets 'mapflag' at the end of a
39  *			  translation.
40  *	mapgetc		- Used to read characters if no mapping is loaded.
41  *	remap		- Set the 'mp_init' flag as a notice that map_init()
42  *			  should be called.
43  *	add_map		- Add a new map translation
44  *	del_map		- Delete a map translation
45  *
46  * Uses getnextc() to read characters and tdecode to decode escape sequences.
47  *
48  * The global 'mapflag' is used to know whether the input should be
49  * taken from the map string by calling 'gmap()' in favour of 'mapgetc()'.
50  */
51 
52 #include <schily/stdio.h>
53 
54 #ifdef	BSH
55 #include "bsh.h"
56 #include "str.h"
57 #include "strsubs.h"
58 #define	C	(char *)
59 #else
60 #include <schily/standard.h>
61 #include "ved.h"
62 #endif
63 
64 #include <schily/stdlib.h>
65 #include <schily/string.h>
66 #include "map.h"
67 #include "ctype.h"
68 #include <schily/termcap.h>
69 #include <schily/errno.h>
70 
71 #ifndef	BSH
72 #define	INTERACTIVE
73 #define	strbeg(x, y)	(strstr((y), (x)) == (y))
74 
75 char	slash[] = "/";
76 char	mapname[] = ".vedmap";
77 char	for_read[] = "rb";
78 /*
79  * Use non-interruptable version
80  */
81 #define	getnextc	nigetnextc
82 
83 #else
84 #define	Uchar	unsigned char
85 #define	UC	(unsigned char *)
86 #endif
87 
88 #ifdef	INTERACTIVE
89 
90 #define	M_NAMELEN	(unsigned)16
91 #define	M_STRINGLEN	(unsigned)128
92 
93 typedef struct {
94 		int	st_cnt;
95 		Uchar	*st_bp;
96 		Uchar	st_buf[M_NAMELEN + 1];
97 } maps_t;
98 
99 typedef struct m_map {
100 	struct	m_map	*m_next;
101 	char		m_from[M_NAMELEN + 1];
102 	char		m_to[M_STRINGLEN + 1];
103 	char		*m_comment;
104 } smap_t;
105 
106 EXPORT	Uchar	maptab[256];
107 EXPORT	int	mapflag;
108 EXPORT	BOOL	mp_init		= TRUE;
109 
110 LOCAL	char	*mapstr;
111 
112 LOCAL	smap_t	*first_map;
113 LOCAL	maps_t	map_str;
114 
115 LOCAL	void	init_mapstream	__PR((void));
116 #ifdef	BSH
117 EXPORT	int	mapgetc		__PR((void));
118 #else
119 EXPORT	int	mapgetc		__PR((ewin_t *wp));
120 #endif
121 LOCAL	void	pushmap		__PR((char *sp, int n));
122 EXPORT	void	map_init	__PR((void));
123 #ifdef	BSH
124 EXPORT	int	rxmap		__PR((int c));
125 #else
126 EXPORT	int	rxmap		__PR((ewin_t *wp, int c));
127 #endif
128 EXPORT	int	gmap		__PR((void));
129 EXPORT	void	remap		__PR((void));
130 EXPORT	BOOL	add_map		__PR((char *from, char *to, char *comment));
131 EXPORT	BOOL	del_map		__PR((char *from));
132 LOCAL	BOOL	_add_map	__PR((Uchar *mn, Uchar *ms, char *comment));
133 LOCAL	BOOL	_del_map	__PR((char *mn));
134 #ifdef	BSH
135 EXPORT	void	list_map	__PR((FILE *f));
136 LOCAL	char	*get_map	__PR((int c));
137 #else
138 EXPORT	void	list_map	__PR((ewin_t *wp));
139 LOCAL	char	*get_map	__PR((ewin_t *wp, int c));
140 #endif
141 LOCAL	BOOL	_has_map	__PR((Uchar *mn));
142 LOCAL	BOOL	fallback_map	__PR((char *from, char *to, char *comment));
143 LOCAL	void	init_cursor_maps __PR((void));
144 #ifndef	BSH
145 EXPORT	void	init_fk_maps	__PR((void));
146 LOCAL	char	*pretty_string	__PR((Uchar *s));
147 #endif
148 LOCAL	void	init_fallback_maps __PR((void));
149 
150 /*
151  * Initialize the intermediate character stack.
152  * This character stack is used to store already read characters
153  * so that thay are not lost after we discovered that there is
154  * no map that starts with a specific sequence.
155  * This character stack handles null bytes correctly.
156  */
157 LOCAL void
init_mapstream()158 init_mapstream()
159 {
160 	map_str.st_cnt = 0;
161 	*(map_str.st_bp = map_str.st_buf) = '\0';
162 }
163 
164 /*
165  * Get the next character (either from low level input or from the
166  * intermediate character stack).
167  */
168 EXPORT int
169 #ifdef	BSH
mapgetc()170 mapgetc()
171 #else
172 mapgetc(wp)
173 	ewin_t	*wp;
174 #endif
175 {
176 	if (map_str.st_cnt > 0) {
177 		map_str.st_cnt--;
178 		return (*map_str.st_bp++);
179 	} else {
180 #ifdef	BSH
181 		return (getnextc());
182 #else
183 		return (getnextc(wp));
184 #endif
185 	}
186 }
187 
188 /*
189  * Push a sequence of characters on the intermadiate character stack.
190  * These charcaters have been read while following a map start sequence.
191  */
192 LOCAL void
pushmap(sp,n)193 pushmap(sp, n)
194 	char	*sp;
195 	int	n;
196 {
197 	register int	i;
198 	register char	*p1;
199 	register char	*p2;
200 
201 	/*
202 	 * Move the old contents to the proper place
203 	 */
204 	i = map_str.st_cnt;
205 	p1 = (char *)map_str.st_bp;
206 	p2 = (char *)&map_str.st_buf[n];
207 	if (p1 != p2) {
208 		while (--i >= 0)
209 			*p2++ = *p1++;
210 	}
211 
212 	/*
213 	 * Insert new stuff before old contents
214 	 */
215 	i = n;
216 	p2 = (char *)map_str.st_buf;
217 	p1 = sp;
218 	while (--i >= 0)
219 		*p2++ = *p1++;
220 	map_str.st_cnt += n;
221 	map_str.st_bp = map_str.st_buf;
222 }
223 
224 /*
225  * Initialize the map package and load the map file.
226  */
227 EXPORT void
map_init()228 map_init()
229 {
230 #define	BUF_SIZE	8192
231 	register FILE	*f;
232 		char	mapfname[512];
233 		char	linebuf[BUF_SIZE+1];	/* + space for null byte */
234 		char	*array[3];
235 		char	*home;
236 	register char	**ap;
237 	register char	*lp;
238 	register int	amt;
239 
240 	home = myhome();
241 	if (home != NULL) {
242 		snprintf(mapfname, sizeof (mapfname), "%s%s%s",
243 						myhome(), slash, mapname);
244 	} else {
245 		strcpy(mapfname, mapname);
246 	}
247 
248 
249 #ifdef	BSH
250 	/*
251 	 * Der ved kann z.Zt. noch kein map/remap nach der
252 	 * Initialisierung.
253 	 *
254 	 */
255 	while (first_map)
256 		_del_map(first_map->m_from);
257 #endif
258 	mp_init = FALSE;
259 	init_mapstream();
260 	init_cursor_maps();
261 	if ((f = fileopen(mapfname, for_read)) == (FILE *)NULL) {
262 		if (geterrno() == ENOENT)
263 			goto fallback;
264 #ifdef	BSH
265 		berror(ecantopen, mapfname, errstr(geterrno()));
266 #else
267 		errmsg("Cannot open '%s'.\n", mapfname);
268 #endif
269 		goto fallback;
270 	}
271 
272 	ap = array;
273 	lp = linebuf;
274 	amt = BUF_SIZE;
275 	linebuf[amt] = '\0';			/* Final null byte after buf */
276 
277 	while ((amt = fileread(f, lp, amt)) > 0) {
278 		register char	*ep;
279 
280 		amt += lp - linebuf;		/* Continue on whole rest */
281 		lp = linebuf;
282 
283 	again:
284 		ep = strchr(lp, '\n');
285 		if (ep == NULL && lp > linebuf && amt >= BUF_SIZE) {
286 			/*
287 			 * If no '\n' could be found, we need to check whether
288 			 * we are in the middle of a line. If the buffer was
289 			 * not full, we are at EOF already.
290 			 */
291 			amt = amt - (lp - linebuf);	/* Unprocessed amt */
292 			movebytes(lp, linebuf, amt);	/* Move to start   */
293 			lp = &linebuf[amt];		/* Point past old  */
294 			amt = BUF_SIZE - amt;		/* Compute remaining */
295 			continue;			/* Fill up buf	   */
296 		}
297 		if (ep)					/* Buf contains '\n' */
298 			*ep = '\0';			/* so clear it	   */
299 
300 		if (breakline(lp, ':', ap, 3) < 2)
301 			continue;
302 		if (ap[1][0] == '\0' && ap[2][0] == '*') {
303 			/*
304 			 * If the to string is empty and the comment starts
305 			 * with a '*', delete an unwanted mapping that may
306 			 * have been introduced from termcap.
307 			 */
308 			del_map(ap[0]);
309 		} else if (!add_map(ap[0], ap[1], ap[2])) {
310 			/*EMPTY*/
311 #ifdef	DEBUG_ALREADY
312 error("'%s' already defined.", pretty_string(UC ap[0]));
313 #endif
314 			;
315 		}
316 
317 		if (ep) {			/* Found '\n', check rest */
318 			lp = &ep[1];
319 			if ((lp - linebuf) >= amt && amt < BUF_SIZE) /* EOF */
320 				break;
321 			goto again;
322 		} else {
323 			if (amt < BUF_SIZE)			    /* EOF */
324 				break;
325 			lp = linebuf;
326 			amt = BUF_SIZE;
327 		}
328 	}
329 	fclose(f);
330 fallback:
331 	init_fallback_maps();
332 }
333 
334 /*
335  * Look for map and load it into 'mapstr' if found.
336  */
337 EXPORT int
338 #ifdef	BSH
rxmap(c)339 rxmap(c)
340 #else
341 rxmap(wp, c)
342 	ewin_t	*wp;
343 #endif
344 	int	c;
345 {
346 	if (mapflag) {
347 #ifdef	BSH
348 		berror("\nMAP ABORTED");	/* only one map at a time */
349 #else
350 		writeerr(wp, "MAP ABORTED");	/* only one map at a time */
351 		/*
352 		 * May be flushed immediately by following characters.
353 		 */
354 		sleep(1);
355 #endif
356 		return (FALSE);
357 	}
358 #ifdef	BSH
359 	if ((mapstr = get_map(c)) == NULL)
360 #else
361 	if ((mapstr = get_map(wp, c)) == NULL)
362 #endif
363 		mapflag = 0;
364 	else
365 		mapflag++;
366 	return (mapflag);
367 }
368 
369 /*
370  * Get the next character from the map replacement string.
371  */
372 EXPORT int
gmap()373 gmap()
374 {
375 	char	c;
376 
377 	if ((c = *mapstr++) == 0)
378 		mapflag--;
379 	return ((Uchar)c);
380 }
381 
382 /*
383  * Set mp_init to force a call of map_init() to reload the map file.
384  */
385 EXPORT void
remap()386 remap()
387 {
388 	mp_init = TRUE;
389 }
390 
391 /*
392  * Add a mapping. Use tdecode() to decode escape sequences.
393  */
394 EXPORT BOOL
add_map(from,to,comment)395 add_map(from, to, comment)
396 	char	*from;
397 	char	*to;
398 	char	*comment;
399 {
400 	char	froms[M_NAMELEN + 1];
401 	char	tos[M_STRINGLEN + 1];
402 	char	*pf;
403 	char	*pt;
404 
405 	if (strlen(from) > M_NAMELEN || strlen(to) > M_STRINGLEN)
406 		return (FALSE);
407 
408 	pf = froms;
409 	pt = tos;
410 	return (_add_map(UC tdecode(from, &pf), UC tdecode(to, &pt), comment));
411 }
412 
413 /*
414  * Delete a mapping. Use tdecode() to decode escape sequences.
415  */
416 EXPORT BOOL
del_map(from)417 del_map(from)
418 	char	*from;
419 {
420 	char	froms[M_NAMELEN + 1];
421 	char	*pf;
422 
423 	if (strlen(from) > M_NAMELEN)
424 		return (FALSE);
425 
426 	pf = froms;
427 	return (_del_map(tdecode(from, &pf)));
428 }
429 
430 /*
431  * Add a new map to the list of known maps.
432  */
433 LOCAL BOOL
_add_map(mn,ms,comment)434 _add_map(mn, ms, comment)
435 	register	Uchar	*mn;
436 			Uchar	*ms;
437 			char	*comment;
438 {
439 	register	smap_t	*np;
440 	register	smap_t	*tn;
441 	register	smap_t	*last;
442 	register	int	cmp;
443 
444 	if (streql((char *)mn, (char *)ms))
445 		return (FALSE);
446 	/*
447 	 * First create and init new map node.
448 	 */
449 	tn = (smap_t *)malloc(sizeof (*tn));
450 	if (tn == (smap_t *)NULL)
451 		return (FALSE);
452 #ifdef	__support_null__
453 	*movebytes((char *)mn, (char *)tn->m_from, M_NAMELEN) = '\0';
454 	*movebytes((char *)ms, (char *)tn->m_to, M_STRINGLEN) = '\0';
455 #else
456 	strlcpy((char *)tn->m_from, (char *)mn, sizeof (tn->m_from));
457 	strlcpy((char *)tn->m_to,   (char *)ms, sizeof (tn->m_to));
458 #endif
459 	if (comment) {
460 		tn->m_comment = malloc(strlen(comment)+1);
461 		if (tn->m_comment)
462 			strcpy(tn->m_comment, comment);
463 	} else {
464 		tn->m_comment = NULL;
465 	}
466 	tn->m_next = (smap_t *)NULL;
467 
468 	if (++maptab[*mn] > 254) {		/* Too many Entrys */
469 		if (tn->m_comment)
470 			free(tn->m_comment);
471 		free((char *)tn);
472 		maptab[*mn]--;
473 		return (FALSE);
474 	}
475 
476 	if (first_map == (smap_t *)NULL) {
477 		first_map = tn;
478 		return (TRUE);
479 	}
480 
481 	/*
482 	 * Insert new map in order.
483 	 */
484 	np = last = first_map;
485 	for (; ; np = np->m_next) {
486 		if (np == (smap_t *)NULL) {
487 			/*
488 			 * Append to end of list
489 			 */
490 			last->m_next = tn;
491 			return (TRUE);
492 		}
493 
494 		cmp = strcmp((char *)mn, np->m_from);
495 
496 		if (cmp == 0) {
497 			/*
498 			 * Map is already defined
499 			 */
500 			if (tn->m_comment)
501 				free(tn->m_comment);
502 			free((char *)tn);
503 			maptab[*mn]--;
504 			return (FALSE);
505 		}
506 		if (cmp < 0) {
507 			if (first_map == np) {
508 				/*
509 				 * Make it the first in list.
510 				 */
511 				tn->m_next = first_map;
512 				first_map = tn;
513 				return (TRUE);
514 			} else {
515 				/*
516 				 * Insert in list
517 				 */
518 				last->m_next = tn;
519 				tn->m_next = np;
520 				return (TRUE);
521 			}
522 		}
523 		last = np;
524 	}
525 }
526 
527 
528 /*
529  * Delete a map
530  */
531 LOCAL BOOL
_del_map(mn)532 _del_map(mn)
533 	register	char	*mn;
534 {
535 	register	smap_t	*np = first_map;
536 	register	smap_t	*tn;
537 
538 	if (np == NULL)
539 		return (FALSE);
540 	if (streql(mn, np->m_from)) {
541 		first_map = np->m_next;
542 		if (np->m_comment)
543 			free(np->m_comment);
544 		free((char *)np);
545 		maptab[(Uchar) *mn]--;
546 		return (TRUE);
547 	}
548 	for (; ; np = np->m_next) {
549 		if (np->m_next == (smap_t *)NULL) {
550 #ifdef	BSH
551 			berror("'%s' not found", mn);
552 			ex_status = 1;
553 #else
554 #ifdef	__use_writerr_on_not_found__
555 			writeerr(wp, "'%s' not found", mn);
556 #else
557 			error("'%s' not found", mn);
558 #endif
559 #endif
560 			return (FALSE);
561 		}
562 		if (streql(mn, np->m_next->m_from)) {
563 			tn = np->m_next;
564 			np->m_next = np->m_next->m_next;
565 			if (tn->m_comment)
566 				free(tn->m_comment);
567 			free((char *)tn);
568 			maptab[(Uchar) *mn]--;
569 			return (TRUE);
570 		}
571 	}
572 }
573 
574 
575 /*
576  * Lists all maps
577  */
578 EXPORT void
579 #ifdef	BSH
list_map(f)580 list_map(f)
581 	register	FILE	*f;
582 #else
583 list_map(wp)
584 	ewin_t	*wp;
585 #endif
586 {
587 	register	smap_t	*np;
588 
589 	for (np = first_map; np; np = np->m_next) {
590 #ifdef	BSH
591 #ifdef	LIB_SHEDIT
592 		if (*f == STDOUT_FILENO && isatty(*f)) {
593 #else
594 		if (f == stdout) {
595 #endif
596 			printf("%-16s ", pretty_string(UC np->m_from));
597 			printf("%-16s", pretty_string(UC np->m_to));
598 		} else {
599 			fprintf(f, "%-16s %-16s", np->m_from, np->m_to);
600 		}
601 		if (np->m_comment)
602 			fprintf(f, "%s", np->m_comment);
603 		fprintf(f, "\n");
604 #else
605 		printscreen(wp, "%-16s ", pretty_string(UC np->m_from));
606 		printscreen(wp, "%-16s", pretty_string(UC np->m_to));
607 		if (np->m_comment)
608 			printscreen(wp, " %s\n", np->m_comment);
609 		else
610 			printscreen(wp, "\n");
611 #endif
612 	}
613 }
614 
615 /*
616  * Do a lookup for a map.
617  * Return the mapped string on success, else return NULL.
618  */
619 LOCAL char *
620 #ifdef	BSH
get_map(c)621 get_map(c)
622 #else
623 get_map(wp, c)
624 	ewin_t	*wp;
625 #endif
626 	char	c;
627 {
628 			char	m_from[M_NAMELEN + 1];
629 	register	smap_t	*tn;
630 	register	int	i;
631 	register	char	*cp;
632 	register	char	*name;
633 
634 #ifndef	BSH
635 #ifdef	GETMAP_DEBUG
636 writeerr("getm %d", c);
637 #endif
638 #endif
639 	cp = name = m_from;
640 	*cp++ = c;
641 	tn = first_map;
642 	for (i = 0; i < M_NAMELEN; i++) {
643 		*cp = '\0';
644 		for (; ; tn = tn->m_next) {
645 			if (tn == (smap_t *)NULL) {
646 				pushmap(&name[1], cp - &name[1]);
647 				return (NULL);
648 			}
649 #ifdef	GETMAP_DEBUG
650 cdbg("name '%s' from '%s' %d", name, pretty_string(tn->m_from), cp - name);
651 #endif
652 			if (strcmp(name, tn->m_from) == 0)
653 				return (tn->m_to);
654 			if (cmpbytes(name, tn->m_from, cp-name) >= (cp-name))
655 				break;
656 		}
657 #ifdef	GETMAP_DEBUG
658 cdbg("mapgetc()");
659 #endif
660 #ifdef	BSH
661 		*cp++ = mapgetc();	/* XXX EOF ??? */
662 #else
663 		*cp++ = mapgetc(wp);	/* XXX EOF ??? */
664 #endif
665 #ifndef	BSH
666 #ifdef	GETMAP_DEBUG
667 writeerr("mapg %d", cp[-1]);
668 #endif
669 #endif
670 	}
671 	return (NULL); /* XXX NOTREACHED ??? */
672 }
673 
674 /*
675  * Check whether a mapping for "nm" already exists in out current map tables.
676  */
677 LOCAL BOOL
_has_map(mn)678 _has_map(mn)
679 	Uchar	*mn;
680 {
681 	register	smap_t	*np;
682 
683 	if (maptab[*mn] == 0)
684 		return (FALSE);
685 
686 	for (np = first_map; np; np = np->m_next) {
687 		if (streql(C mn, np->m_from))
688 			return (TRUE);
689 	}
690 	return (FALSE);
691 }
692 
693 /*
694  * Install a fallback mapping in case no other mapping for the "from" string
695  * has been been set up yet.
696  *
697  * This allows ved/bsh/bosh to work nicely even when the user did not yet
698  * install own rc files with mappings in his $HOME.
699  */
700 LOCAL BOOL
fallback_map(from,to,comment)701 fallback_map(from, to, comment)
702 	char	*from;
703 	char	*to;
704 	char	*comment;
705 {
706 	char	froms[M_NAMELEN + 1];
707 	char	tos[M_STRINGLEN + 1];
708 	char	*pf;
709 	char	*pt;
710 
711 	if (strlen(from) > M_NAMELEN || strlen(to) > M_STRINGLEN)
712 		return (FALSE);
713 
714 	pf = froms;
715 	pt = tos;
716 	tdecode(from, &pf);
717 	tdecode(to, &pt);
718 
719 	if (_has_map(UC froms))
720 		return (FALSE);
721 
722 	return (_add_map(UC froms, UC tos, comment));
723 }
724 
725 #ifndef BSH
726 
727 /*
728  * Initialize cursor mappings for ved. Tgetent has been called before.
729  */
730 LOCAL void
init_cursor_maps()731 init_cursor_maps()
732 {
733 	extern char	*KU;
734 	extern char	*KD;
735 	extern char	*KR;
736 	extern char	*KL;
737 
738 	if (KU) {
739 		_add_map(UC KU, UC "", "Cursor up");
740 	} else {
741 		_add_map(UC "\33OA", UC "", "Cursor up");
742 		_add_map(UC "\33[A", UC "", "Cursor up");
743 	}
744 	if (KD) {
745 		_add_map(UC KD, UC "", "Cursor down");
746 	} else {
747 		_add_map(UC "\33OB", UC "", "Cursor down");
748 		_add_map(UC "\33[B", UC "", "Cursor down");
749 	}
750 	if (KR) {
751 		_add_map(UC KR, UC "", "Cursor forward");
752 	} else {
753 		_add_map(UC "\33OC", UC "", "Cursor forward");
754 		_add_map(UC "\33[C", UC "", "Cursor forward");
755 	}
756 	if (KL) {
757 		_add_map(UC KL, UC "", "Cursor left");
758 	} else {
759 		_add_map(UC "\33OD", UC "", "Cursor left");
760 		_add_map(UC "\33[D", UC "", "Cursor left");
761 	}
762 }
763 
764 LOCAL struct fk_maps {
765 	char	*fk_tc;
766 	Uchar	*fk_map;
767 	char	*fk_comment;
768 } fk_maps[] = {
769 	{ "k0", 0,	0 },
770 	{ "k1", UC "", "Quit Editor  (F1)" },
771 	{ "k2", UC "", "Top of File  (F2)" },
772 	{ "k3", UC "", "Delete char  (F3)" },
773 	{ "k4", UC "", "Delete line  (F4)" },
774 	{ "k5", UC "", "Open line    (F5)" },
775 	{ "k6", UC "", "Cut line     (F6)" },
776 	{ "k7", UC "", "Paste        (F7)" },
777 	{ "k8", UC "", "Change buffer(F8)" },
778 	{ "k9", UC "", "Search down  (F9)" },
779 #ifdef	__coment__
780 	{ "k;", UC "^Z", "Re search    (F10)" },
781 /* XXX Real ^Z replaced by ^ Z to allow compilation on DOS/WNT */
782 #endif
783 	{ "k;", UC "\032", "Re search    (F10)" },
784 
785 	{ "F1", UC "", "Get from     (F11)" },
786 	{ "F2", UC "", "Write to     (F12)" },
787 
788 	{ "kA", UC "",	"Insert line" },
789 	{ "kD", UC "\177",	"Delete char" },
790 	{ "kb", UC "\177",	"Key Backspace -> Delete char" },
791 	{ "kE", UC "",	"Delete to eol" },
792 	{ "@7", UC "",	"Go to eol" },
793 	{ "kh", UC "",	"Go to sol" },
794 	{ "kL", UC "",	"Delete line" },
795 	{ "kN", UC "n",	"Page down"},
796 	{ "kP", UC "p",	"Page up"},
797 				/* \015 was ^M before Mac OS X */
798 	{ "kS",	UC "999999\015",	"Delete to end of screen" },
799 
800 	{ 0, 0, 0},
801 };
802 #ifdef VI
803 {					/* Command mappings. */
804 	{"kA",    "O",	"insert line"},
805 	{"kD",    "x",	"delete character"},
806 	{"kd",    "j",	"cursor down"},
807 	{"kE",    "D",	"delete to eol"},
808 	{"kF", "\004",	"scroll down"},
809 	{"kH",    "$",	"go to eol"},
810 	{"kh",    "^",	"go to sol"},
811 	{"kI",    "i",	"insert at cursor"},
812 	{"kL",   "dd",	"delete line"},
813 	{"kl",    "h",	"cursor left"},
814 	{"kN", "\006",	"page down"},
815 	{"kP", "\002",	"page up"},
816 	{"kR", "\025",	"scroll up"},
817 	{"kS",	 "dG",	"delete to end of screen"},
818 	{"kr",    "l",	"cursor right"},
819 	{"ku",    "k",	"cursor up"},
820 	{NULL},
821 };
822 #endif
823 
824 /*
825  * Initialize function key mappings.
826  */
827 EXPORT void
init_fk_maps()828 init_fk_maps()
829 {
830 	char	*p;
831 	struct fk_maps *mp;
832 	extern char	**tty_entry __PR((void));
833 
834 	for (mp = fk_maps; mp->fk_tc; mp++) {
835 		if (mp->fk_map) {
836 			p = tgetstr(mp->fk_tc, tty_entry());
837 #ifdef	INIT_FP_MAPS_DEBUG
838 error("tc: '%s' map: '%s' %X\r\n", mp->fk_tc, p, *tty_entry());
839 #endif
840 			if (p == NULL) {
841 				if (mp->fk_tc[0] == 'k' &&
842 				    mp->fk_tc[1] == 'h') {
843 					p = "\33[7~";
844 				}
845 				if (mp->fk_tc[0] == '@' &&
846 				    mp->fk_tc[1] == '7') {
847 					p = "\33[8~";
848 				}
849 				if (mp->fk_tc[0] == 'k' &&
850 				    mp->fk_tc[1] == 'D') {
851 					p = "\33[3~";
852 				}
853 			}
854 			if (p)
855 				_add_map(UC p, mp->fk_map, mp->fk_comment);
856 		}
857 	}
858 }
859 
860 /*
861  * Make a string readable - it may contain comtrol characters.
862  */
863 LOCAL char *
pretty_string(s)864 pretty_string(s)
865 	register Uchar	*s;
866 {
867 	static	 Uchar	buf[16];
868 	static	 Uchar	*str = 0;
869 	register Uchar	*s1  = 0;
870 	register int	len;
871 
872 	if (str && str != buf)
873 		free(str);
874 
875 	len = 3 *(unsigned)strlen((char *)s) + 1;
876 	if (len > sizeof (buf))
877 		s1 = str = (Uchar *)malloc(len);
878 
879 	if (s1 == 0) {
880 		len = sizeof (buf);
881 		s1 = str = buf;
882 	}
883 	while (*s && --len > 0) {
884 		if (isprint(*s)) {
885 			*s1++ = *s++;
886 			continue;
887 		}
888 		if (*s & 0x80) {
889 			*s1++ = '~';
890 			len--;
891 		}
892 		if (*s != 127 && *s & 0x60) {
893 			*s1++ = *s++ & 0x7F;
894 		} else {
895 			*s1++ = '^';
896 			*s1++ = (*s++ & 0x7F) ^ 0100;
897 			len--;
898 		}
899 	}
900 	*s1 = '\0';
901 	return ((char *)str);
902 }
903 
904 #else	/* This is for BSH */
905 
906 /*
907  * Initialize cursor mappings for bsh. Tgetent has not been called before.
908  */
909 LOCAL void
init_cursor_maps()910 init_cursor_maps()
911 {
912 		char	stbuf[1024];
913 		char	*sbp;
914 		char	*ku;
915 		char	*kd;
916 		char	*kr;
917 		char	*kl;
918 		char	*kh;
919 		char	*ke;
920 		char	*kD;
921 		char	*kb;
922 		char	*tname;
923 		char	**esav;
924 	extern	char	**environ;
925 
926 	sbp = stbuf;
927 
928 	/*
929 	 * Let getenv() simulate the behaviour of getcurenv().
930 	 * We need TERMCAP=, TERMPATH=, HOME= and TERM=.
931 	 * This allows us to use the same tgetent() for bsh and ved
932 	 * and we may use -lxtermcap
933 	 */
934 	esav = environ;
935 	environ = evarray;
936 	if ((tname = getcurenv(termname)) != NULL &&
937 					tgetent(NULL, tname) == 1) {
938 		ev_insert(concat(termcapname, eql, tcgetbuf(), (char *)NULL));
939 		ku = tgetstr("ku", &sbp);		/* Cursor up */
940 		kd = tgetstr("kd", &sbp);		/* Cursor down */
941 		kr = tgetstr("kr", &sbp);		/* Cursor forward */
942 		kl = tgetstr("kl", &sbp);		/* Cursor left */
943 		kh = tgetstr("kh", &sbp);		/* Cursor -> Home */
944 		ke = tgetstr("@7", &sbp);		/* Cursor -> End */
945 		kD = tgetstr("kD", &sbp);		/* Delete Character */
946 		kb = tgetstr("kb", &sbp);		/* Key Backspace */
947 
948 		if (ku) {
949 			_add_map(UC ku, UC "", "Cursor up");
950 		} else {
951 			_add_map(UC "\33OA", UC "", "Cursor up");
952 			_add_map(UC "\33[A", UC "", "Cursor up");
953 		}
954 		if (kd) {
955 			_add_map(UC kd, UC "", "Cursor down");
956 		} else {
957 			_add_map(UC "\33OB", UC "", "Cursor down");
958 			_add_map(UC "\33[B", UC "", "Cursor down");
959 		}
960 		if (kr) {
961 			_add_map(UC kr, UC "", "Cursor forward");
962 		} else {
963 			_add_map(UC "\33OC", UC "", "Cursor forward");
964 			_add_map(UC "\33[C", UC "", "Cursor forward");
965 		}
966 		if (kl) {
967 			_add_map(UC kl, UC "", "Cursor left");
968 		} else {
969 			_add_map(UC "\33OD", UC "", "Cursor left");
970 			_add_map(UC "\33[D", UC "", "Cursor left");
971 		}
972 
973 		if (kh)
974 			_add_map(UC kh, UC "", "Cursor Home");
975 		else
976 			_add_map(UC "\33[7~", UC "", "Cursor Home");
977 		if (ke)
978 			_add_map(UC ke, UC "", "Cursor End");
979 		else
980 			_add_map(UC "\33[8~", UC "", "Cursor End");
981 		if (kD)
982 			_add_map(UC kD, UC "\177", "Delete Char");
983 		else
984 			_add_map(UC "\33[3~", UC "\177", "Delete Char");
985 
986 		if (kb)
987 			_add_map(UC kb, UC "\177", "Key Backspace -> Delete Char");
988 	} else {
989 		/*
990 		 * ANSI Cursor mapping in "edit mode".
991 		 */
992 		_add_map(UC "\33OA", UC "", "Cursor up");
993 		_add_map(UC "\33OB", UC "", "Cursor down");
994 		_add_map(UC "\33OC", UC "", "Cursor forward");
995 		_add_map(UC "\33OD", UC "", "Cursor left");
996 		/*
997 		 * ANSI Cursor mapping in "default mode".
998 		 */
999 		_add_map(UC "\33[A", UC "", "Cursor up");
1000 		_add_map(UC "\33[B", UC "", "Cursor down");
1001 		_add_map(UC "\33[C", UC "", "Cursor forward");
1002 		_add_map(UC "\33[D", UC "", "Cursor left");
1003 
1004 		/*
1005 		 * PC keyboard keys "Del", "Pos1", "End"
1006 		 */
1007 		_add_map(UC "\33[7~", UC "", "Cursor Home");
1008 		_add_map(UC "\33[8~", UC "", "Cursor End");
1009 		_add_map(UC "\33[3~", UC "\177", "Delete Char");
1010 	}
1011 	_add_map(UC "\33n", UC "\33",
1012 				"Search down after clearing previous search");
1013 	_add_map(UC "\33p", UC "\33",
1014 				"Search up after clearing previous search");
1015 	environ = esav;
1016 }
1017 #endif
1018 
1019 LOCAL void
init_fallback_maps()1020 init_fallback_maps()
1021 {
1022 #ifdef	BSH
1023 	fallback_map("^[[3~", "^?", "Delete Char");
1024 	fallback_map("^[[7~", "^A", "Cursor Home");
1025 	fallback_map("^[[8~", "^E", "Cursor End");
1026 #else
1027 	fallback_map("^[[3~", "^?", "Delete Char");
1028 	fallback_map("^[[5~", "^[p", "Page up");
1029 	fallback_map("^[[6~", "^[n", "Page down");
1030 	fallback_map("^[[7~", "^A", "Go to sol");
1031 	fallback_map("^[[8~", "^E", "Go to eol");
1032 	fallback_map("^[[11~", "^C", "Quit Editor  (F1)");
1033 	fallback_map("^[[12~", "^B", "Top          (F2)");
1034 	fallback_map("^[[13~", "^D", "Delete Char  (F3)");
1035 	fallback_map("^[[14~", "^K", "Delete Line  (F4)");
1036 	fallback_map("^[[15~", "^O", "Open Line    (F5)");
1037 	fallback_map("^[[17~", "^T", "Cut Line     (F6)");
1038 	fallback_map("^[[18~", "^V", "Paste        (F7)");
1039 	fallback_map("^[[19~", "^\\", "Change Buffer(F8)");
1040 	fallback_map("^[[20~", "^R", "Search down  (F9)");
1041 	fallback_map("^[[21~", "^Z", "Research     (F10)");
1042 	fallback_map("^[[23~", "^G", "Get from     (F11)");
1043 	fallback_map("^[[24~", "^W", "Write to     (F12)");
1044 #endif
1045 }
1046 
1047 #endif	/* INTERACTIVE */
1048