Removed KLUDGELINEMODE option.
[unix-history] / usr.bin / elvis / tio.c
CommitLineData
15637ed4
RG
1/* tio.c */
2
3/* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
8 */
9
10
11/* This file contains terminal I/O functions */
12
13#include "config.h"
14#include "vi.h"
15#include "ctype.h"
16
08746e8b 17static int showmsg P_((void));
15637ed4
RG
18
19/* This function reads in a line from the terminal. */
20int vgets(prompt, buf, bsize)
08746e8b 21 int prompt; /* the prompt character, or '\0' for none */
15637ed4
RG
22 char *buf; /* buffer into which the string is read */
23 int bsize; /* size of the buffer */
24{
25 int len; /* how much we've read so far */
26 int ch; /* a character from the user */
27 int quoted; /* is the next char quoted? */
28 int tab; /* column position of cursor */
29 char widths[132]; /* widths of characters */
30 int word; /* index of first letter of word */
31#ifndef NO_DIGRAPH
32 int erased; /* 0, or first char of a digraph */
33#endif
34
35 /* show the prompt */
36 move(LINES - 1, 0);
37 tab = 0;
38 if (prompt)
39 {
40 addch(prompt);
41 tab = 1;
42 }
43 clrtoeol();
44 refresh();
45
46 /* read in the line */
47#ifndef NO_DIGRAPH
48 erased =
49#endif
50 quoted = len = 0;
51 for (;;)
52 {
53#ifndef NO_ABBR
54 if (quoted || mode == MODE_EX)
55 {
56 ch = getkey(0);
57 }
58 else
59 {
60 /* maybe expand an abbreviation while getting key */
08746e8b 61 for (word = len; --word >= 0 && !isspace(buf[word]); )
15637ed4
RG
62 {
63 }
64 word++;
65 ch = getabkey(WHEN_EX, &buf[word], len - word);
66 }
67#else
68 ch = getkey(0);
69#endif
70#ifndef NO_EXTENSIONS
71 if (ch == ctrl('O'))
72 {
73 ch = getkey(quoted ? 0 : WHEN_EX);
74 }
75#endif
76
77 /* some special conversions */
08746e8b 78#if 0
15637ed4
RG
79 if (ch == ctrl('D') && len == 0)
80 ch = ctrl('[');
08746e8b 81#endif
15637ed4
RG
82#ifndef NO_DIGRAPH
83 if (*o_digraph && erased != 0 && ch != '\b')
84 {
85 ch = digraph(erased, ch);
86 erased = 0;
87 }
88#endif
89
90 /* inhibit detection of special chars (except ^J) after a ^V */
91 if (quoted && ch != '\n')
92 {
93 ch |= 256;
94 }
95
96 /* process the character */
97 switch(ch)
98 {
99 case ctrl('V'):
100 qaddch('^');
101 qaddch('\b');
102 quoted = TRUE;
103 break;
104
08746e8b 105 case ctrl('D'):
15637ed4
RG
106 return -1;
107
08746e8b 108 case ctrl('['):
15637ed4
RG
109 case '\n':
110#if OSK
111 case '\l':
112#else
113 case '\r':
114#endif
115 clrtoeol();
116 goto BreakBreak;
117
08746e8b
AM
118#ifndef CRUNCH
119 case ctrl('U'):
120 while (len > 0)
121 {
122 len--;
123 while (widths[len]-- > 0)
124 {
125 qaddch('\b');
126 qaddch(' ');
127 qaddch('\b');
128 }
129 }
130 break;
131#endif
132
15637ed4
RG
133 case '\b':
134 if (len > 0)
135 {
136 len--;
137#ifndef NO_DIGRAPH
138 erased = buf[len];
139#endif
140 for (ch = widths[len]; ch > 0; ch--)
141 addch('\b');
142 if (mode == MODE_EX)
143 {
144 clrtoeol();
145 }
146 tab -= widths[len];
147 }
148 else
149 {
150 return -1;
151 }
152 break;
153
154 default:
155 /* strip off quotation bit */
156 if (ch & 256)
157 {
158 ch &= ~256;
159 qaddch(' ');
160 qaddch('\b');
161 }
162
163 /* add & echo the char */
164 if (len < bsize - 1)
165 {
166 if (ch == '\t' && !quoted)
167 {
168 widths[len] = *o_tabstop - (tab % *o_tabstop);
169 addstr(" " + 8 - widths[len]);
170 tab += widths[len];
171 }
172 else if (ch > 0 && ch < ' ') /* > 0 by GB */
173 {
174 addch('^');
175 addch(ch + '@');
176 widths[len] = 2;
177 tab += 2;
178 }
179 else if (ch == '\177')
180 {
181 addch('^');
182 addch('?');
183 widths[len] = 2;
184 tab += 2;
185 }
186 else
187 {
188 addch(ch);
189 widths[len] = 1;
190 tab++;
191 }
192 buf[len++] = ch;
193 }
194 else
195 {
196 beep();
197 }
198 quoted = FALSE;
199 }
200 }
201BreakBreak:
202 refresh();
203 buf[len] = '\0';
204 return len;
205}
206
207
208static int manymsgs; /* This variable keeps msgs from overwriting each other */
209static char pmsg[80]; /* previous message (waiting to be displayed) */
210
211
212static int showmsg()
213{
214 /* if there is no message to show, then don't */
215 if (!manymsgs)
216 return FALSE;
217
218 /* display the message */
219 move(LINES - 1, 0);
220 if (*pmsg)
221 {
222 standout();
223 qaddch(' ');
224 qaddstr(pmsg);
225 qaddch(' ');
226 standend();
227 }
228 clrtoeol();
229
230 manymsgs = FALSE;
231 return TRUE;
232}
233
234
235void endmsgs()
236{
237 if (manymsgs)
238 {
239 showmsg();
240 addch('\n');
241 }
242}
243
244/* Write a message in an appropriate way. This should really be a varargs
245 * function, but there is no such thing as vwprintw. Hack!!!
246 *
247 * In MODE_EX or MODE_COLON, the message is written immediately, with a
248 * newline at the end.
249 *
250 * In MODE_VI, the message is stored in a character buffer. It is not
251 * displayed until getkey() is called. msg() will call getkey() itself,
252 * if necessary, to prevent messages from being lost.
253 *
254 * msg("") - clears the message line
255 * msg("%s %d", ...) - does a printf onto the message line
256 */
08746e8b
AM
257#ifdef __STDC__
258void msg (char *fmt, ...)
259{
260 va_list ap;
261 va_start (ap, fmt);
262#else
15637ed4
RG
263void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
264 char *fmt;
265 long arg1, arg2, arg3, arg4, arg5, arg6, arg7;
266{
08746e8b 267#endif
15637ed4
RG
268 if (mode != MODE_VI)
269 {
08746e8b
AM
270#ifdef __STDC__
271 vsprintf (pmsg, fmt, ap);
272#else
15637ed4 273 sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
08746e8b 274#endif
15637ed4
RG
275 qaddstr(pmsg);
276 addch('\n');
277 exrefresh();
278 }
279 else
280 {
281 /* wait for keypress between consecutive msgs */
282 if (manymsgs)
283 {
284 getkey(WHEN_MSG);
285 }
286
287 /* real message */
08746e8b
AM
288#ifdef __STDC__
289 vsprintf (pmsg, fmt, ap);
290#else
15637ed4 291 sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
08746e8b 292#endif
15637ed4
RG
293 if (*fmt)
294 {
295 manymsgs = TRUE;
296 }
297 }
08746e8b
AM
298#ifdef __STDC__
299 va_end (ap);
300#endif
15637ed4
RG
301}
302
303
304/* This function calls refresh() if the option exrefresh is set */
305void exrefresh()
306{
307 char *scan;
308
309 /* If this ex command wrote ANYTHING set exwrote so vi's : command
310 * can tell that it must wait for a user keystroke before redrawing.
311 */
312 for (scan=kbuf; scan<stdscr; scan++)
313 if (*scan == '\n')
314 exwrote = TRUE;
315
316 /* now we do the refresh thing */
317 if (*o_exrefresh)
318 {
319 refresh();
320 }
321 else
322 {
323 wqrefresh();
324 }
325 if (mode != MODE_VI)
326 {
327 manymsgs = FALSE;
328 }
329}
330
331
332/* This structure is used to store maps and abbreviations. The distinction
333 * between them is that maps are stored in the list referenced by the "maps"
334 * pointer, while abbreviations are referenced by the "abbrs" pointer.
335 */
336typedef struct _map
337{
338 struct _map *next; /* another abbreviation */
339 short len; /* length of the "rawin" characters */
340 short flags; /* various flags */
341 char *label; /* label of the map/abbr, or NULL */
342 char *rawin; /* the "rawin" characters */
343 char *cooked;/* the "cooked" characters */
344} MAP;
345
346static char keybuf[KEYBUFSIZE];
347static int cend; /* end of input characters */
348static int user; /* from user through end are chars typed by user */
349static int next; /* index of the next character to be returned */
350static MAP *match; /* the matching map, found by countmatch() */
351static MAP *maps; /* the map table */
352#ifndef NO_ABBR
353static MAP *abbrs; /* the abbreviation table */
354#endif
355
356
357
358/* ring the terminal's bell */
359void beep()
360{
361 /* do a visible/audible bell */
362 if (*o_flash)
363 {
364 do_VB();
365 refresh();
366 }
367 else if (*o_errorbells)
368 {
08746e8b 369 tputs("\007", 1, faddch);
15637ed4
RG
370 }
371
372 /* discard any buffered input, and abort macros */
373 next = user = cend;
374}
375
376
377
378/* This function replaces a "rawin" character sequence with the "cooked" version,
379 * by modifying the internal type-ahead buffer.
380 */
381void execmap(rawlen, cookedstr, visual)
382 int rawlen; /* length of rawin text -- string to delete */
383 char *cookedstr; /* the cooked text -- string to insert */
384 int visual; /* boolean -- chars to be executed in visual mode? */
385{
386 int cookedlen;
387 char *src, *dst;
388 int i;
389
390 /* find the length of the cooked string */
391 cookedlen = strlen(cookedstr);
392#ifndef NO_EXTENSIONS
393 if (visual)
394 {
395 cookedlen *= 2;
396 }
397#endif
398
399 /* if too big to fit in type-ahead buffer, then don't do it */
400 if (cookedlen + (cend - next) - rawlen > KEYBUFSIZE)
401 {
402 return;
403 }
404
405 /* shift to make room for cookedstr at the front of keybuf */
406 src = &keybuf[next + rawlen];
407 dst = &keybuf[cookedlen];
408 i = cend - (next + rawlen);
409 if (src >= dst)
410 {
411 while (i-- > 0)
412 {
413 *dst++ = *src++;
414 }
415 }
416 else
417 {
418 src += i;
419 dst += i;
420 while (i-- > 0)
421 {
422 *--dst = *--src;
423 }
424 }
425
426 /* insert cookedstr, and adjust offsets */
427 cend += cookedlen - rawlen - next;
428 user += cookedlen - rawlen - next;
429 next = 0;
430 for (dst = keybuf, src = cookedstr; *src; )
431 {
432#ifndef NO_EXTENSIONS
433 if (visual)
434 {
435 *dst++ = ctrl('O');
436 cookedlen--;
437 }
438#endif
439 *dst++ = *src++;
440 }
441
442#ifdef DEBUG2
443 {
444#include <stdio.h>
445 FILE *debout;
446 int i;
447
448 debout = fopen("debug.out", "a");
449 fprintf(debout, "After execmap(%d, \"%s\", %d)...\n", rawlen, cookedstr, visual);
450 for (i = 0; i < cend; i++)
451 {
452 if (i == next) fprintf(debout, "(next)");
453 if (i == user) fprintf(debout, "(user)");
454 if (UCHAR(keybuf[i]) < ' ')
455 fprintf(debout, "^%c", keybuf[i] ^ '@');
456 else
457 fprintf(debout, "%c", keybuf[i]);
458 }
459 fprintf(debout, "(end)\n");
460 fclose(debout);
461 }
462#endif
463}
464
08746e8b
AM
465#ifndef NO_CURSORSHAPE
466/* made global so that suspend_curses() can reset it. -nox */
467int oldcurs;
468#endif
15637ed4
RG
469/* This function calls ttyread(). If necessary, it will also redraw the screen,
470 * change the cursor shape, display the mode, and update the ruler. If the
471 * number of characters read is 0, and we didn't time-out, then it exits because
472 * we've apparently reached the end of an EX script.
473 */
474static int fillkeybuf(when, timeout)
475 int when; /* mixture of WHEN_XXX flags */
476 int timeout;/* timeout in 1/10 second increments, or 0 */
477{
478 int nkeys;
479#ifndef NO_SHOWMODE
480 static int oldwhen; /* "when" from last time */
481 static int oldleft;
482 static long oldtop;
483 static long oldnlines;
484 char *str;
485#endif
15637ed4
RG
486
487#ifdef DEBUG
488 watch();
489#endif
490
491
492#ifndef NO_CURSORSHAPE
493 /* make sure the cursor is the right shape */
494 if (has_CQ)
495 {
496 if (when != oldcurs)
497 {
498 switch (when)
499 {
500 case WHEN_EX: do_CX(); break;
501 case WHEN_VICMD: do_CV(); break;
502 case WHEN_VIINP: do_CI(); break;
503 case WHEN_VIREP: do_CR(); break;
504 }
505 oldcurs = when;
506 }
507 }
508#endif
509
510#ifndef NO_SHOWMODE
511 /* if "showmode" then say which mode we're in */
512 if (*o_smd && (when & WHENMASK))
513 {
514 /* redraw the screen before we check to see whether the
515 * "showmode" message needs to be redrawn.
516 */
517 redraw(cursor, !(when & WHEN_VICMD));
518
519 /* now the "topline" test should be valid */
08746e8b
AM
520 if (when != oldwhen
521 || topline != oldtop
522 || leftcol != oldleft
523 || nlines != oldnlines)
15637ed4
RG
524 {
525 oldwhen = when;
526 oldtop = topline;
527 oldleft = leftcol;
528 oldnlines = nlines;
529
530 if (when & WHEN_VICMD) str = "Command";
531 else if (when & WHEN_VIINP) str = " Input ";
532 else if (when & WHEN_VIREP) str = "Replace";
08746e8b
AM
533 else if (when & WHEN_REP1) str = "Replc 1";
534 else if (when & WHEN_CUT) str = "Buffer ";
535 else if (when & WHEN_MARK) str = " Mark ";
15637ed4
RG
536 else if (when & WHEN_CHAR) str = "Dest Ch";
537 else str = (char *)0;
538
539 if (str)
540 {
541 move(LINES - 1, COLS - 10);
542 standout();
543 qaddstr(str);
544 standend();
545 }
546 }
547 }
548#endif
549
550#ifndef NO_EXTENSIONS
551 /* maybe display the ruler */
552 if (*o_ruler && (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)))
553 {
554 char buf[20];
555
556 redraw(cursor, !(when & WHEN_VICMD));
557 pfetch(markline(cursor));
558 sprintf(buf, "%7ld,%-4d", markline(cursor), 1 + idx2col(cursor, ptext, when & (WHEN_VIINP|WHEN_VIREP)));
559 move(LINES - 1, COLS - 22);
560 addstr(buf);
561 }
562#endif
563
564 /* redraw, so the cursor is in the right place */
565 if (when & WHENMASK)
566 {
567 redraw(cursor, !(when & (WHENMASK & ~(WHEN_VIREP|WHEN_VIINP))));
568 }
569
570 /* Okay, now we can finally read the rawin keystrokes */
571 refresh();
572 nkeys = ttyread(keybuf + cend, sizeof keybuf - cend, timeout);
573
574 /* if nkeys == 0 then we've reached EOF of an ex script. */
575 if (nkeys == 0 && timeout == 0)
576 {
577 tmpabort(TRUE);
578 move(LINES - 1, 0);
579 clrtoeol();
580 refresh();
581 endwin();
08746e8b 582 exit(exitcode);
15637ed4
RG
583 }
584
585 cend += nkeys;
586 user += nkeys;
587 return nkeys;
588}
589
590
591/* This function counts the number of maps that could match the characters
592 * between &keybuf[next] and &keybuf[cend], including incomplete matches.
593 * The longest comlete match is remembered via the "match" variable.
594 */
595static int countmatch(when)
596 int when; /* mixture of WHEN_XXX flags */
597{
598 MAP *map;
599 int count;
600
601 /* clear the "match" variable */
602 match = (MAP *)0;
603
604 /* check every map */
605 for (count = 0, map = maps; map; map = map->next)
606 {
607 /* can't match if wrong mode */
608 if ((map->flags & when) == 0)
609 {
610 continue;
611 }
612
613 /* would this be a complete match? */
614 if (map->len <= cend - next)
615 {
616 /* Yes, it would be. Now does it really match? */
617 if (!strncmp(map->rawin, &keybuf[next], map->len))
618 {
619 count++;
620
621 /* if this is the longest complete match,
622 * then remember it.
623 */
624 if (!match || match->len < map->len)
625 {
626 match = map;
627 }
628 }
629 }
630 else
631 {
632 /* No, it wouldn't. But check for partial match */
633 if (!strncmp(map->rawin, &keybuf[next], cend - next))
634 {
08746e8b
AM
635 /* increment by 2 instead of 1 so that, in the
636 * event that we have a partial match with a
637 * single map, we don't mistakenly assume we
638 * have resolved the map yet.
639 */
640 count += 2;
15637ed4
RG
641 }
642 }
643 }
644 return count;
645}
646
647
648#ifndef NO_ABBR
649/* This function checks to see whether a word is an abbreviation. If it is,
650 * then an appropriate number of backspoace characters is inserted into the
651 * type-ahead buffer, followed by the expanded form of the abbreviation.
652 */
653static void expandabbr(word, wlen)
654 char *word;
655 int wlen;
656{
657 MAP *abbr;
658
659 /* if the next character wouldn't end the word, then don't expand */
08746e8b 660 if (isalnum(keybuf[next]) || keybuf[next] == ctrl('V') || keybuf[next] == '\b')
15637ed4
RG
661 {
662 return;
663 }
664
665 /* find the abbreviation, if any */
666 for (abbr = abbrs;
667 abbr && (abbr->len != wlen || strncmp(abbr->rawin, word, wlen));
668 abbr = abbr->next)
669 {
670 }
671
672 /* If an abbreviation was found, then expand it by inserting the long
673 * version into the type-ahead buffer, and then inserting (in front of
674 * the long version) enough backspaces to erase to the short version.
675 */
676 if (abbr)
677 {
678 execmap(0, abbr->cooked, FALSE);
679 while (wlen > 15)
680 {
681 execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", FALSE);
682 wlen -= 15;
683 }
684 if (wlen > 0)
685 {
686 execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + 15 - wlen, FALSE);
687 }
688 }
689}
690#endif
691
692
693/* This function calls getabkey() without attempting to expand abbreviations */
694int getkey(when)
695 int when; /* mixture of WHEN_XXX flags */
696{
697 return getabkey(when, "", 0);
698}
699
700
701/* This is it. This function returns keystrokes one-at-a-time, after mapping
702 * and abbreviations have been taken into account.
703 */
704int getabkey(when, word, wlen)
705 int when; /* mixture of WHEN_XXX flags */
706 char *word; /* a word that may need to be expanded as an abbr */
707 int wlen; /* length of "word" -- since "word" might not have \0 */
708{
709 int matches;
710
711 /* if this key is needed for delay between multiple error messages,
712 * then reset the manymsgs flag and abort any mapped key sequence.
713 */
714 if (showmsg())
715 {
716 if (when == WHEN_MSG)
717 {
718#ifndef CRUNCH
719 if (!*o_more)
720 {
721 refresh();
722 return ' ';
723 }
724#endif
725 qaddstr("[More...]");
726 refresh();
727 execmap(user, "", FALSE);
728 }
729 }
730
731#ifdef DEBUG
732 /* periodically check for screwed up internal tables */
733 watch();
734#endif
735
736 /* if buffer empty, read some characters without timeout */
737 if (next >= cend)
738 {
739 next = user = cend = 0;
740 fillkeybuf(when, 0);
741 }
742
743 /* try to map the key, unless already mapped and not ":set noremap" */
08746e8b 744 if (next <= user || *o_remap)
15637ed4
RG
745 {
746 do
747 {
748 do
749 {
750 matches = countmatch(when);
751 } while (matches > 1 && fillkeybuf(when, *o_keytime) > 0);
752 if (matches == 1)
753 {
754 execmap(match->len, match->cooked,
755 (match->flags & WHEN_INMV) != 0
756 && (when & (WHEN_VIINP|WHEN_VIREP)) != 0);
757 }
758 } while (*o_remap && matches == 1);
759 }
760
08746e8b
AM
761 /* ERASEKEY should always be mapped to '\b'. */
762 if (keybuf[next] == ERASEKEY)
763 {
764 keybuf[next] = '\b';
765 }
766
15637ed4
RG
767#ifndef NO_ABBR
768 /* try to expand an abbreviation, except in visual command mode */
769 if (wlen > 0 && (mode & (WHEN_EX|WHEN_VIINP|WHEN_VIREP)) != 0)
770 {
771 expandabbr(word, wlen);
772 }
773#endif
774
15637ed4
RG
775 /* return the next key */
776 return keybuf[next++];
777}
778
779/* This function maps or unmaps a key */
780void mapkey(rawin, cooked, when, name)
781 char *rawin; /* the input key sequence, before mapping */
782 char *cooked;/* after mapping -- or NULL to remove map */
08746e8b 783 int when; /* bitmap of when mapping should happen */
15637ed4
RG
784 char *name; /* name of the key, NULL for no name, "abbr" for abbr */
785{
786 MAP **head; /* head of list of maps or abbreviations */
787 MAP *scan; /* used for scanning through the list */
788 MAP *prev; /* used during deletions */
789
790 /* Is this a map or an abbreviation? Choose the right list. */
791#ifndef NO_ABBR
792 head = ((!name || strcmp(name, "abbr")) ? &maps : &abbrs);
793#else
794 head = &maps;
795#endif
796
797 /* try to find the map in the list */
798 for (scan = *head, prev = (MAP *)0;
08746e8b 799 scan && (strcmp(rawin, scan->rawin) && strcmp(rawin, scan->cooked) ||
15637ed4
RG
800 !(scan->flags & when & (WHEN_EX|WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)));
801 prev = scan, scan = scan->next)
802 {
803 }
804
805 /* trying to map? (not unmap) */
806 if (cooked && *cooked)
807 {
808 /* if map starts with "visual ", then mark it as a visual map */
809 if (head == &maps && !strncmp(cooked, "visual ", 7))
810 {
811 cooked += 7;
812 when |= WHEN_INMV;
813 }
814
815 /* "visual" maps always work in input mode */
816 if (when & WHEN_INMV)
817 {
818 when |= WHEN_VIINP|WHEN_VIREP|WHEN_POPUP;
819 }
820
821 /* if not already in the list, then allocate a new structure */
822 if (!scan)
823 {
824 scan = (MAP *)malloc(sizeof(MAP));
825 scan->len = strlen(rawin);
08746e8b 826 scan->rawin = malloc((unsigned)(scan->len + 1));
15637ed4
RG
827 strcpy(scan->rawin, rawin);
828 scan->flags = when;
829 scan->label = name;
830 if (*head)
831 {
832 prev->next = scan;
833 }
834 else
835 {
836 *head = scan;
837 }
838 scan->next = (MAP *)0;
839 }
840 else /* recycle old structure */
841 {
08746e8b 842 _free_(scan->cooked);
15637ed4 843 }
08746e8b 844 scan->cooked = malloc((unsigned)(strlen(cooked) + 1));
15637ed4
RG
845 strcpy(scan->cooked, cooked);
846 }
847 else /* unmapping */
848 {
849 /* if nothing to unmap, then exit silently */
850 if (!scan)
851 {
852 return;
853 }
854
855 /* unlink the structure from the list */
856 if (prev)
857 {
858 prev->next = scan->next;
859 }
860 else
861 {
862 *head = scan->next;
863 }
864
865 /* free it, and the strings that it refers to */
08746e8b
AM
866 _free_(scan->rawin);
867 _free_(scan->cooked);
868 _free_(scan);
15637ed4
RG
869 }
870}
871
872
873/* This function returns a printable version of a string. It uses tmpblk.c */
874char *printable(str)
875 char *str; /* the string to convert */
876{
877 char *build; /* used for building the string */
878
879 for (build = tmpblk.c; *str; str++)
880 {
881#if AMIGA
882 if (*str == '\233')
883 {
884 *build++ = '<';
885 *build++ = 'C';
886 *build++ = 'S';
887 *build++ = 'I';
888 *build++ = '>';
889 } else
890#endif
891 if (UCHAR(*str) < ' ' || *str == '\177')
892 {
893 *build++ = '^';
894 *build++ = *str ^ '@';
895 }
896 else
897 {
898 *build++ = *str;
899 }
900 }
901 *build = '\0';
902 return tmpblk.c;
903}
904
905/* This function displays the contents of either the map table or the
906 * abbreviation table. User commands call this function as follows:
907 * :map dumpkey(WHEN_VICMD, FALSE);
908 * :map! dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
909 * :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
910 * :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
911 */
912void dumpkey(when, abbr)
913 int when; /* WHEN_XXXX of mappings to be dumped */
914 int abbr; /* boolean: dump abbreviations instead of maps? */
915{
916 MAP *scan;
917 char *str;
918 int len;
919
920#ifndef NO_ABBR
921 for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
922#else
923 for (scan = maps; scan; scan = scan->next)
924#endif
925 {
926 /* skip entries that don't match "when" */
927 if ((scan->flags & when) == 0)
928 {
929 continue;
930 }
931
932 /* dump the key label, if any */
933 if (!abbr)
934 {
935 len = 8;
936 if (scan->label)
937 {
938 qaddstr(scan->label);
939 len -= strlen(scan->label);
940 }
941 do
942 {
943 qaddch(' ');
944 } while (len-- > 0);
945 }
946
947 /* dump the rawin version */
948 str = printable(scan->rawin);
949 qaddstr(str);
950 len = strlen(str);
951 do
952 {
953 qaddch(' ');
954 } while (len++ < 8);
955
956 /* dump the mapped version */
957#ifndef NO_EXTENSIONS
958 if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
959 {
960 qaddstr("visual ");
961 }
962#endif
963 str = printable(scan->cooked);
964 qaddstr(str);
965 addch('\n');
966 exrefresh();
967 }
968}
969
970#ifndef NO_MKEXRC
971
08746e8b 972static void safequote(str)
15637ed4
RG
973 char *str;
974{
975 char *build;
976
977 build = tmpblk.c + strlen(tmpblk.c);
978 while (*str)
979 {
980 if (*str <= ' ' && *str >= 1 || *str == '|')
981 {
982 *build++ = ctrl('V');
983 }
984 *build++ = *str++;
985 }
986 *build = '\0';
987}
988
989/* This function saves the contents of either the map table or the
990 * abbreviation table into a file. Both the "bang" and "no bang" versions
991 * are saved.
992 * :map dumpkey(WHEN_VICMD, FALSE);
993 * :map! dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
994 * :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
995 * :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
996 */
08746e8b 997void
15637ed4
RG
998savemaps(fd, abbr)
999 int fd; /* file descriptor of an open file to write to */
1000 int abbr; /* boolean: do abbr table? (else do map table) */
1001{
1002 MAP *scan;
15637ed4
RG
1003 int bang;
1004 int when;
15637ed4
RG
1005
1006# ifndef NO_ABBR
1007 for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
1008# else
1009 for (scan = maps; scan; scan = scan->next)
1010# endif
1011 {
1012 /* skip maps that have labels, except for function keys */
1013 if (scan->label && *scan->label != '#')
1014 {
1015 continue;
1016 }
1017
1018 for (bang = 0; bang < 2; bang++)
1019 {
1020 /* decide which "when" flags we want */
1021# ifndef NO_ABBR
1022 if (abbr)
1023 when = (bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP);
1024 else
1025# endif
1026 when = (bang ? WHEN_VIREP|WHEN_VIINP : WHEN_VICMD);
1027
1028 /* skip entries that don't match "when" */
1029 if ((scan->flags & when) == 0)
1030 {
1031 continue;
1032 }
1033
1034 /* write a "map" or "abbr" command name */
1035# ifndef NO_ABBR
1036 if (abbr)
1037 strcpy(tmpblk.c, "abbr");
1038 else
1039# endif
1040 strcpy(tmpblk.c, "map");
1041
1042 /* maybe write a bang. Definitely write a space */
1043 if (bang)
1044 strcat(tmpblk.c, "! ");
1045 else
1046 strcat(tmpblk.c, " ");
1047
1048 /* write the rawin version */
1049# ifndef NO_FKEY
1050 if (scan->label)
1051 strcat(tmpblk.c, scan->label);
1052 else
1053# endif
1054 safequote(scan->rawin);
1055 strcat(tmpblk.c, " ");
1056
1057 /* dump the mapped version */
1058# ifndef NO_EXTENSIONS
1059 if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
1060 {
1061 strcat(tmpblk.c, "visual ");
1062 }
1063# endif
1064 safequote(scan->cooked);
1065 strcat(tmpblk.c, "\n");
1066 twrite(fd, tmpblk.c, strlen(tmpblk.c));
1067 }
1068 }
1069}
1070#endif