BSD 3 development
[unix-history] / usr / src / cmd / ex / ex_vget.c
CommitLineData
27704fe4
BJ
1/* Copyright (c) 1979 Regents of the University of California */
2#include "ex.h"
3#include "ex_tty.h"
4#include "ex_vis.h"
5
6/*
7 * Input routines for open/visual.
8 * We handle upper case only terminals in visual and reading from the
9 * echo area here as well as notification on large changes
10 * which appears in the echo area.
11 */
12
13/*
14 * Return the key.
15 */
16ungetkey(c)
17 char c;
18{
19
20 if (Peekkey != ATTN)
21 Peekkey = c;
22}
23
24/*
25 * Return a keystroke, but never a ^@.
26 */
27getkey()
28{
29 register char c;
30
31 do {
32 c = getbr();
33 if (c==0)
34 beep();
35 } while (c == 0);
36 return (c);
37}
38
39/*
40 * Tell whether next keystroke would be a ^@.
41 */
42peekbr()
43{
44
45 Peekkey = getbr();
46 return (Peekkey == 0);
47}
48
49short precbksl;
50
51/*
52 * Get a keystroke, including a ^@.
53 * If an key was returned with ungetkey, that
54 * comes back first. Next comes unread input (e.g.
55 * from repeating commands with .), and finally new
56 * keystrokes.
57 *
58 * The hard work here is in mapping of \ escaped
59 * characters on upper case only terminals.
60 */
61getbr()
62{
63 char ch;
64 register int c, d;
65 register char *colp;
66
67getATTN:
68 if (Peekkey) {
69 c = Peekkey;
70 Peekkey = 0;
71 return (c);
72 }
73 if (vglobp) {
74 if (*vglobp)
75 return (lastvgk = *vglobp++);
76 lastvgk = 0;
77 return (ESCAPE);
78 }
79 if (vmacp) {
80 if (*vmacp)
81 return(*vmacp++);
82 /* End of a macro or set of nested macros */
83 vmacp = 0;
84 if (inopen == -1) /* don't screw up undo for esc esc */
85 vundkind = VMANY;
86 inopen = 1; /* restore old setting now that macro done */
87 }
88#ifdef TRACE
89 if (trace)
90 fflush(trace);
91#endif
92 flusho();
93again:
94 if (read(0, &ch, 1) != 1) {
95 if (errno == EINTR)
96 goto getATTN;
97 error("Input read error");
98 }
99 c = ch & TRIM;
100
101#ifdef UCVISUAL
102 /*
103 * The algorithm here is that of the UNIX kernel.
104 * See the description in the programmers manual.
105 */
106 if (UPPERCASE) {
107 if (isupper(c))
108 c = tolower(c);
109 if (c == '\\') {
110 if (precbksl < 2)
111 precbksl++;
112 if (precbksl == 1)
113 goto again;
114 } else if (precbksl) {
115 d = 0;
116 if (islower(c))
117 d = toupper(c);
118 else {
119 colp = "({)}!|^~'~";
120 while (d = *colp++)
121 if (d == c) {
122 d = *colp++;
123 break;
124 } else
125 colp++;
126 }
127 if (precbksl == 2) {
128 if (!d) {
129 Peekkey = c;
130 precbksl = 0;
131 c = '\\';
132 }
133 } else if (d)
134 c = d;
135 else {
136 Peekkey = c;
137 precbksl = 0;
138 c = '\\';
139 }
140 }
141 if (c != '\\')
142 precbksl = 0;
143 }
144#endif
145#ifdef TRACE
146 if (trace) {
147 if (!techoin) {
148 tfixnl();
149 techoin = 1;
150 fprintf(trace, "*** Input: ");
151 }
152 tracec(c);
153 }
154#endif
155 lastvgk = 0;
156 return (c);
157}
158
159/*
160 * Get a key, but if a delete, quit or attention
161 * is typed return 0 so we will abort a partial command.
162 */
163getesc()
164{
165 register int c;
166
167 c = getkey();
168 switch (c) {
169
170 case ATTN:
171 case QUIT:
172 ungetkey(c);
173 return (0);
174
175 case ESCAPE:
176 return (0);
177 }
178 return (c);
179}
180
181/*
182 * Peek at the next keystroke.
183 */
184peekkey()
185{
186
187 Peekkey = getkey();
188 return (Peekkey);
189}
190
191/*
192 * Read a line from the echo area, with single character prompt c.
193 * A return value of 1 means the user blewit or blewit away.
194 */
195readecho(c)
196 char c;
197{
198 register char *sc = cursor;
199 register int (*OP)();
200 bool waste;
201 register int OPeek;
202
203 if (WBOT == WECHO)
204 vclean();
205 else
206 vclrech(0);
207 splitw++;
208 vgoto(WECHO, 0);
209 putchar(c);
210 vclreol();
211 vgoto(WECHO, 1);
212 cursor = linebuf; linebuf[0] = 0; genbuf[0] = c;
213 if (peekbr()) {
214 if (!INS[0] || (INS[0] & (QUOTE|TRIM)) == OVERBUF)
215 goto blewit;
216 vglobp = INS;
217 }
218 OP = Pline; Pline = normline;
219 ignore(vgetline(0, genbuf + 1, &waste));
220 vscrap();
221 Pline = OP;
222 if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL(h)) {
223 cursor = sc;
224 vclreol();
225 return (0);
226 }
227blewit:
228 OPeek = Peekkey==CTRL(h) ? 0 : Peekkey; Peekkey = 0;
229 splitw = 0;
230 vclean();
231 vshow(dot, NOLINE);
232 vnline(sc);
233 Peekkey = OPeek;
234 return (1);
235}
236
237/*
238 * A complete command has been defined for
239 * the purposes of repeat, so copy it from
240 * the working to the previous command buffer.
241 */
242setLAST()
243{
244
245 if (vglobp)
246 return;
247 lastreg = vreg;
248 lasthad = Xhadcnt;
249 lastcnt = Xcnt;
250 *lastcp = 0;
251 CP(lastcmd, workcmd);
252}
253
254/*
255 * Gather up some more text from an insert.
256 * If the insertion buffer oveflows, then destroy
257 * the repeatability of the insert.
258 */
259addtext(cp)
260 char *cp;
261{
262
263 if (vglobp)
264 return;
265 addto(INS, cp);
266 if ((INS[0] & (QUOTE|TRIM)) == OVERBUF)
267 lastcmd[0] = 0;
268}
269
270setDEL()
271{
272
273 setBUF(DEL);
274}
275
276/*
277 * Put text from cursor upto wcursor in BUF.
278 */
279setBUF(BUF)
280 register char *BUF;
281{
282 register int c;
283 register char *wp = wcursor;
284
285 c = *wp;
286 *wp = 0;
287 BUF[0] = 0;
288 addto(BUF, cursor);
289 *wp = c;
290}
291
292addto(buf, str)
293 register char *buf, *str;
294{
295
296 if ((buf[0] & (QUOTE|TRIM)) == OVERBUF)
297 return;
298 if (strlen(buf) + strlen(str) + 1 >= VBSIZE) {
299 buf[0] = OVERBUF;
300 return;
301 }
302 ignore(strcat(buf, str));
303}
304
305/*
306 * Note a change affecting a lot of lines, or non-visible
307 * lines. If the parameter must is set, then we only want
308 * to do this for open modes now; return and save for later
309 * notification in visual.
310 */
311noteit(must)
312 bool must;
313{
314 register int sdl = destline, sdc = destcol;
315
316 if (notecnt < 2 || !must && state == VISUAL)
317 return (0);
318 splitw++;
319 if (WBOT == WECHO)
320 vmoveitup(1, 1);
321 vigoto(WECHO, 0);
322 printf("%d %sline", notecnt, notesgn);
323 if (notecnt > 1)
324 putchar('s');
325 if (*notenam) {
326 printf(" %s", notenam);
327 if (*(strend(notenam) - 1) != 'e')
328 putchar('e');
329 putchar('d');
330 }
331 vclreol();
332 notecnt = 0;
333 if (state != VISUAL)
334 vcnt = vcline = 0;
335 splitw = 0;
336 if (state == ONEOPEN || state == CRTOPEN)
337 vup1();
338 destline = sdl; destcol = sdc;
339 return (1);
340}
341
342/*
343 * Rrrrringgggggg.
344 * If possible, use flash (VB).
345 */
346beep()
347{
348
349 if (VB)
350 vputp(VB, 0);
351 else
352 vputc(CTRL(g));
353}
354
355/*
356 * Map the command input character c,
357 * for keypads and labelled keys which do cursor
358 * motions. I.e. on an adm3a we might map ^K to ^P.
359 * DM1520 for example has a lot of mappable characters.
360 */
361
362map(c,maps)
363 register int c;
364 register struct maps *maps;
365{
366 register int d;
367 register char *p, *q;
368 char b[10]; /* Assumption: no keypad sends string longer than 10 */
369
370 /*
371 * Mapping for special keys on the terminal only.
372 * BUG: if there's a long sequence and it matches
373 * some chars and then misses, we lose some chars.
374 *
375 * For this to work, some conditions must be met.
376 * 1) Keypad sends SHORT (2 or 3 char) strings
377 * 2) All strings sent are same length & similar
378 * 3) The user is unlikely to type the first few chars of
379 * one of these strings very fast.
380 * Note: some code has been fixed up since the above was laid out,
381 * so conditions 1 & 2 are probably not required anymore.
382 * However, this hasn't been tested with any first char
383 * that means anything else except escape.
384 */
385#ifdef MDEBUG
386 if (trace)
387 fprintf(trace,"map(%c): ",c);
388#endif
389 b[0] = c;
390 b[1] = 0;
391 for (d=0; maps[d].mapto; d++) {
392#ifdef MDEBUG
393 if (trace)
394 fprintf(trace,"d=%d, ",d);
395#endif
396 if (p = maps[d].cap) {
397 for (q=b; *p; p++, q++) {
398#ifdef MDEBUG
399 if (trace)
400 fprintf(trace,"q->b[%d], ",q-b);
401#endif
402 if (*q==0) {
403 /*
404 * This test is oversimplified, but
405 * should work mostly. It handles the
406 * case where we get an ESCAPE that
407 * wasn't part of a keypad string.
408 */
409 if ((c=='#' ? peekkey() : fastpeekkey()) == 0) {
410#ifdef MDEBUG
411 if (trace)
412 fprintf(trace,"fpk=0: return %c",c);
413#endif
414 macpush(&b[1],1);
415 return(c);
416 }
417 *q = getkey();
418 q[1] = 0;
419 }
420 if (*p != *q)
421 goto contin;
422 }
423 macpush(maps[d].mapto,1);
424 c = getkey();
425#ifdef MDEBUG
426 if (trace)
427 fprintf(trace,"Success: return %c",c);
428#endif
429 return(c); /* first char of map string */
430 contin:;
431 }
432 }
433#ifdef MDEBUG
434 if (trace)
435 fprintf(trace,"Fail: return %c",c); /* DEBUG */
436#endif
437 macpush(&b[1],0);
438 return(c);
439}
440
441/*
442 * Push st onto the front of vmacp. This is tricky because we have to
443 * worry about where vmacp was previously pointing. We also have to
444 * check for overflow (which is typically from a recursive macro)
445 * Finally we have to set a flag so the whole thing can be undone.
446 * canundo is 1 iff we want to be able to undo the macro. This
447 * is false for, for example, pushing back lookahead from fastpeekkey(),
448 * since otherwise two fast escapes can clobber our undo.
449 */
450macpush(st, canundo)
451char *st;
452int canundo;
453{
454 char tmpbuf[BUFSIZ];
455
456 if (st==0 || *st==0)
457 return;
458#ifdef TRACE
459 if (trace)
460 fprintf(trace, "macpush(%s)",st);
461#endif
462 if (strlen(vmacp) + strlen(st) > BUFSIZ)
463 error("Macro too long@ - maybe recursive?");
464 if (vmacp) {
465 strcpy(tmpbuf, vmacp);
466 canundo = 0; /* can't undo inside a macro anyway */
467 }
468 strcpy(vmacbuf, st);
469 if (vmacp)
470 strcat(vmacbuf, tmpbuf);
471 vmacp = vmacbuf;
472 /* arrange to be able to undo the whole macro */
473 if (canundo) {
474 inopen = -1; /* no need to save since it had to be 1 or -1 before */
475 otchng = tchng;
476 vsave();
477 saveall();
478 vundkind = VMANY;
479 }
480#ifdef TRACE
481 if (trace)
482 fprintf(trace, "saveall for macro: undkind=%d, unddel=%d, undap1=%d, undap2=%d, dol=%d, unddol=%d, truedol=%d\n", undkind, lineno(unddel), lineno(undap1), lineno(undap2), lineno(dol), lineno(unddol), lineno(truedol));
483#endif
484}
485
486/*
487 * Get a count from the keyed input stream.
488 * A zero count is indistinguishable from no count.
489 */
490vgetcnt()
491{
492 register int c, cnt;
493
494 cnt = 0;
495 for (;;) {
496 c = getkey();
497 if (!isdigit(c))
498 break;
499 cnt *= 10, cnt += c - '0';
500 }
501 ungetkey(c);
502 Xhadcnt = 1;
503 Xcnt = cnt;
504 return(cnt);
505}
506
507/*
508 * fastpeekkey is just like peekkey but insists the character come in
509 * fast (within 1 second). This will succeed if it is the 2nd char of
510 * a machine generated sequence (such as a function pad from an escape
511 * flavor terminal) but fail for a human hitting escape then waiting.
512 */
513fastpeekkey()
514{
515 int trapalarm();
516 register int c;
517
518 if (inopen == -1) /* don't work inside macros! */
519 return (0);
520 signal(SIGALRM, trapalarm);
521 alarm(1);
522 CATCH
523 c = peekkey();
524#ifdef MDEBUG
525 if (trace)
526 fprintf(trace,"[OK]",c);
527#endif
528 alarm(0);
529 ONERR
530 c = 0;
531#ifdef MDEBUG
532 if (trace)
533 fprintf(trace,"[TOUT]",c);
534#endif
535 ENDCATCH
536#ifdef MDEBUG
537 if (trace)
538 fprintf(trace,"[fpk:%o]",c);
539#endif
540 return(c);
541}
542
543trapalarm() {
544 alarm(0);
545 longjmp(vreslab,1);
546}