/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* Copyright (c) 1981 Regents of the University of California */ #include "ex.h" #include "ex_tty.h" #include "ex_vis.h" /* * Input routines for open/visual. * We handle upper case only terminals in visual and reading from the * echo area here as well as notification on large changes * which appears in the echo area. */ /* * Return the key. */ void ungetkey(int c) { if (Peekkey != ATTN) Peekkey = c; } /* * Return a keystroke, but never a ^@. */ int getkey(void) { int c; /* char --> int */ do { c = getbr(); if (c==0) (void) beep(); } while (c == 0); return (c); } /* * Tell whether next keystroke would be a ^@. */ int peekbr(void) { Peekkey = getbr(); return (Peekkey == 0); } short precbksl; /* * Get a keystroke, including a ^@. * If an key was returned with ungetkey, that * comes back first. Next comes unread input (e.g. * from repeating commands with .), and finally new * keystrokes. * * The hard work here is in mapping of \ escaped * characters on upper case only terminals. */ int getbr(void) { unsigned char ch; int c, d; unsigned char *colp; int cnt; static unsigned char Peek2key; extern short slevel, ttyindes; getATTN: if (Peekkey) { c = Peekkey; Peekkey = 0; return (c); } if (Peek2key) { c = Peek2key; Peek2key = 0; return (c); } if (vglobp) { if (*vglobp) return (lastvgk = *vglobp++); lastvgk = 0; return (ESCAPE); } if (vmacp) { if (*vmacp) return(*vmacp++); /* End of a macro or set of nested macros */ vmacp = 0; if (inopen == -1) /* don't mess up undo for esc esc */ vundkind = VMANY; inopen = 1; /* restore old setting now that macro done */ vch_mac = VC_NOTINMAC; } flusho(); again: if ((c=read(slevel == 0 ? 0 : ttyindes, &ch, 1)) != 1) { if (errno == EINTR) goto getATTN; else if (errno == EIO) kill(getpid(), SIGHUP); error(gettext("Input read error")); } c = ch; if (beehive_glitch && slevel==0 && c == ESCAPE) { if (read(0, &Peek2key, 1) != 1) goto getATTN; switch (Peek2key) { case 'C': /* SPOW mode sometimes sends \EC for space */ c = ' '; Peek2key = 0; break; case 'q': /* f2 -> ^C */ c = CTRL('c'); Peek2key = 0; break; case 'p': /* f1 -> esc */ Peek2key = 0; break; } } /* * The algorithm here is that of the UNIX kernel. * See the description in the programmers manual. */ if (UPPERCASE) { if (isupper(c)) c = tolower(c); if (c == '\\') { if (precbksl < 2) precbksl++; if (precbksl == 1) goto again; } else if (precbksl) { d = 0; if (islower(c)) d = toupper(c); else { colp = (unsigned char *)"({)}!|^~'~"; while (d = *colp++) if (d == c) { d = *colp++; break; } else colp++; } if (precbksl == 2) { if (!d) { Peekkey = c; precbksl = 0; c = '\\'; } } else if (d) c = d; else { Peekkey = c; precbksl = 0; c = '\\'; } } if (c != '\\') precbksl = 0; } #ifdef TRACE if (trace) { if (!techoin) { tfixnl(); techoin = 1; fprintf(trace, "*** Input: "); } tracec(c); } #endif lastvgk = 0; return (c); } /* * Get a key, but if a delete, quit or attention * is typed return 0 so we will abort a partial command. */ int getesc(void) { int c; c = getkey(); switch (c) { case CTRL('v'): case CTRL('q'): c = getkey(); return (c); case ATTN: case QUIT: ungetkey(c); return (0); case ESCAPE: return (0); } return (c); } /* * Peek at the next keystroke. */ int peekkey(void) { Peekkey = getkey(); return (Peekkey); } /* * Read a line from the echo area, with single character prompt c. * A return value of 1 means the user blewit or blewit away. */ int readecho(c) unsigned char c; { unsigned char *sc = cursor; int (*OP)(); bool waste; int OPeek; if (WBOT == WECHO) vclean(); else vclrech(0); splitw++; vgoto(WECHO, 0); putchar(c); vclreol(); vgoto(WECHO, 1); cursor = linebuf; linebuf[0] = 0; genbuf[0] = c; ixlatctl(1); if (peekbr()) { if (!INS[0] || (unsigned char)INS[128] == 0200) { INS[128] = 0; goto blewit; } vglobp = INS; } OP = Pline; Pline = normline; (void)vgetline(0, genbuf + 1, &waste, c); doomed = 0; /* don't care about doomed characters in echo line */ ixlatctl(0); if (Outchar == termchar) putchar('\n'); vscrap(); Pline = OP; if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL('h')) { cursor = sc; vclreol(); return (0); } blewit: OPeek = Peekkey==CTRL('h') ? 0 : Peekkey; Peekkey = 0; splitw = 0; vclean(); vshow(dot, NOLINE); vnline(sc); Peekkey = OPeek; return (1); } /* * A complete command has been defined for * the purposes of repeat, so copy it from * the working to the previous command buffer. */ void setLAST(void) { if (vglobp || vmacp) return; lastreg = vreg; lasthad = Xhadcnt; lastcnt = Xcnt; *lastcp = 0; CP(lastcmd, workcmd); } /* * Gather up some more text from an insert. * If the insertion buffer oveflows, then destroy * the repeatability of the insert. */ void addtext(unsigned char *cp) { if (vglobp) return; addto(INS, cp); if ((unsigned char)INS[128] == 0200) lastcmd[0] = 0; } void setDEL(void) { setBUF(DEL); } /* * Put text from cursor upto wcursor in BUF. */ void setBUF(unsigned char *BUF) { int c; unsigned char *wp = wcursor; c = *wp; *wp = 0; BUF[0] = 0; BUF[128] = 0; addto(BUF, cursor); *wp = c; } void addto(unsigned char *buf, unsigned char *str) { if ((unsigned char)buf[128] == 0200) return; if (strlen(buf) + strlen(str) + 1 >= VBSIZE) { buf[128] = 0200; return; } (void)strcat(buf, str); buf[128] = 0; } /* * Verbalize command name and embed it in message. */ char * verbalize(cnt, cmdstr, sgn) int cnt; char *cmdstr, *sgn; { if (cmdstr[0] == '\0') cmdstr = (char *)Command; if (sgn[0] == '\0') { switch (cmdstr[0]) { case 'c': if (cmdstr[1] == 'h') { viprintf((cnt == 1) ? gettext("1 line changed") : gettext("%d lines changed"), cnt); break; } else if (cmdstr[1] != 'o') { goto Default; } /* FALLTHROUGH */ case 't': if (cmdstr[1] != '\0') goto Default; viprintf((cnt == 1) ? gettext("1 line copied") : gettext("%d lines copied"), cnt); break; case 'd': viprintf((cnt == 1) ? gettext("1 line deleted") : gettext("%d lines deleted"), cnt); break; case 'j': viprintf((cnt == 1) ? gettext("1 line joined") : gettext("%d lines joined"), cnt); break; case 'm': viprintf((cnt == 1) ? gettext("1 line moved") : gettext("%d lines moved"), cnt); break; case 'p': viprintf((cnt == 1) ? gettext("1 line put") : gettext("%d lines put"), cnt); break; case 'y': viprintf((cnt == 1) ? gettext("1 line yanked") : gettext("%d lines yanked"), cnt); break; case '>': viprintf((cnt == 1) ? gettext("1 line >>ed") : gettext("%d lines >>ed"), cnt); break; case '=': viprintf((cnt == 1) ? gettext("1 line =ed") : gettext("%d lines =ed"), cnt); break; case '<': viprintf((cnt == 1) ? gettext("1 line <b[%d], ",q-b); #endif if (*q==0) { /* * Is there another char waiting? * * This test is oversimplified, but * should work mostly. It handles the * case where we get an ESCAPE that * wasn't part of a keypad string. */ if ((c=='#' ? peekkey() : fastpeekkey()) == 0) { #ifdef MDEBUG if (trace) fprintf(trace,"fpk=0: will return '%c'",c); #endif /* * Nothing waiting. Push back * what we peeked at & return * failure (c). * * We want to be able to undo * commands, but it's nonsense * to undo part of an insertion * so if in input mode don't. */ #ifdef MDEBUG if (trace) fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]); #endif macpush(&b[1],maps == arrows); #ifdef MDEBUG if (trace) fprintf(trace, "return %d\n", c); #endif return(c); } *q = getkey(); q[1] = 0; } if (*p != *q) goto contin; } macpush(maps[d].mapto,maps == arrows); /* * For all macros performed within insert, * append, or replacement mode, we must end * up returning back to that mode when we * return (except that append will become * insert for key, so cursor is not * in second column). * * In order to preserve backward movement * when leaving insert mode, an 'l' must be * done to compensate for the left done by * the (except when cursor is already * in the first column: i.e., outcol = 0). */ if ((maps == immacs) && strcmp(maps[d].descr, maps[d].cap)) { switch (commch) { case 'R': if (!strcmp(maps[d].descr, "home")) st = (unsigned char *)"R"; else if (outcol == 0) st = (unsigned char *)"R"; else st = (unsigned char *)"lR"; break; case 'i': if (!strcmp(maps[d].descr, "home")) st = (unsigned char *)"i"; else if (outcol == 0) st = (unsigned char *)"i"; else st = (unsigned char *)"li"; break; case 'a': if (!strcmp(maps[d].descr, "home")) st = (unsigned char *)"i"; else st = (unsigned char *)"a"; break; default: st = (unsigned char *)"i"; } if(strlen(vmacbuf) + strlen(st) > BUFSIZE) error(value(vi_TERSE) ? gettext("Macro too long") : gettext("Macro too long - maybe recursive?")); else /* * Macros such as function keys are * performed by leaving the insert, * replace, or append mode, executing * the proper cursor movement commands * and returning to the mode we are * currently in (commch). */ strcat(vmacbuf, st); } c = getkey(); #ifdef MDEBUG if (trace) fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c); #endif return(c); /* first char of map string */ contin:; } } #ifdef MDEBUG if (trace) fprintf(trace,"Fail: push(%s), return %c", &b[1], c); #endif macpush(&b[1],0); return(c); } /* * Push st onto the front of vmacp. This is tricky because we have to * worry about where vmacp was previously pointing. We also have to * check for overflow (which is typically from a recursive macro) * Finally we have to set a flag so the whole thing can be undone. * canundo is 1 iff we want to be able to undo the macro. This * is false for, for example, pushing back lookahead from fastpeekkey(), * since otherwise two fast escapes can clobber our undo. */ void macpush(unsigned char *st, int canundo) { unsigned char tmpbuf[BUFSIZE]; if (st==0 || *st==0) return; #ifdef MDEBUG if (trace) fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo); #endif if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZE) error(value(vi_TERSE) ? gettext("Macro too long") : gettext("Macro too long - maybe recursive?")); if (vmacp) { strcpy(tmpbuf, vmacp); if (!FIXUNDO) canundo = 0; /* can't undo inside a macro anyway */ } strcpy(vmacbuf, st); if (vmacp) strcat(vmacbuf, tmpbuf); vmacp = vmacbuf; /* arrange to be able to undo the whole macro */ if (canundo) { #ifdef notdef otchng = tchng; vsave(); saveall(); inopen = -1; /* no need to save since it had to be 1 or -1 before */ vundkind = VMANY; #endif vch_mac = VC_NOCHANGE; } } #ifdef UNDOTRACE visdump(s) unsigned char *s; { int i; if (!trace) return; fprintf(trace, "\n%s: basWTOP=%d, basWLINES=%d, WTOP=%d, WBOT=%d, WLINES=%d, WCOLS=%d, WECHO=%d\n", s, basWTOP, basWLINES, WTOP, WBOT, WLINES, WCOLS, WECHO); fprintf(trace, " vcnt=%d, vcline=%d, cursor=%d, wcursor=%d, wdot=%d\n", vcnt, vcline, cursor-linebuf, wcursor-linebuf, wdot-zero); for (i=0; i= 0) { signal(SIGALRM, trapalarm); setalarm(); } c = peekkey(); cancelalarm(); ONERR c = 0; ENDCATCH /* Should have an alternative method based on select for 4.2BSD */ return(c); } static int ftfd; struct requestbuf { short time; short signo; }; /* * Arrange for SIGALRM to come in shortly, so we don't * hang very long if the user didn't type anything. There are * various ways to do this on different systems. */ void setalarm(void) { unsigned char ftname[20]; struct requestbuf rb; #ifdef FTIOCSET /* * Use nonstandard "fast timer" to get better than * one second resolution. We must wait at least * 1/15th of a second because some keypads don't * transmit faster than this. */ /* Open ft psuedo-device - we need our own copy. */ if (ftfd == 0) { strcpy(ftname, "/dev/ft0"); while (ftfd <= 0 && ftname[7] <= '~') { ftfd = open(ftname, 0); if (ftfd <= 0) ftname[7] ++; } } if (ftfd <= 0) { /* Couldn't open a /dev/ft? */ alarm(1); } else { rb.time = 6; /* 6 ticks = 100 ms > 67 ms. */ rb.signo = SIGALRM; ioctl(ftfd, FTIOCSET, &rb); } #else /* * No special capabilities, so we use alarm, with 1 sec. resolution. */ alarm(1); #endif } /* * Get rid of any impending incoming SIGALRM. */ void cancelalarm(void) { struct requestbuf rb; #ifdef FTIOCSET if (ftfd > 0) { rb.time = 0; rb.signo = SIGALRM; ioctl(ftfd, FTIOCCANCEL, &rb); } #endif alarm(0); /* Have to do this whether or not FTIOCSET */ } void trapalarm() { alarm(0); longjmp(vreslab,1); }