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