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