/* * zle_misc.c - miscellaneous editor routines * * This file is part of zsh, the Z shell. * * Copyright (c) 1992-1997 Paul Falstad * All rights reserved. * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and to distribute modified versions of this software for any * purpose, provided that the above copyright notice and the following * two paragraphs appear in all copies of this software. * * In no event shall Paul Falstad or the Zsh Development Group be liable * to any party for direct, indirect, special, incidental, or consequential * damages arising out of the use of this software and its documentation, * even if Paul Falstad and the Zsh Development Group have been advised of * the possibility of such damage. * * Paul Falstad and the Zsh Development Group specifically disclaim any * warranties, including, but not limited to, the implied warranties of * merchantability and fitness for a particular purpose. The software * provided hereunder is on an "as is" basis, and Paul Falstad and the * Zsh Development Group have no obligation to provide maintenance, * support, updates, enhancements, or modifications. * */ #include "zle.mdh" #include "zle_misc.pro" /* insert a metafied string, with repetition and suffix removal */ /**/ void doinsert(char *str) { char *s; int len = ztrlen(str); int c1 = *str == Meta ? STOUC(str[1])^32 : STOUC(*str);/* first character */ int neg = zmult < 0; /* insert *after* the cursor? */ int m = neg ? -zmult : zmult; /* number of copies to insert */ iremovesuffix(c1); invalidatelist(); if(insmode) spaceinline(m * len); else if(cs + m * len > ll) spaceinline(cs + m * len - ll); while(m--) for(s = str; *s; s++) line[cs++] = *s == Meta ? *++s ^ 32 : *s; if(neg) cs += zmult * len; } /**/ void selfinsert(void) { char s[3], *p = s; if(imeta(c)) { *p++ = Meta; c ^= 32; } *p++ = c; *p = 0; doinsert(s); } /**/ void selfinsertunmeta(void) { c &= 0x7f; if (c == '\r') c = '\n'; selfinsert(); } /**/ void deletechar(void) { if (zmult < 0) { zmult = -zmult; backwarddeletechar(); zmult = -zmult; return; } if (cs + zmult <= ll) { cs += zmult; backdel(zmult); } else feep(); } /**/ void backwarddeletechar(void) { if (zmult < 0) { zmult = -zmult; deletechar(); zmult = -zmult; return; } backdel(zmult > cs ? cs : zmult); } /**/ void killwholeline(void) { int i, fg, n = zmult; if (n < 0) return; while (n--) { if ((fg = (cs && cs == ll))) cs--; while (cs && line[cs - 1] != '\n') cs--; for (i = cs; i != ll && line[i] != '\n'; i++); forekill(i - cs + (i != ll), fg); } } /**/ void killbuffer(void) { cs = 0; forekill(ll, 0); } /**/ void backwardkillline(void) { int i = 0, n = zmult; if (n < 0) { zmult = -n; killline(); zmult = n; return; } while (n--) { if (cs && line[cs - 1] == '\n') cs--, i++; else while (cs && line[cs - 1] != '\n') cs--, i++; } forekill(i, 1); } /**/ void gosmacstransposechars(void) { int cc; if (cs < 2 || line[cs - 1] == '\n' || line[cs - 2] == '\n') { if (cs == ll || line[cs] == '\n' || ((cs + 1 == ll || line[cs + 1] == '\n') && (!cs || line[cs - 1] == '\n'))) { feep(); return; } cs += (cs == 0 || line[cs - 1] == '\n') ? 2 : 1; } cc = line[cs - 2]; line[cs - 2] = line[cs - 1]; line[cs - 1] = cc; } /**/ void transposechars(void) { int cc, ct; int n = zmult; int neg = n < 0; if (neg) n = -n; while (n--) { if (!(ct = cs) || line[cs - 1] == '\n') { if (ll == cs || line[cs] == '\n') { feep(); return; } if (!neg) cs++; ct++; } if (neg) { if (cs && line[cs - 1] != '\n') { cs--; if (ct > 1 && line[ct - 2] != '\n') ct--; } } else { if (cs != ll && line[cs] != '\n') cs++; } if (ct == ll || line[ct] == '\n') ct--; if (ct < 1 || line[ct - 1] == '\n') { feep(); return; } cc = line[ct - 1]; line[ct - 1] = line[ct]; line[ct] = cc; } } /**/ void poundinsert(void) { cs = 0; vifirstnonblank(); if (line[cs] != '#') { spaceinline(1); line[cs] = '#'; cs = findeol(); while(cs != ll) { cs++; vifirstnonblank(); spaceinline(1); line[cs] = '#'; cs = findeol(); } } else { foredel(1); cs = findeol(); while(cs != ll) { cs++; vifirstnonblank(); if(line[cs] == '#') foredel(1); cs = findeol(); } } done = 1; } /**/ void acceptline(void) { done = 1; } /**/ void acceptandhold(void) { pushnode(bufstack, metafy((char *)line, ll, META_DUP)); stackcs = cs; done = 1; } /**/ void killline(void) { int i = 0, n = zmult; if (n < 0) { zmult = -n; backwardkillline(); zmult = n; return; } while (n--) { if (line[cs] == '\n') cs++, i++; else while (cs != ll && line[cs] != '\n') cs++, i++; } backkill(i, 0); } /**/ void killregion(void) { if (mark > ll) mark = ll; if (mark > cs) forekill(mark - cs, 0); else backkill(cs - mark, 1); } /**/ void copyregionaskill(void) { if (mark > ll) mark = ll; if (mark > cs) cut(cs, mark - cs, 0); else cut(mark, cs - mark, 1); } static int kct, yankb, yanke; /**/ void yank(void) { Cutbuffer buf = &cutbuf; int n = zmult; if (n < 0) return; if (zmod.flags & MOD_VIBUF) buf = &vibuf[zmod.vibuf]; if (!buf->buf) { feep(); return; } mark = cs; yankb = cs; while (n--) { kct = kringnum; spaceinline(buf->len); memcpy((char *)line + cs, buf->buf, buf->len); cs += buf->len; yanke = cs; } } /**/ void yankpop(void) { int cc; if (!(lastcmd & ZLE_YANK) || !kring[kct].buf) { feep(); return; } cs = yankb; foredel(yanke - yankb); cc = kring[kct].len; spaceinline(cc); memcpy((char *)line + cs, kring[kct].buf, cc); cs += cc; yanke = cs; kct = (kct + KRINGCT - 1) % KRINGCT; } /**/ void overwritemode(void) { insmode ^= 1; } /**/ void whatcursorposition(void) { char msg[100]; char *s = msg; int bol = findbol(); int c = STOUC(line[cs]); if (cs == ll) strucpy(&s, "EOF"); else { strucpy(&s, "Char: "); switch (c) { case ' ': strucpy(&s, "SPC"); break; case '\t': strucpy(&s, "TAB"); break; case '\n': strucpy(&s, "LFD"); break; default: if (imeta(c)) { *s++ = Meta; *s++ = c ^ 32; } else *s++ = c; } sprintf(s, " (0%o, %d, 0x%x)", c, c, c); s += strlen(s); } sprintf(s, " point %d of %d(%d%%) column %d", cs+1, ll+1, ll ? 100 * cs / ll : 0, cs - bol); showmsg(msg); } /**/ void undefinedkey(void) { feep(); } /**/ void quotedinsert(void) { #ifndef HAS_TIO struct sgttyb sob; sob = shttyinfo.sgttyb; sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO; ioctl(SHTTY, TIOCSETN, &sob); #endif c = getkey(0); #ifndef HAS_TIO setterm(); #endif if (c < 0) feep(); else selfinsert(); } /**/ void digitargument(void) { int sign = (zmult < 0) ? -1 : 1; if (!(zmod.flags & MOD_TMULT)) zmod.tmult = 0; if (zmod.flags & MOD_NEG) { /* If we just had a negative argument, this is the digit, * * rather than the -1 assumed by negargument() */ zmod.tmult = sign * (c & 0xf); zmod.flags &= ~MOD_NEG; } else zmod.tmult = zmod.tmult * 10 + sign * (c & 0xf); zmod.flags |= MOD_TMULT; prefixflag = 1; } /**/ void negargument(void) { if(zmod.flags & MOD_TMULT) { feep(); return; } zmod.tmult = -1; zmod.flags |= MOD_TMULT|MOD_NEG; prefixflag = 1; } /**/ void universalargument(void) { int digcnt = 0, pref = 0, minus = 1, gotk; while ((gotk = getkey(0)) != EOF) { if (gotk == '-' && !digcnt) { minus = -1; digcnt++; } else if (gotk >= '0' && gotk <= '9') { pref = pref * 10 + (gotk & 0xf); digcnt++; } else { ungetkey(gotk); break; } } if (digcnt) zmod.tmult = minus * (pref ? pref : 1); else zmod.tmult *= 4; zmod.flags |= MOD_TMULT; prefixflag = 1; } /**/ void copyprevword(void) { int len, t0; for (t0 = cs - 1; t0 >= 0; t0--) if (iword(line[t0])) break; for (; t0 >= 0; t0--) if (!iword(line[t0])) break; if (t0) t0++; len = cs - t0; spaceinline(len); memcpy((char *)&line[cs], (char *)&line[t0], len); cs += len; } /**/ void sendbreak(void) { errflag = 1; } /**/ void quoteregion(void) { char *str; size_t len; if (mark > ll) mark = ll; if (mark < cs) { int tmp = mark; mark = cs; cs = tmp; } str = (char *)hcalloc(len = mark - cs); memcpy(str, (char *)&line[cs], len); foredel(len); str = makequote(str, &len); spaceinline(len); memcpy((char *)&line[cs], str, len); mark = cs; cs += len; } /**/ void quoteline(void) { char *str; size_t len = ll; str = makequote((char *)line, &len); sizeline(len); memcpy(line, str, len); cs = ll = len; } /**/ static char * makequote(char *str, size_t *len) { int qtct = 0; char *l, *ol; char *end = str + *len; for (l = str; l < end; l++) if (*l == '\'') qtct++; *len += 2 + qtct*3; l = ol = (char *)halloc(*len); *l++ = '\''; for (; str < end; str++) if (*str == '\'') { *l++ = '\''; *l++ = '\\'; *l++ = '\''; *l++ = '\''; } else *l++ = *str; *l++ = '\''; return ol; } static char *cmdbuf; static LinkList cmdll; static int cmdambig; /**/ static void scancompcmd(HashNode hn, int flags) { int l; Thingy t = (Thingy) hn; if(strpfx(cmdbuf, t->nam)) { addlinknode(cmdll, t->nam); l = pfxlen(peekfirst(cmdll), t->nam); if (l < cmdambig) cmdambig = l; } } #define NAMLEN 60 /**/ Thingy executenamedcommand(char *prmt) { Thingy cmd; int len, l = strlen(prmt); char *ptr; char *okeymap = curkeymapname; cmdbuf = halloc(l + NAMLEN + 2); strcpy(cmdbuf, prmt); statusline = cmdbuf; selectkeymap("main", 1); ptr = cmdbuf += l; len = 0; for (;;) { *ptr = '_'; statusll = l + len + 1; refresh(); if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) { statusline = NULL; selectkeymap(okeymap, 1); return NULL; } if(cmd == Th(z_clearscreen)) { clearscreen(); } else if(cmd == Th(z_redisplay)) { redisplay(); } else if(cmd == Th(z_viquotedinsert)) { *ptr = '^'; refresh(); c = getkey(0); if(c == EOF || !c || len == NAMLEN) feep(); else *ptr++ = c, len++; } else if(cmd == Th(z_quotedinsert)) { if((c = getkey(0)) == EOF || !c || len == NAMLEN) feep(); else *ptr++ = c, len++; } else if(cmd == Th(z_backwarddeletechar) || cmd == Th(z_vibackwarddeletechar)) { if (len) len--, ptr--; } else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) || cmd == Th(z_vibackwardkillword)) { while (len && (len--, *--ptr != '-')); } else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) || cmd == Th(z_backwardkillline)) { len = 0; ptr = cmdbuf; } else { if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) { Thingy r; unambiguous: *ptr = 0; r = rthingy(cmdbuf); if (!(r->flags & DISABLED)) { unrefthingy(r); statusline = NULL; selectkeymap(okeymap, 1); return r; } unrefthingy(r); } if(cmd == Th(z_selfinsertunmeta)) { c &= 0x7f; if(c == '\r') c = '\n'; cmd = Th(z_selfinsert); } if (cmd == Th(z_listchoices) || cmd == Th(z_deletecharorlist) || cmd == Th(z_expandorcomplete) || cmd == Th(z_completeword) || cmd == Th(z_expandorcompleteprefix) || cmd == Th(z_vicmdmode) || cmd == Th(z_acceptline) || c == ' ' || c == '\t') { cmdambig = 100; HEAPALLOC { cmdll = newlinklist(); *ptr = 0; scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0); } LASTALLOC; if (empty(cmdll)) feep(); else if (cmd == Th(z_listchoices) || cmd == Th(z_deletecharorlist)) { int zmultsav = zmult; *ptr = '_'; statusll = l + len + 1; zmult = 1; listlist(cmdll); zmult = zmultsav; } else if (!nextnode(firstnode(cmdll))) { strcpy(ptr = cmdbuf, peekfirst(cmdll)); ptr += (len = strlen(ptr)); if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) goto unambiguous; } else { strcpy(cmdbuf, peekfirst(cmdll)); ptr = cmdbuf + cmdambig; *ptr = '_'; if (isset(AUTOLIST) && !(isset(LISTAMBIGUOUS) && cmdambig > len)) { int zmultsav = zmult; if (isset(LISTBEEP)) feep(); statusll = l + cmdambig + 1; zmult = 1; listlist(cmdll); zmult = zmultsav; } len = cmdambig; } } else { if (len == NAMLEN || icntrl(c) || cmd != Th(z_selfinsert)) feep(); else *ptr++ = c, len++; } } handlefeep(); } } /*****************/ /* Suffix system */ /*****************/ /* * The completion system sometimes tentatively adds a suffix to a word, * which can be removed depending on what is inserted next. These * functions provide the capability to handle a removable suffix. * * Any removable suffix consists of characters immediately before the * cursor. Whether it is removed depends on the next editing action. * There can be more than one suffix simultaneously present, with * different actions deleting different numbers of characters. * * If the next editing action changes the buffer other than by inserting * characters, normally the suffix should be removed so as to leave a * meaningful complete word. The behaviour should be the same if the * next character inserted is a word separator. If the next character * reasonably belongs where it is typed, or if the next editing action * is a deletion, the suffix should not be removed. Other reasons for * suffix removal may have other behaviour. * * In order to maintain a consistent state, after a suffix has been added * the table *must* be zeroed, one way or another, before the buffer is * changed. If the suffix is not being removed, call fixsuffix() to * indicate that it is being permanently fixed. */ /* Length of suffix to remove when inserting each possible character value. * * suffixlen[256] is the length to remove for non-insertion editing actions. */ /**/ int suffixlen[257]; /* Set up suffix: the last n characters are a suffix that should be * * removed in the usual word end conditions. */ /**/ void makesuffix(int n) { suffixlen[256] = suffixlen[' '] = suffixlen['\t'] = suffixlen['\n'] = n; } /* Set up suffix for parameter names: the last n characters are a suffix * * that should be removed if the next character is one of the ones that * * needs to go immediately after the parameter name. br indicates that * * the name is in braces (${PATH} instead of $PATH), so the extra * * characters that can only be used in braces are included. */ /**/ void makeparamsuffix(int br, int n) { if(br || unset(KSHARRAYS)) suffixlen[':'] = suffixlen['['] = n; if(br) { suffixlen['#'] = suffixlen['%'] = suffixlen['?'] = n; suffixlen['-'] = suffixlen['+'] = suffixlen['='] = n; /*{*/ suffixlen['}'] = n; } } /* Remove suffix, if there is one, when inserting character c. */ /**/ void iremovesuffix(int c) { int sl = suffixlen[c]; if(sl) { backdel(sl); invalidatelist(); } fixsuffix(); } /* Fix the suffix in place, if there is one, making it non-removable. */ /**/ void fixsuffix(void) { memset(suffixlen, 0, sizeof(suffixlen)); }