diff --git a/contrib/libedit/Makefile b/contrib/libedit/Makefile index 105a6a92e0ee..f7cd3baf885f 100644 --- a/contrib/libedit/Makefile +++ b/contrib/libedit/Makefile @@ -1,143 +1,149 @@ -# $NetBSD: Makefile,v 1.66 2019/10/13 07:28:10 mrg Exp $ +# $NetBSD: Makefile,v 1.70 2023/08/03 14:56:36 rin Exp $ # @(#)Makefile 8.1 (Berkeley) 6/4/93 USE_SHLIBDIR= yes WARNS?= 5 LIB= edit LIBDPLIBS+= terminfo ${.CURDIR}/../libterminfo .include "bsd.own.mk" COPTS+= -Wunused-parameter CWARNFLAGS.gcc+= -Wconversion CWARNFLAGS.clang+= -Wno-cast-qual SRCS = chared.c chartype.c common.c el.c eln.c emacs.c filecomplete.c \ hist.c history.c historyn.c keymacro.c literal.c map.c \ parse.c prompt.c read.c readline.c refresh.c search.c sig.c \ terminal.c tokenizer.c tokenizern.c tty.c vi.c MAN= editline.3 editrc.5 editline.7 +FILES+= libedit.pc +FILESOWN_libedit.pc= ${BINOWN} +FILESGRP_libedit.pc= ${BINGRP} +FILESMODE_libedit.pc= ${NONBINMODE} +FILESDIR_libedit.pc= /usr/lib/pkgconfig + MLINKS= \ editline.3 el_deletestr.3 \ editline.3 el_end.3 \ editline.3 el_get.3 \ editline.3 el_getc.3 \ editline.3 el_gets.3 \ editline.3 el_init.3 \ editline.3 el_init_fd.3 \ editline.3 el_insertstr.3 \ editline.3 el_line.3 \ editline.3 el_parse.3 \ editline.3 el_push.3 \ editline.3 el_reset.3 \ editline.3 el_resize.3 \ editline.3 el_set.3 \ editline.3 el_source.3 \ editline.3 history.3 \ editline.3 history_end.3 \ editline.3 history_init.3 \ editline.3 tok_end.3 \ editline.3 tok_init.3 \ editline.3 tok_line.3 \ editline.3 tok_reset.3 \ editline.3 tok_str.3 MLINKS+= \ editline.3 el_wdeletestr.3 \ editline.3 el_wget.3 \ editline.3 el_wgetc.3 \ editline.3 el_wgets.3 \ editline.3 el_winsertstr.3 \ editline.3 el_wline.3 \ editline.3 el_wparse.3 \ editline.3 el_wpush.3 \ editline.3 el_wset.3 \ editline.3 history_w.3 \ editline.3 history_wend.3 \ editline.3 history_winit.3 \ editline.3 tok_wend.3 \ editline.3 tok_winit.3 \ editline.3 tok_wline.3 \ editline.3 tok_wreset.3 \ editline.3 tok_wstr.3 LIBEDITDIR?=${.CURDIR} INCS= histedit.h INCSDIR=/usr/include CLEANFILES+=common.h.tmp emacs.h.tmp fcns.h.tmp func.h.tmp CLEANFILES+=help.h.tmp vi.h.tmp tc1.o tc1 .depend CPPFLAGS+=-I. -I${LIBEDITDIR} CPPFLAGS+=-I. -I${.CURDIR} #CPPFLAGS+=-DDEBUG_TTY -DDEBUG_KEY -DDEBUG -DDEBUG_REFRESH #CPPFLAGS+=-DDEBUG_PASTE -DDEBUG_EDIT AHDR=vi.h emacs.h common.h ASRC=${LIBEDITDIR}/vi.c ${LIBEDITDIR}/emacs.c ${LIBEDITDIR}/common.c DPSRCS+= ${AHDR} fcns.h func.h help.h CLEANFILES+= ${AHDR} fcns.h func.h help.h SUBDIR= readline .depend: ${AHDR} fcns.h func.h help.h vi.h: vi.c makelist Makefile ${_MKTARGET_CREATE} ${HOST_SH} ${LIBEDITDIR}/makelist -h ${LIBEDITDIR}/vi.c \ > ${.TARGET}.tmp && \ mv ${.TARGET}.tmp ${.TARGET} emacs.h: emacs.c makelist Makefile ${_MKTARGET_CREATE} ${HOST_SH} ${LIBEDITDIR}/makelist -h ${LIBEDITDIR}/emacs.c \ > ${.TARGET}.tmp && \ mv ${.TARGET}.tmp ${.TARGET} common.h: common.c makelist Makefile ${_MKTARGET_CREATE} ${HOST_SH} ${LIBEDITDIR}/makelist -h ${LIBEDITDIR}/common.c \ > ${.TARGET}.tmp && \ mv ${.TARGET}.tmp ${.TARGET} fcns.h: ${AHDR} makelist Makefile ${_MKTARGET_CREATE} ${HOST_SH} ${LIBEDITDIR}/makelist -fh ${AHDR} > ${.TARGET}.tmp && \ mv ${.TARGET}.tmp ${.TARGET} func.h: ${AHDR} makelist Makefile ${_MKTARGET_CREATE} ${HOST_SH} ${LIBEDITDIR}/makelist -fc ${AHDR} > ${.TARGET}.tmp && \ mv ${.TARGET}.tmp ${.TARGET} help.h: ${ASRC} makelist Makefile ${_MKTARGET_CREATE} ${HOST_SH} ${LIBEDITDIR}/makelist -bh ${ASRC} > ${.TARGET}.tmp && \ mv ${.TARGET}.tmp ${.TARGET} tc1.o: ${LIBEDITDIR}/TEST/tc1.c tc1: libedit.a tc1.o ${_MKTARGET_LINK} ${CC} ${LDFLAGS} ${.ALLSRC} -o ${.TARGET} libedit.a ${LDADD} -ltermlib .include .include # XXX .if defined(HAVE_GCC) COPTS.editline.c+= -Wno-cast-qual COPTS.literal.c+= -Wno-sign-conversion COPTS.tokenizer.c+= -Wno-cast-qual COPTS.tokenizern.c+= -Wno-cast-qual .endif -COPTS.history.c+= ${GCC_NO_STRINGOP_OVERFLOW} -COPTS.historyn.c+= ${GCC_NO_STRINGOP_OVERFLOW} -COPTS.readline.c+= ${GCC_NO_STRINGOP_TRUNCATION} ${GCC_NO_STRINGOP_OVERFLOW} +COPTS.history.c+= ${CC_WNO_STRINGOP_OVERFLOW} +COPTS.historyn.c+= ${CC_WNO_STRINGOP_OVERFLOW} +COPTS.readline.c+= ${CC_WNO_STRINGOP_TRUNCATION} ${CC_WNO_STRINGOP_OVERFLOW} diff --git a/contrib/libedit/chared.c b/contrib/libedit/chared.c index 03d31ddeec85..72d84e46740f 100644 --- a/contrib/libedit/chared.c +++ b/contrib/libedit/chared.c @@ -1,813 +1,815 @@ -/* $NetBSD: chared.c,v 1.63 2022/10/30 19:11:31 christos Exp $ */ +/* $NetBSD: chared.c,v 1.64 2024/06/29 14:13:14 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: chared.c,v 1.63 2022/10/30 19:11:31 christos Exp $"); +__RCSID("$NetBSD: chared.c,v 1.64 2024/06/29 14:13:14 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * chared.c: Character editor utilities */ #include #include #include #include "el.h" #include "common.h" #include "fcns.h" /* value to leave unused in line buffer */ #define EL_LEAVE 2 /* cv_undo(): * Handle state for the vi undo command */ libedit_private void cv_undo(EditLine *el) { c_undo_t *vu = &el->el_chared.c_undo; c_redo_t *r = &el->el_chared.c_redo; size_t size; /* Save entire line for undo */ size = (size_t)(el->el_line.lastchar - el->el_line.buffer); vu->len = (ssize_t)size; vu->cursor = (int)(el->el_line.cursor - el->el_line.buffer); (void)memcpy(vu->buf, el->el_line.buffer, size * sizeof(*vu->buf)); /* save command info for redo */ r->count = el->el_state.doingarg ? el->el_state.argument : 0; r->action = el->el_chared.c_vcmd.action; r->pos = r->buf; r->cmd = el->el_state.thiscmd; r->ch = el->el_state.thisch; } /* cv_yank(): * Save yank/delete data for paste */ libedit_private void cv_yank(EditLine *el, const wchar_t *ptr, int size) { c_kill_t *k = &el->el_chared.c_kill; (void)memcpy(k->buf, ptr, (size_t)size * sizeof(*k->buf)); k->last = k->buf + size; } /* c_insert(): * Insert num characters */ libedit_private void c_insert(EditLine *el, int num) { wchar_t *cp; if (el->el_line.lastchar + num >= el->el_line.limit) { if (!ch_enlargebufs(el, (size_t)num)) return; /* can't go past end of buffer */ } if (el->el_line.cursor < el->el_line.lastchar) { /* if I must move chars */ for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--) cp[num] = *cp; } el->el_line.lastchar += num; } /* c_delafter(): * Delete num characters after the cursor */ libedit_private void c_delafter(EditLine *el, int num) { if (el->el_line.cursor + num > el->el_line.lastchar) num = (int)(el->el_line.lastchar - el->el_line.cursor); if (el->el_map.current != el->el_map.emacs) { cv_undo(el); cv_yank(el, el->el_line.cursor, num); } if (num > 0) { wchar_t *cp; for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) *cp = cp[num]; el->el_line.lastchar -= num; } } /* c_delafter1(): * Delete the character after the cursor, do not yank */ libedit_private void c_delafter1(EditLine *el) { wchar_t *cp; for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) *cp = cp[1]; el->el_line.lastchar--; } /* c_delbefore(): * Delete num characters before the cursor */ libedit_private void c_delbefore(EditLine *el, int num) { if (el->el_line.cursor - num < el->el_line.buffer) num = (int)(el->el_line.cursor - el->el_line.buffer); if (el->el_map.current != el->el_map.emacs) { cv_undo(el); cv_yank(el, el->el_line.cursor - num, num); } if (num > 0) { wchar_t *cp; for (cp = el->el_line.cursor - num; &cp[num] <= el->el_line.lastchar; cp++) *cp = cp[num]; el->el_line.lastchar -= num; } } /* c_delbefore1(): * Delete the character before the cursor, do not yank */ libedit_private void c_delbefore1(EditLine *el) { wchar_t *cp; for (cp = el->el_line.cursor - 1; cp <= el->el_line.lastchar; cp++) *cp = cp[1]; el->el_line.lastchar--; } /* ce__isword(): * Return if p is part of a word according to emacs */ libedit_private int ce__isword(wint_t p) { return iswalnum(p) || wcschr(L"*?_-.[]~=", p) != NULL; } /* cv__isword(): * Return if p is part of a word according to vi */ libedit_private int cv__isword(wint_t p) { if (iswalnum(p) || p == L'_') return 1; if (iswgraph(p)) return 2; return 0; } /* cv__isWord(): * Return if p is part of a big word according to vi */ libedit_private int cv__isWord(wint_t p) { return !iswspace(p); } /* c__prev_word(): * Find the previous word */ libedit_private wchar_t * c__prev_word(wchar_t *p, wchar_t *low, int n, int (*wtest)(wint_t)) { p--; while (n--) { while ((p >= low) && !(*wtest)(*p)) p--; while ((p >= low) && (*wtest)(*p)) p--; } /* cp now points to one character before the word */ p++; if (p < low) p = low; /* cp now points where we want it */ return p; } /* c__next_word(): * Find the next word */ libedit_private wchar_t * c__next_word(wchar_t *p, wchar_t *high, int n, int (*wtest)(wint_t)) { while (n--) { while ((p < high) && !(*wtest)(*p)) p++; while ((p < high) && (*wtest)(*p)) p++; } if (p > high) p = high; /* p now points where we want it */ return p; } /* cv_next_word(): * Find the next word vi style */ libedit_private wchar_t * cv_next_word(EditLine *el, wchar_t *p, wchar_t *high, int n, int (*wtest)(wint_t)) { int test; while (n--) { test = (*wtest)(*p); while ((p < high) && (*wtest)(*p) == test) p++; /* * vi historically deletes with cw only the word preserving the * trailing whitespace! This is not what 'w' does.. */ if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT)) while ((p < high) && iswspace(*p)) p++; } /* p now points where we want it */ if (p > high) return high; else return p; } /* cv_prev_word(): * Find the previous word vi style */ libedit_private wchar_t * cv_prev_word(wchar_t *p, wchar_t *low, int n, int (*wtest)(wint_t)) { int test; p--; while (n--) { while ((p > low) && iswspace(*p)) p--; test = (*wtest)(*p); while ((p >= low) && (*wtest)(*p) == test) p--; + if (p < low) + return low; } p++; /* p now points where we want it */ if (p < low) return low; else return p; } /* cv_delfini(): * Finish vi delete action */ libedit_private void cv_delfini(EditLine *el) { int size; int action = el->el_chared.c_vcmd.action; if (action & INSERT) el->el_map.current = el->el_map.key; if (el->el_chared.c_vcmd.pos == 0) /* sanity */ return; size = (int)(el->el_line.cursor - el->el_chared.c_vcmd.pos); if (size == 0) size = 1; el->el_line.cursor = el->el_chared.c_vcmd.pos; if (action & YANK) { if (size > 0) cv_yank(el, el->el_line.cursor, size); else cv_yank(el, el->el_line.cursor + size, -size); } else { if (size > 0) { c_delafter(el, size); re_refresh_cursor(el); } else { c_delbefore(el, -size); el->el_line.cursor += size; } } el->el_chared.c_vcmd.action = NOP; } /* cv__endword(): * Go to the end of this word according to vi */ libedit_private wchar_t * cv__endword(wchar_t *p, wchar_t *high, int n, int (*wtest)(wint_t)) { int test; p++; while (n--) { while ((p < high) && iswspace(*p)) p++; test = (*wtest)(*p); while ((p < high) && (*wtest)(*p) == test) p++; } p--; return p; } /* ch_init(): * Initialize the character editor */ libedit_private int ch_init(EditLine *el) { el->el_line.buffer = el_calloc(EL_BUFSIZ, sizeof(*el->el_line.buffer)); if (el->el_line.buffer == NULL) return -1; el->el_line.cursor = el->el_line.buffer; el->el_line.lastchar = el->el_line.buffer; el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE]; el->el_chared.c_undo.buf = el_calloc(EL_BUFSIZ, sizeof(*el->el_chared.c_undo.buf)); if (el->el_chared.c_undo.buf == NULL) return -1; el->el_chared.c_undo.len = -1; el->el_chared.c_undo.cursor = 0; el->el_chared.c_redo.buf = el_calloc(EL_BUFSIZ, sizeof(*el->el_chared.c_redo.buf)); if (el->el_chared.c_redo.buf == NULL) goto out; el->el_chared.c_redo.pos = el->el_chared.c_redo.buf; el->el_chared.c_redo.lim = el->el_chared.c_redo.buf + EL_BUFSIZ; el->el_chared.c_redo.cmd = ED_UNASSIGNED; el->el_chared.c_vcmd.action = NOP; el->el_chared.c_vcmd.pos = el->el_line.buffer; el->el_chared.c_kill.buf = el_calloc(EL_BUFSIZ, sizeof(*el->el_chared.c_kill.buf)); if (el->el_chared.c_kill.buf == NULL) goto out; el->el_chared.c_kill.mark = el->el_line.buffer; el->el_chared.c_kill.last = el->el_chared.c_kill.buf; el->el_chared.c_resizefun = NULL; el->el_chared.c_resizearg = NULL; el->el_chared.c_aliasfun = NULL; el->el_chared.c_aliasarg = NULL; el->el_map.current = el->el_map.key; el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ el->el_state.doingarg = 0; el->el_state.metanext = 0; el->el_state.argument = 1; el->el_state.lastcmd = ED_UNASSIGNED; return 0; out: ch_end(el); return -1; } /* ch_reset(): * Reset the character editor */ libedit_private void ch_reset(EditLine *el) { el->el_line.cursor = el->el_line.buffer; el->el_line.lastchar = el->el_line.buffer; el->el_chared.c_undo.len = -1; el->el_chared.c_undo.cursor = 0; el->el_chared.c_vcmd.action = NOP; el->el_chared.c_vcmd.pos = el->el_line.buffer; el->el_chared.c_kill.mark = el->el_line.buffer; el->el_map.current = el->el_map.key; el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ el->el_state.doingarg = 0; el->el_state.metanext = 0; el->el_state.argument = 1; el->el_state.lastcmd = ED_UNASSIGNED; el->el_history.eventno = 0; } /* ch_enlargebufs(): * Enlarge line buffer to be able to hold twice as much characters. * Returns 1 if successful, 0 if not. */ libedit_private int ch_enlargebufs(EditLine *el, size_t addlen) { size_t sz, newsz; wchar_t *newbuffer, *oldbuf, *oldkbuf; sz = (size_t)(el->el_line.limit - el->el_line.buffer + EL_LEAVE); newsz = sz * 2; /* * If newly required length is longer than current buffer, we need * to make the buffer big enough to hold both old and new stuff. */ if (addlen > sz) { while(newsz - sz < addlen) newsz *= 2; } /* * Reallocate line buffer. */ newbuffer = el_realloc(el->el_line.buffer, newsz * sizeof(*newbuffer)); if (!newbuffer) return 0; /* zero the newly added memory, leave old data in */ (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); oldbuf = el->el_line.buffer; el->el_line.buffer = newbuffer; el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf); el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf); /* don't set new size until all buffers are enlarged */ el->el_line.limit = &newbuffer[sz - EL_LEAVE]; /* * Reallocate kill buffer. */ newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz * sizeof(*newbuffer)); if (!newbuffer) return 0; /* zero the newly added memory, leave old data in */ (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); oldkbuf = el->el_chared.c_kill.buf; el->el_chared.c_kill.buf = newbuffer; el->el_chared.c_kill.last = newbuffer + (el->el_chared.c_kill.last - oldkbuf); el->el_chared.c_kill.mark = el->el_line.buffer + (el->el_chared.c_kill.mark - oldbuf); /* * Reallocate undo buffer. */ newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz * sizeof(*newbuffer)); if (!newbuffer) return 0; /* zero the newly added memory, leave old data in */ (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); el->el_chared.c_undo.buf = newbuffer; newbuffer = el_realloc(el->el_chared.c_redo.buf, newsz * sizeof(*newbuffer)); if (!newbuffer) return 0; el->el_chared.c_redo.pos = newbuffer + (el->el_chared.c_redo.pos - el->el_chared.c_redo.buf); el->el_chared.c_redo.lim = newbuffer + (el->el_chared.c_redo.lim - el->el_chared.c_redo.buf); el->el_chared.c_redo.buf = newbuffer; if (!hist_enlargebuf(el, sz, newsz)) return 0; /* Safe to set enlarged buffer size */ el->el_line.limit = &el->el_line.buffer[newsz - EL_LEAVE]; if (el->el_chared.c_resizefun) (*el->el_chared.c_resizefun)(el, el->el_chared.c_resizearg); return 1; } /* ch_end(): * Free the data structures used by the editor */ libedit_private void ch_end(EditLine *el) { el_free(el->el_line.buffer); el->el_line.buffer = NULL; el->el_line.limit = NULL; el_free(el->el_chared.c_undo.buf); el->el_chared.c_undo.buf = NULL; el_free(el->el_chared.c_redo.buf); el->el_chared.c_redo.buf = NULL; el->el_chared.c_redo.pos = NULL; el->el_chared.c_redo.lim = NULL; el->el_chared.c_redo.cmd = ED_UNASSIGNED; el_free(el->el_chared.c_kill.buf); el->el_chared.c_kill.buf = NULL; ch_reset(el); } /* el_insertstr(): * Insert string at cursor */ int el_winsertstr(EditLine *el, const wchar_t *s) { size_t len; if (s == NULL || (len = wcslen(s)) == 0) return -1; if (el->el_line.lastchar + len >= el->el_line.limit) { if (!ch_enlargebufs(el, len)) return -1; } c_insert(el, (int)len); while (*s) *el->el_line.cursor++ = *s++; return 0; } /* el_deletestr(): * Delete num characters before the cursor */ void el_deletestr(EditLine *el, int n) { if (n <= 0) return; if (el->el_line.cursor < &el->el_line.buffer[n]) return; c_delbefore(el, n); /* delete before dot */ el->el_line.cursor -= n; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; } /* el_deletestr1(): * Delete characters between start and end */ int el_deletestr1(EditLine *el, int start, int end) { size_t line_length, len; wchar_t *p1, *p2; if (end <= start) return 0; line_length = (size_t)(el->el_line.lastchar - el->el_line.buffer); if (start >= (int)line_length || end >= (int)line_length) return 0; len = (size_t)(end - start); if (len > line_length - (size_t)end) len = line_length - (size_t)end; p1 = el->el_line.buffer + start; p2 = el->el_line.buffer + end; for (size_t i = 0; i < len; i++) { *p1++ = *p2++; el->el_line.lastchar--; } if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; return end - start; } /* el_wreplacestr(): * Replace the contents of the line with the provided string */ int el_wreplacestr(EditLine *el, const wchar_t *s) { size_t len; wchar_t * p; if (s == NULL || (len = wcslen(s)) == 0) return -1; if (el->el_line.buffer + len >= el->el_line.limit) { if (!ch_enlargebufs(el, len)) return -1; } p = el->el_line.buffer; for (size_t i = 0; i < len; i++) *p++ = *s++; el->el_line.buffer[len] = '\0'; el->el_line.lastchar = el->el_line.buffer + len; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; return 0; } /* el_cursor(): * Move the cursor to the left or the right of the current position */ int el_cursor(EditLine *el, int n) { if (n == 0) goto out; el->el_line.cursor += n; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; out: return (int)(el->el_line.cursor - el->el_line.buffer); } /* c_gets(): * Get a string */ libedit_private int c_gets(EditLine *el, wchar_t *buf, const wchar_t *prompt) { ssize_t len; wchar_t *cp = el->el_line.buffer, ch; if (prompt) { len = (ssize_t)wcslen(prompt); (void)memcpy(cp, prompt, (size_t)len * sizeof(*cp)); cp += len; } len = 0; for (;;) { el->el_line.cursor = cp; *cp = ' '; el->el_line.lastchar = cp + 1; re_refresh(el); if (el_wgetc(el, &ch) != 1) { ed_end_of_file(el, 0); len = -1; break; } switch (ch) { case L'\b': /* Delete and backspace */ case 0177: if (len == 0) { len = -1; break; } len--; cp--; continue; case 0033: /* ESC */ case L'\r': /* Newline */ case L'\n': buf[len] = ch; break; default: if (len >= (ssize_t)(EL_BUFSIZ - 16)) terminal_beep(el); else { buf[len++] = ch; *cp++ = ch; } continue; } break; } el->el_line.buffer[0] = '\0'; el->el_line.lastchar = el->el_line.buffer; el->el_line.cursor = el->el_line.buffer; return (int)len; } /* c_hpos(): * Return the current horizontal position of the cursor */ libedit_private int c_hpos(EditLine *el) { wchar_t *ptr; /* * Find how many characters till the beginning of this line. */ if (el->el_line.cursor == el->el_line.buffer) return 0; else { for (ptr = el->el_line.cursor - 1; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--) continue; return (int)(el->el_line.cursor - ptr - 1); } } libedit_private int ch_resizefun(EditLine *el, el_zfunc_t f, void *a) { el->el_chared.c_resizefun = f; el->el_chared.c_resizearg = a; return 0; } libedit_private int ch_aliasfun(EditLine *el, el_afunc_t f, void *a) { el->el_chared.c_aliasfun = f; el->el_chared.c_aliasarg = a; return 0; } diff --git a/contrib/libedit/chartype.c b/contrib/libedit/chartype.c index 9c74cfeb677a..b731d63bafb5 100644 --- a/contrib/libedit/chartype.c +++ b/contrib/libedit/chartype.c @@ -1,341 +1,341 @@ -/* $NetBSD: chartype.c,v 1.36 2022/10/30 19:11:31 christos Exp $ */ +/* $NetBSD: chartype.c,v 1.37 2023/08/10 20:38:00 mrg Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * chartype.c: character classification and meta information */ #include "config.h" #if !defined(lint) && !defined(SCCSID) -__RCSID("$NetBSD: chartype.c,v 1.36 2022/10/30 19:11:31 christos Exp $"); +__RCSID("$NetBSD: chartype.c,v 1.37 2023/08/10 20:38:00 mrg Exp $"); #endif /* not lint && not SCCSID */ #include #include #include #include #include "el.h" #define CT_BUFSIZ ((size_t)1024) static int ct_conv_cbuff_resize(ct_buffer_t *, size_t); static int ct_conv_wbuff_resize(ct_buffer_t *, size_t); static int ct_conv_cbuff_resize(ct_buffer_t *conv, size_t csize) { void *p; if (csize <= conv->csize) return 0; conv->csize = csize; p = el_realloc(conv->cbuff, conv->csize * sizeof(*conv->cbuff)); if (p == NULL) { conv->csize = 0; el_free(conv->cbuff); conv->cbuff = NULL; return -1; } conv->cbuff = p; return 0; } static int ct_conv_wbuff_resize(ct_buffer_t *conv, size_t wsize) { void *p; if (wsize <= conv->wsize) return 0; conv->wsize = wsize; p = el_realloc(conv->wbuff, conv->wsize * sizeof(*conv->wbuff)); if (p == NULL) { conv->wsize = 0; el_free(conv->wbuff); conv->wbuff = NULL; return -1; } conv->wbuff = p; return 0; } char * ct_encode_string(const wchar_t *s, ct_buffer_t *conv) { char *dst; ssize_t used; if (!s) return NULL; dst = conv->cbuff; for (;;) { used = (ssize_t)(dst - conv->cbuff); if ((conv->csize - (size_t)used) < 5) { if (ct_conv_cbuff_resize(conv, conv->csize + CT_BUFSIZ) == -1) return NULL; dst = conv->cbuff + used; } if (!*s) break; used = ct_encode_char(dst, (size_t)5, *s); if (used == -1) /* failed to encode, need more buffer space */ abort(); ++s; dst += used; } *dst = '\0'; return conv->cbuff; } wchar_t * ct_decode_string(const char *s, ct_buffer_t *conv) { size_t len; if (!s) return NULL; len = mbstowcs(NULL, s, (size_t)0); if (len == (size_t)-1) return NULL; if (conv->wsize < ++len) if (ct_conv_wbuff_resize(conv, len + CT_BUFSIZ) == -1) return NULL; mbstowcs(conv->wbuff, s, conv->wsize); return conv->wbuff; } libedit_private wchar_t ** ct_decode_argv(int argc, const char *argv[], ct_buffer_t *conv) { size_t bufspace; int i; wchar_t *p; wchar_t **wargv; ssize_t bytes; /* Make sure we have enough space in the conversion buffer to store all * the argv strings. */ for (i = 0, bufspace = 0; i < argc; ++i) bufspace += argv[i] ? strlen(argv[i]) + 1 : 0; if (conv->wsize < ++bufspace) if (ct_conv_wbuff_resize(conv, bufspace + CT_BUFSIZ) == -1) return NULL; wargv = el_calloc((size_t)(argc + 1), sizeof(*wargv)); if (wargv == NULL) return NULL; for (i = 0, p = conv->wbuff; i < argc; ++i) { if (!argv[i]) { /* don't pass null pointers to mbstowcs */ wargv[i] = NULL; continue; } else { wargv[i] = p; bytes = (ssize_t)mbstowcs(p, argv[i], bufspace); } if (bytes == -1) { el_free(wargv); return NULL; } else bytes++; /* include '\0' in the count */ bufspace -= (size_t)bytes; p += bytes; } wargv[i] = NULL; return wargv; } libedit_private size_t ct_enc_width(wchar_t c) { mbstate_t mbs; char buf[MB_LEN_MAX]; size_t size; memset(&mbs, 0, sizeof(mbs)); if ((size = wcrtomb(buf, c, &mbs)) == (size_t)-1) return 0; return size; } libedit_private ssize_t ct_encode_char(char *dst, size_t len, wchar_t c) { ssize_t l = 0; if (len < ct_enc_width(c)) return -1; l = wctomb(dst, c); if (l < 0) { wctomb(NULL, L'\0'); l = 0; } return l; } libedit_private const wchar_t * ct_visual_string(const wchar_t *s, ct_buffer_t *conv) { wchar_t *dst; ssize_t used; if (!s) return NULL; if (ct_conv_wbuff_resize(conv, CT_BUFSIZ) == -1) return NULL; used = 0; dst = conv->wbuff; while (*s) { used = ct_visual_char(dst, conv->wsize - (size_t)(dst - conv->wbuff), *s); if (used != -1) { ++s; dst += used; continue; } /* failed to encode, need more buffer space */ - used = dst - conv->wbuff; + uintptr_t sused = (uintptr_t)dst - (uintptr_t)conv->wbuff; if (ct_conv_wbuff_resize(conv, conv->wsize + CT_BUFSIZ) == -1) return NULL; - dst = conv->wbuff + used; + dst = conv->wbuff + sused; } if (dst >= (conv->wbuff + conv->wsize)) { /* sigh */ - used = dst - conv->wbuff; + uintptr_t sused = (uintptr_t)dst - (uintptr_t)conv->wbuff; if (ct_conv_wbuff_resize(conv, conv->wsize + CT_BUFSIZ) == -1) return NULL; - dst = conv->wbuff + used; + dst = conv->wbuff + sused; } *dst = L'\0'; return conv->wbuff; } libedit_private int ct_visual_width(wchar_t c) { int t = ct_chr_class(c); switch (t) { case CHTYPE_ASCIICTL: return 2; /* ^@ ^? etc. */ case CHTYPE_TAB: return 1; /* Hmm, this really need to be handled outside! */ case CHTYPE_NL: return 0; /* Should this be 1 instead? */ case CHTYPE_PRINT: return wcwidth(c); case CHTYPE_NONPRINT: if (c > 0xffff) /* prefer standard 4-byte display over 5-byte */ return 8; /* \U+12345 */ else return 7; /* \U+1234 */ default: return 0; /* should not happen */ } } libedit_private ssize_t ct_visual_char(wchar_t *dst, size_t len, wchar_t c) { int t = ct_chr_class(c); switch (t) { case CHTYPE_TAB: case CHTYPE_NL: case CHTYPE_ASCIICTL: if (len < 2) return -1; /* insufficient space */ *dst++ = '^'; if (c == '\177') *dst = '?'; /* DEL -> ^? */ else *dst = c | 0100; /* uncontrolify it */ return 2; case CHTYPE_PRINT: if (len < 1) return -1; /* insufficient space */ *dst = c; return 1; case CHTYPE_NONPRINT: /* we only use single-width glyphs for display, * so this is right */ if ((ssize_t)len < ct_visual_width(c)) return -1; /* insufficient space */ *dst++ = '\\'; *dst++ = 'U'; *dst++ = '+'; #define tohexdigit(v) "0123456789ABCDEF"[v] if (c > 0xffff) /* prefer standard 4-byte display over 5-byte */ *dst++ = tohexdigit(((unsigned int) c >> 16) & 0xf); *dst++ = tohexdigit(((unsigned int) c >> 12) & 0xf); *dst++ = tohexdigit(((unsigned int) c >> 8) & 0xf); *dst++ = tohexdigit(((unsigned int) c >> 4) & 0xf); *dst = tohexdigit(((unsigned int) c ) & 0xf); return c > 0xffff ? 8 : 7; /*FALLTHROUGH*/ /* these two should be handled outside this function */ default: /* we should never hit the default */ return 0; } } libedit_private int ct_chr_class(wchar_t c) { if (c == '\t') return CHTYPE_TAB; else if (c == '\n') return CHTYPE_NL; else if (c < 0x100 && iswcntrl(c)) return CHTYPE_ASCIICTL; else if (iswprint(c)) return CHTYPE_PRINT; else return CHTYPE_NONPRINT; } diff --git a/contrib/libedit/common.c b/contrib/libedit/common.c index f33c29650d79..e038956464c4 100644 --- a/contrib/libedit/common.c +++ b/contrib/libedit/common.c @@ -1,837 +1,838 @@ -/* $NetBSD: common.c,v 1.49 2020/03/30 06:54:37 ryo Exp $ */ +/* $NetBSD: common.c,v 1.50 2024/06/30 16:29:42 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)common.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: common.c,v 1.49 2020/03/30 06:54:37 ryo Exp $"); +__RCSID("$NetBSD: common.c,v 1.50 2024/06/30 16:29:42 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * common.c: Common Editor functions */ #include #include #include "el.h" #include "common.h" #include "fcns.h" #include "parse.h" #include "vi.h" /* ed_end_of_file(): * Indicate end of file * [^D] */ libedit_private el_action_t /*ARGSUSED*/ ed_end_of_file(EditLine *el, wint_t c __attribute__((__unused__))) { re_goto_bottom(el); *el->el_line.lastchar = '\0'; return CC_EOF; } /* ed_insert(): * Add character to the line * Insert a character [bound to all insert keys] */ libedit_private el_action_t ed_insert(EditLine *el, wint_t c) { int count = el->el_state.argument; if (c == '\0') return CC_ERROR; if (el->el_line.lastchar + el->el_state.argument >= el->el_line.limit) { /* end of buffer space, try to allocate more */ if (!ch_enlargebufs(el, (size_t) count)) return CC_ERROR; /* error allocating more */ } if (count == 1) { if (el->el_state.inputmode == MODE_INSERT || el->el_line.cursor >= el->el_line.lastchar) c_insert(el, 1); *el->el_line.cursor++ = c; re_fastaddc(el); /* fast refresh for one char. */ } else { if (el->el_state.inputmode != MODE_REPLACE_1) c_insert(el, el->el_state.argument); while (count-- && el->el_line.cursor < el->el_line.lastchar) *el->el_line.cursor++ = c; re_refresh(el); } if (el->el_state.inputmode == MODE_REPLACE_1) return vi_command_mode(el, 0); return CC_NORM; } /* ed_delete_prev_word(): * Delete from beginning of current word to cursor * [M-^?] [^W] */ libedit_private el_action_t /*ARGSUSED*/ ed_delete_prev_word(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *cp, *p, *kp; if (el->el_line.cursor == el->el_line.buffer) return CC_ERROR; cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, el->el_state.argument, ce__isword); for (p = cp, kp = el->el_chared.c_kill.buf; p < el->el_line.cursor; p++) *kp++ = *p; el->el_chared.c_kill.last = kp; c_delbefore(el, (int)(el->el_line.cursor - cp));/* delete before dot */ el->el_line.cursor = cp; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; /* bounds check */ return CC_REFRESH; } /* ed_delete_next_char(): * Delete character under cursor * [^D] [x] */ libedit_private el_action_t /*ARGSUSED*/ ed_delete_next_char(EditLine *el, wint_t c __attribute__((__unused__))) { #ifdef DEBUG_EDIT #define EL el->el_line (void) fprintf(el->el_errfile, "\nD(b: %p(%ls) c: %p(%ls) last: %p(%ls) limit: %p(%ls)\n", EL.buffer, EL.buffer, EL.cursor, EL.cursor, EL.lastchar, EL.lastchar, EL.limit, EL.limit); #endif if (el->el_line.cursor == el->el_line.lastchar) { /* if I'm at the end */ if (el->el_map.type == MAP_VI) { if (el->el_line.cursor == el->el_line.buffer) { /* if I'm also at the beginning */ #ifdef KSHVI return CC_ERROR; #else /* then do an EOF */ terminal_writec(el, c); return CC_EOF; #endif } else { #ifdef KSHVI el->el_line.cursor--; #else return CC_ERROR; #endif } } else return CC_ERROR; } c_delafter(el, el->el_state.argument); /* delete after dot */ if (el->el_map.type == MAP_VI && el->el_line.cursor >= el->el_line.lastchar && el->el_line.cursor > el->el_line.buffer) /* bounds check */ el->el_line.cursor = el->el_line.lastchar - 1; return CC_REFRESH; } /* ed_kill_line(): * Cut to the end of line * [^K] [^K] */ libedit_private el_action_t /*ARGSUSED*/ ed_kill_line(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *kp, *cp; cp = el->el_line.cursor; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.lastchar) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; /* zap! -- delete to end */ el->el_line.lastchar = el->el_line.cursor; return CC_REFRESH; } /* ed_move_to_end(): * Move cursor to the end of line * [^E] [^E] */ libedit_private el_action_t /*ARGSUSED*/ ed_move_to_end(EditLine *el, wint_t c __attribute__((__unused__))) { el->el_line.cursor = el->el_line.lastchar; if (el->el_map.type == MAP_VI) { if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return CC_REFRESH; } #ifdef VI_MOVE - el->el_line.cursor--; + if (el->el_line.cursor > el->el_line.buffer) + el->el_line.cursor--; #endif } return CC_CURSOR; } /* ed_move_to_beg(): * Move cursor to the beginning of line * [^A] [^A] */ libedit_private el_action_t /*ARGSUSED*/ ed_move_to_beg(EditLine *el, wint_t c __attribute__((__unused__))) { el->el_line.cursor = el->el_line.buffer; if (el->el_map.type == MAP_VI) { /* We want FIRST non space character */ while (iswspace(*el->el_line.cursor)) el->el_line.cursor++; if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return CC_REFRESH; } } return CC_CURSOR; } /* ed_transpose_chars(): * Exchange the character to the left of the cursor with the one under it * [^T] [^T] */ libedit_private el_action_t ed_transpose_chars(EditLine *el, wint_t c) { if (el->el_line.cursor < el->el_line.lastchar) { if (el->el_line.lastchar <= &el->el_line.buffer[1]) return CC_ERROR; else el->el_line.cursor++; } if (el->el_line.cursor > &el->el_line.buffer[1]) { /* must have at least two chars entered */ c = el->el_line.cursor[-2]; el->el_line.cursor[-2] = el->el_line.cursor[-1]; el->el_line.cursor[-1] = c; return CC_REFRESH; } else return CC_ERROR; } /* ed_next_char(): * Move to the right one character * [^F] [^F] */ libedit_private el_action_t /*ARGSUSED*/ ed_next_char(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *lim = el->el_line.lastchar; if (el->el_line.cursor >= lim || (el->el_line.cursor == lim - 1 && el->el_map.type == MAP_VI && el->el_chared.c_vcmd.action == NOP)) return CC_ERROR; el->el_line.cursor += el->el_state.argument; if (el->el_line.cursor > lim) el->el_line.cursor = lim; if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return CC_REFRESH; } return CC_CURSOR; } /* ed_prev_word(): * Move to the beginning of the current word * [M-b] [b] */ libedit_private el_action_t /*ARGSUSED*/ ed_prev_word(EditLine *el, wint_t c __attribute__((__unused__))) { if (el->el_line.cursor == el->el_line.buffer) return CC_ERROR; el->el_line.cursor = c__prev_word(el->el_line.cursor, el->el_line.buffer, el->el_state.argument, ce__isword); if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return CC_REFRESH; } return CC_CURSOR; } /* ed_prev_char(): * Move to the left one character * [^B] [^B] */ libedit_private el_action_t /*ARGSUSED*/ ed_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) { if (el->el_line.cursor > el->el_line.buffer) { el->el_line.cursor -= el->el_state.argument; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return CC_REFRESH; } return CC_CURSOR; } else return CC_ERROR; } /* ed_quoted_insert(): * Add the next character typed verbatim * [^V] [^V] */ libedit_private el_action_t /*ARGSUSED*/ ed_quoted_insert(EditLine *el, wint_t c __attribute__((__unused__))) { int num; wchar_t ch; tty_quotemode(el); num = el_wgetc(el, &ch); tty_noquotemode(el); if (num == 1) return ed_insert(el, ch); else return ed_end_of_file(el, 0); } /* ed_digit(): * Adds to argument or enters a digit */ libedit_private el_action_t ed_digit(EditLine *el, wint_t c) { if (!iswdigit(c)) return CC_ERROR; if (el->el_state.doingarg) { /* if doing an arg, add this in... */ if (el->el_state.lastcmd == EM_UNIVERSAL_ARGUMENT) el->el_state.argument = c - '0'; else { if (el->el_state.argument > 1000000) return CC_ERROR; el->el_state.argument = (el->el_state.argument * 10) + (c - '0'); } return CC_ARGHACK; } return ed_insert(el, c); } /* ed_argument_digit(): * Digit that starts argument * For ESC-n */ libedit_private el_action_t ed_argument_digit(EditLine *el, wint_t c) { if (!iswdigit(c)) return CC_ERROR; if (el->el_state.doingarg) { if (el->el_state.argument > 1000000) return CC_ERROR; el->el_state.argument = (el->el_state.argument * 10) + (c - '0'); } else { /* else starting an argument */ el->el_state.argument = c - '0'; el->el_state.doingarg = 1; } return CC_ARGHACK; } /* ed_unassigned(): * Indicates unbound character * Bound to keys that are not assigned */ libedit_private el_action_t /*ARGSUSED*/ ed_unassigned(EditLine *el __attribute__((__unused__)), wint_t c __attribute__((__unused__))) { return CC_ERROR; } /* ed_ignore(): * Input characters that have no effect * [^C ^O ^Q ^S ^Z ^\ ^]] [^C ^O ^Q ^S ^\] */ libedit_private el_action_t /*ARGSUSED*/ ed_ignore(EditLine *el __attribute__((__unused__)), wint_t c __attribute__((__unused__))) { return CC_NORM; } /* ed_newline(): * Execute command * [^J] */ libedit_private el_action_t /*ARGSUSED*/ ed_newline(EditLine *el, wint_t c __attribute__((__unused__))) { re_goto_bottom(el); *el->el_line.lastchar++ = '\n'; *el->el_line.lastchar = '\0'; return CC_NEWLINE; } /* ed_delete_prev_char(): * Delete the character to the left of the cursor * [^?] */ libedit_private el_action_t /*ARGSUSED*/ ed_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) { if (el->el_line.cursor <= el->el_line.buffer) return CC_ERROR; c_delbefore(el, el->el_state.argument); el->el_line.cursor -= el->el_state.argument; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; return CC_REFRESH; } /* ed_clear_screen(): * Clear screen leaving current line at the top * [^L] */ libedit_private el_action_t /*ARGSUSED*/ ed_clear_screen(EditLine *el, wint_t c __attribute__((__unused__))) { terminal_clear_screen(el); /* clear the whole real screen */ re_clear_display(el); /* reset everything */ return CC_REFRESH; } /* ed_redisplay(): * Redisplay everything * ^R */ libedit_private el_action_t /*ARGSUSED*/ ed_redisplay(EditLine *el __attribute__((__unused__)), wint_t c __attribute__((__unused__))) { return CC_REDISPLAY; } /* ed_start_over(): * Erase current line and start from scratch * [^G] */ libedit_private el_action_t /*ARGSUSED*/ ed_start_over(EditLine *el, wint_t c __attribute__((__unused__))) { ch_reset(el); return CC_REFRESH; } /* ed_sequence_lead_in(): * First character in a bound sequence * Placeholder for external keys */ libedit_private el_action_t /*ARGSUSED*/ ed_sequence_lead_in(EditLine *el __attribute__((__unused__)), wint_t c __attribute__((__unused__))) { return CC_NORM; } /* ed_prev_history(): * Move to the previous history line * [^P] [k] */ libedit_private el_action_t /*ARGSUSED*/ ed_prev_history(EditLine *el, wint_t c __attribute__((__unused__))) { char beep = 0; int sv_event = el->el_history.eventno; el->el_chared.c_undo.len = -1; *el->el_line.lastchar = '\0'; /* just in case */ if (el->el_history.eventno == 0) { /* save the current buffer * away */ (void) wcsncpy(el->el_history.buf, el->el_line.buffer, EL_BUFSIZ); el->el_history.last = el->el_history.buf + (el->el_line.lastchar - el->el_line.buffer); } el->el_history.eventno += el->el_state.argument; if (hist_get(el) == CC_ERROR) { if (el->el_map.type == MAP_VI) { el->el_history.eventno = sv_event; } beep = 1; /* el->el_history.eventno was fixed by first call */ (void) hist_get(el); } if (beep) return CC_REFRESH_BEEP; return CC_REFRESH; } /* ed_next_history(): * Move to the next history line * [^N] [j] */ libedit_private el_action_t /*ARGSUSED*/ ed_next_history(EditLine *el, wint_t c __attribute__((__unused__))) { el_action_t beep = CC_REFRESH, rval; el->el_chared.c_undo.len = -1; *el->el_line.lastchar = '\0'; /* just in case */ el->el_history.eventno -= el->el_state.argument; if (el->el_history.eventno < 0) { el->el_history.eventno = 0; beep = CC_REFRESH_BEEP; } rval = hist_get(el); if (rval == CC_REFRESH) return beep; return rval; } /* ed_search_prev_history(): * Search previous in history for a line matching the current * next search history [M-P] [K] */ libedit_private el_action_t /*ARGSUSED*/ ed_search_prev_history(EditLine *el, wint_t c __attribute__((__unused__))) { const wchar_t *hp; int h; int found = 0; el->el_chared.c_vcmd.action = NOP; el->el_chared.c_undo.len = -1; *el->el_line.lastchar = '\0'; /* just in case */ if (el->el_history.eventno < 0) { #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, "e_prev_search_hist(): eventno < 0;\n"); #endif el->el_history.eventno = 0; return CC_ERROR; } if (el->el_history.eventno == 0) { (void) wcsncpy(el->el_history.buf, el->el_line.buffer, EL_BUFSIZ); el->el_history.last = el->el_history.buf + (el->el_line.lastchar - el->el_line.buffer); } if (el->el_history.ref == NULL) return CC_ERROR; hp = HIST_FIRST(el); if (hp == NULL) return CC_ERROR; c_setpat(el); /* Set search pattern !! */ for (h = 1; h <= el->el_history.eventno; h++) hp = HIST_NEXT(el); while (hp != NULL) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "Comparing with \"%ls\"\n", hp); #endif if ((wcsncmp(hp, el->el_line.buffer, (size_t) (el->el_line.lastchar - el->el_line.buffer)) || hp[el->el_line.lastchar - el->el_line.buffer]) && c_hmatch(el, hp)) { found = 1; break; } h++; hp = HIST_NEXT(el); } if (!found) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "not found\n"); #endif return CC_ERROR; } el->el_history.eventno = h; return hist_get(el); } /* ed_search_next_history(): * Search next in history for a line matching the current * [M-N] [J] */ libedit_private el_action_t /*ARGSUSED*/ ed_search_next_history(EditLine *el, wint_t c __attribute__((__unused__))) { const wchar_t *hp; int h; int found = 0; el->el_chared.c_vcmd.action = NOP; el->el_chared.c_undo.len = -1; *el->el_line.lastchar = '\0'; /* just in case */ if (el->el_history.eventno == 0) return CC_ERROR; if (el->el_history.ref == NULL) return CC_ERROR; hp = HIST_FIRST(el); if (hp == NULL) return CC_ERROR; c_setpat(el); /* Set search pattern !! */ for (h = 1; h < el->el_history.eventno && hp; h++) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "Comparing with \"%ls\"\n", hp); #endif if ((wcsncmp(hp, el->el_line.buffer, (size_t) (el->el_line.lastchar - el->el_line.buffer)) || hp[el->el_line.lastchar - el->el_line.buffer]) && c_hmatch(el, hp)) found = h; hp = HIST_NEXT(el); } if (!found) { /* is it the current history number? */ if (!c_hmatch(el, el->el_history.buf)) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "not found\n"); #endif return CC_ERROR; } } el->el_history.eventno = found; return hist_get(el); } /* ed_prev_line(): * Move up one line * Could be [k] [^p] */ libedit_private el_action_t /*ARGSUSED*/ ed_prev_line(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *ptr; int nchars = c_hpos(el); /* * Move to the line requested */ if (*(ptr = el->el_line.cursor) == '\n') ptr--; for (; ptr >= el->el_line.buffer; ptr--) if (*ptr == '\n' && --el->el_state.argument <= 0) break; if (el->el_state.argument > 0) return CC_ERROR; /* * Move to the beginning of the line */ for (ptr--; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--) continue; /* * Move to the character requested */ for (ptr++; nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n'; ptr++) continue; el->el_line.cursor = ptr; return CC_CURSOR; } /* ed_next_line(): * Move down one line * Could be [j] [^n] */ libedit_private el_action_t /*ARGSUSED*/ ed_next_line(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *ptr; int nchars = c_hpos(el); /* * Move to the line requested */ for (ptr = el->el_line.cursor; ptr < el->el_line.lastchar; ptr++) if (*ptr == '\n' && --el->el_state.argument <= 0) break; if (el->el_state.argument > 0) return CC_ERROR; /* * Move to the character requested */ for (ptr++; nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n'; ptr++) continue; el->el_line.cursor = ptr; return CC_CURSOR; } /* ed_command(): * Editline extended command * [M-X] [:] */ libedit_private el_action_t /*ARGSUSED*/ ed_command(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t tmpbuf[EL_BUFSIZ]; int tmplen; tmplen = c_gets(el, tmpbuf, L"\n: "); terminal__putc(el, '\n'); if (tmplen < 0 || (tmpbuf[tmplen] = 0, parse_line(el, tmpbuf)) == -1) terminal_beep(el); el->el_map.current = el->el_map.key; re_clear_display(el); return CC_REFRESH; } diff --git a/contrib/libedit/edit.expsym b/contrib/libedit/edit.expsym new file mode 100644 index 000000000000..c5d021b31a65 --- /dev/null +++ b/contrib/libedit/edit.expsym @@ -0,0 +1,204 @@ +_el_fn_complete +_el_fn_sh_complete +_rl_abort_internal +_rl_complete_mark_directories +_rl_completion_prefix_display_length +_rl_echoing_p +_rl_erase_entire_line +_rl_print_completions_horizontally +_rl_qsort_string_compare +add_history +append_history +clear_history +completion_matches +ct_decode_string +ct_encode_string +current_history +el_beep +el_cursor +el_deletestr +el_deletestr1 +el_end +el_get +el_getc +el_gets +el_init +el_init_fd +el_insertstr +el_line +el_parse +el_push +el_replacestr +el_reset +el_resize +el_set +el_source +el_wget +el_wgetc +el_wgets +el_winsertstr +el_wline +el_wparse +el_wpush +el_wreplacestr +el_wset +emacs_ctlx_keymap +emacs_meta_keymap +emacs_standard_keymap +filename_completion_function +fn_complete +fn_complete2 +fn_display_match_list +fn_filename_completion_function +fn_tilde_expand +free_history_entry +get_history_event +history +history_arg_extract +history_base +history_end +history_expand +history_expansion_char +history_get +history_get_history_state +history_inhibit_expansion_function +history_init +history_is_stifled +history_length +history_list +history_max_entries +history_no_expand_chars +history_offset +history_search +history_search_pos +history_search_prefix +history_set_pos +history_subst_char +history_tokenize +history_total_bytes +history_truncate_file +history_w +history_wend +history_winit +max_input_history +next_history +previous_history +read_history +readline +readline_echoing_p +remove_history +replace_history_entry +rl_abort +rl_add_defun +rl_already_prompted +rl_attempted_completion_function +rl_attempted_completion_over +rl_basic_quote_characters +rl_basic_word_break_characters +rl_bind_key +rl_bind_key_in_map +rl_callback_handler_install +rl_callback_handler_remove +rl_callback_read_char +rl_catch_signals +rl_catch_sigwinch +rl_cleanup_after_signal +rl_complete +rl_completer_quote_characters +rl_completer_word_break_characters +rl_completion_append_character +rl_completion_display_matches_hook +rl_completion_entry_function +rl_completion_matches +rl_completion_query_items +rl_completion_suppress_append +rl_completion_type +rl_completion_word_break_hook +rl_copy_text +rl_crlf +rl_delete_text +rl_deprep_term_function +rl_deprep_terminal +rl_ding +rl_directory_completion_hook +rl_display_match_list +rl_display_prompt +rl_done +rl_echo_signal_char +rl_end +rl_erase_empty_line +rl_event_hook +rl_filename_completion_desired +rl_filename_completion_function +rl_forced_update_display +rl_free_line_state +rl_generic_bind +rl_get_keymap +rl_get_previous_history +rl_get_screen_size +rl_getc_function +rl_ignore_completion_duplicates +rl_inhibit_completion +rl_initialize +rl_insert +rl_insert_text +rl_instream +rl_kill_text +rl_library_version +rl_line_buffer +rl_linefunc +rl_make_bare_keymap +rl_message +rl_newline +rl_on_new_line +rl_outstream +rl_parse_and_bind +rl_point +rl_pre_input_hook +rl_prep_term_function +rl_prep_terminal +rl_prompt +rl_prompt_saved +rl_read_init_file +rl_read_key +rl_readline_name +rl_readline_state +rl_readline_version +rl_redisplay +rl_redisplay_function +rl_replace_line +rl_reset_after_signal +rl_reset_terminal +rl_resize_terminal +rl_restore_prompt +rl_save_prompt +rl_set_key +rl_set_keyboard_input_timeout +rl_set_keymap +rl_set_keymap_name +rl_set_prompt +rl_set_screen_size +rl_sort_completion_matches +rl_special_prefixes +rl_startup1_hook +rl_startup_hook +rl_stuff_char +rl_terminal_name +rl_variable_bind +stifle_history +tilde_expand +tok_end +tok_init +tok_line +tok_reset +tok_str +tok_wend +tok_winit +tok_wline +tok_wreset +tok_wstr +unstifle_history +username_completion_function +using_history +where_history +write_history diff --git a/contrib/libedit/editline.3 b/contrib/libedit/editline.3 index f104dcc07c7a..465af0e655df 100644 --- a/contrib/libedit/editline.3 +++ b/contrib/libedit/editline.3 @@ -1,1020 +1,1020 @@ -.\" $NetBSD: editline.3,v 1.101 2021/08/15 10:12:54 wiz Exp $ +.\" $NetBSD: editline.3,v 1.102 2024/02/04 18:47:27 andvar Exp $ .\" .\" Copyright (c) 1997-2014 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" This file was contributed to The NetBSD Foundation by Luke Mewburn. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED .\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR .\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS .\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd August 15, 2021 .Dt EDITLINE 3 .Os .Sh NAME .Nm editline , .Nm el_init , .Nm el_init_fd , .Nm el_end , .Nm el_reset , .Nm el_gets , .Nm el_wgets , .Nm el_getc , .Nm el_wgetc , .Nm el_push , .Nm el_wpush , .Nm el_parse , .Nm el_wparse , .Nm el_set , .Nm el_wset , .Nm el_get , .Nm el_wget , .Nm el_source , .Nm el_resize , .Nm el_cursor , .Nm el_line , .Nm el_wline , .Nm el_insertstr , .Nm el_winsertstr , .Nm el_deletestr , .Nm el_wdeletestr , .Nm history_init , .Nm history_winit , .Nm history_end , .Nm history_wend , .Nm history , .Nm history_w , .Nm tok_init , .Nm tok_winit , .Nm tok_end , .Nm tok_wend , .Nm tok_reset , .Nm tok_wreset , .Nm tok_line , .Nm tok_wline , .Nm tok_str , .Nm tok_wstr .Nd line editor, history and tokenization functions .Sh LIBRARY .Lb libedit .Sh SYNOPSIS .In histedit.h .Ft EditLine * .Fn el_init "const char *prog" "FILE *fin" "FILE *fout" "FILE *ferr" .Ft EditLine * .Fn el_init_fd "const char *prog" "FILE *fin" "FILE *fout" "FILE *ferr" "int fdin" "int fdout" "int fderr" .Ft void .Fn el_end "EditLine *e" .Ft void .Fn el_reset "EditLine *e" .Ft const char * .Fn el_gets "EditLine *e" "int *count" .Ft const wchar_t * .Fn el_wgets "EditLine *e" "int *count" .Ft int .Fn el_getc "EditLine *e" "char *ch" .Ft int .Fn el_wgetc "EditLine *e" "wchar_t *wc" .Ft void .Fn el_push "EditLine *e" "const char *mbs" .Ft void .Fn el_wpush "EditLine *e" "const wchar_t *wcs" .Ft int .Fn el_parse "EditLine *e" "int argc" "const char *argv[]" .Ft int .Fn el_wparse "EditLine *e" "int argc" "const wchar_t *argv[]" .Ft int .Fn el_set "EditLine *e" "int op" "..." .Ft int .Fn el_wset "EditLine *e" "int op" "..." .Ft int .Fn el_get "EditLine *e" "int op" "..." .Ft int .Fn el_wget "EditLine *e" "int op" "..." .Ft int .Fn el_source "EditLine *e" "const char *file" .Ft void .Fn el_resize "EditLine *e" .Ft int .Fn el_cursor "EditLine *e" "int count" .Ft const LineInfo * .Fn el_line "EditLine *e" .Ft const LineInfoW * .Fn el_wline "EditLine *e" .Ft int .Fn el_insertstr "EditLine *e" "const char *str" .Ft int .Fn el_winsertstr "EditLine *e" "const wchar_t *str" .Ft void .Fn el_deletestr "EditLine *e" "int count" .Ft void .Fn el_wdeletestr "EditLine *e" "int count" .Ft History * .Fn history_init void .Ft HistoryW * .Fn history_winit void .Ft void .Fn history_end "History *h" .Ft void .Fn history_wend "HistoryW *h" .Ft int .Fn history "History *h" "HistEvent *ev" "int op" "..." .Ft int .Fn history_w "HistoryW *h" "HistEventW *ev" "int op" "..." .Ft Tokenizer * .Fn tok_init "const char *IFS" .Ft TokenizerW * .Fn tok_winit "const wchar_t *IFS" .Ft void .Fn tok_end "Tokenizer *t" .Ft void .Fn tok_wend "TokenizerW *t" .Ft void .Fn tok_reset "Tokenizer *t" .Ft void .Fn tok_wreset "TokenizerW *t" .Ft int .Fn tok_line "Tokenizer *t" "const LineInfo *li" "int *argc" "const char **argv[]" "int *cursorc" "int *cursoro" .Ft int .Fn tok_wline "TokenizerW *t" "const LineInfoW *li" "int *argc" "const wchar_t **argv[]" "int *cursorc" "int *cursoro" .Ft int .Fn tok_str "Tokenizer *t" "const char *str" "int *argc" "const char **argv[]" .Ft int .Fn tok_wstr "TokenizerW *t" "const wchar_t *str" "int *argc" "const wchar_t **argv[]" .Sh DESCRIPTION The .Nm library provides generic line editing, history and tokenization functions, similar to those found in .Xr sh 1 . .Pp These functions are available in the .Nm libedit library (which needs the .Nm libtermcap library). Programs should be linked with .Fl ledit ltermcap . .Pp The .Nm library respects the .Ev LC_CTYPE locale set by the application program and never uses .Xr setlocale 3 to change the locale. .Sh LINE EDITING FUNCTIONS The line editing functions use a common data structure, .Fa EditLine , which is created by .Fn el_init or .Fn el_init_fd and freed by .Fn el_end . .Pp The wide-character functions behave the same way as their narrow counterparts. .Pp The following functions are available: .Bl -tag -width 4n .It Fn el_init Initialize the line editor, and return a data structure to be used by all other line editing functions, or .Dv NULL on failure. .Fa prog is the name of the invoking program, used when reading the .Xr editrc 5 file to determine which settings to use. .Fa fin , .Fa fout and .Fa ferr are the input, output, and error streams (respectively) to use. In this documentation, references to .Dq the tty are actually to this input/output stream combination. .It Fn el_init_fd Like .Fn el_init but allows specifying file descriptors for the .Xr stdio 3 corresponding streams, in case those were created with .Xr funopen 3 . .It Fn el_end Clean up and finish with .Fa e , assumed to have been created with .Fn el_init or .Fn el_init_fd . .It Fn el_reset Reset the tty and the parser. This should be called after an error which may have upset the tty's state. .It Fn el_gets Read a line from the tty. .Fa count is modified to contain the number of characters read. Returns the line read if successful, or .Dv NULL if no characters were read or if an error occurred. If an error occurred, .Fa count is set to \-1 and .Dv errno contains the error code that caused it. The return value may not remain valid across calls to .Fn el_gets and must be copied if the data is to be retained. .It Fn el_wgetc Read a wide character from the tty, respecting the current locale, or from the input queue described in .Xr editline 7 if that is not empty, and store it in .Fa wc . If an invalid or incomplete character is found, it is discarded, .Va errno is set to .Er EILSEQ , and the next character is read and stored in .Fa wc . Returns 1 if a valid character was read, 0 on end of file, or \-1 on .Xr read 2 failure. In the latter case, .Va errno is set to indicate the error. .It Fn el_getc Read a wide character as described for .Fn el_wgetc and return 0 on end of file or \-1 on failure. If the wide character can be represented as a single-byte character, convert it with .Xr wctob 3 , store the result in .Fa ch , and return 1; otherwise, set .Va errno to .Er ERANGE and return \-1. In the C or POSIX locale, this simply reads a byte, but for any other locale, including UTF-8, this is rarely useful. .It Fn el_wpush Push the wide character string .Fa wcs back onto the input queue described in .Xr editline 7 . If the queue overflows, for example due to a recursive macro, or if an error occurs, for example because .Fa wcs is .Dv NULL or memory allocation fails, the function beeps at the user, but does not report the problem to the caller. .It Fn el_push Use the current locale to convert the multibyte string .Fa mbs to a wide character string, and pass the result to .Fn el_wpush . .It Fn el_parse Parses the .Fa argv array (which is .Fa argc elements in size) to execute builtin .Nm commands. If the command is prefixed with .Dq prog : then .Fn el_parse will only execute the command if .Dq prog matches the .Fa prog argument supplied to .Fn el_init . The return value is \-1 if the command is unknown, 0 if there was no error or .Dq prog didn't match, or 1 if the command returned an error. Refer to .Xr editrc 5 for more information. .It Fn el_set Set .Nm parameters. .Fa op determines which parameter to set, and each operation has its own parameter list. Returns 0 on success, \-1 on failure. .Pp The following values for .Fa op are supported, along with the required argument list: .Bl -tag -width 4n .It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)" Define prompt printing function as .Fa f , which is to return a string that contains the prompt. .It Dv EL_PROMPT_ESC , Fa "char *(*f)(EditLine *)" , Fa "char c" Same as .Dv EL_PROMPT , but the .Fa c argument indicates the start/stop literal prompt character. .Pp If a start/stop literal character is found in the prompt, the character itself is not printed, but characters after it are printed directly to the terminal without affecting the state of the current line. A subsequent second start/stop literal character ends this behavior. This is typically used to embed literal escape sequences that change the color/style of the terminal in the prompt. Note that the literal escape character cannot be the last character in the prompt, as the escape sequence is attached to the next character in the prompt. .Dv 0 unsets it. .It Dv EL_REFRESH Re-display the current line on the next terminal line. .It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)" Define right side prompt printing function as .Fa f , which is to return a string that contains the prompt. .It Dv EL_RPROMPT_ESC , Fa "char *(*f)(EditLine *)" , Fa "char c" Define the right prompt printing function but with a literal escape character. .It Dv EL_TERMINAL , Fa "const char *type" Define terminal type of the tty to be .Fa type , or to .Ev TERM if .Fa type is .Dv NULL . .It Dv EL_EDITOR , Fa "const char *mode" Set editing mode to .Fa mode , which must be one of .Dq emacs or .Dq vi . .It Dv EL_SIGNAL , Fa "int flag" If .Fa flag is non-zero, .Nm will install its own signal handler for the following signals when reading command input: .Dv SIGCONT , .Dv SIGHUP , .Dv SIGINT , .Dv SIGQUIT , .Dv SIGSTOP , .Dv SIGTERM , .Dv SIGTSTP , and .Dv SIGWINCH . Otherwise, the current signal handlers will be used. .It Dv EL_BIND , Fa "const char *" , Fa "..." , Dv NULL Perform the .Ic bind builtin command. Refer to .Xr editrc 5 for more information. .It Dv EL_ECHOTC , Fa "const char *" , Fa "..." , Dv NULL Perform the .Ic echotc builtin command. Refer to .Xr editrc 5 for more information. .It Dv EL_SETTC , Fa "const char *" , Fa "..." , Dv NULL Perform the .Ic settc builtin command. Refer to .Xr editrc 5 for more information. .It Dv EL_SETTY , Fa "const char *" , Fa "..." , Dv NULL Perform the .Ic setty builtin command. Refer to .Xr editrc 5 for more information. .It Dv EL_TELLTC , Fa "const char *" , Fa "..." , Dv NULL Perform the .Ic telltc builtin command. Refer to .Xr editrc 5 for more information. .It Dv EL_ADDFN , Fa "const char *name" , Fa "const char *help" , \ Fa "unsigned char (*func)(EditLine *e, int ch)" Add a user defined function, .Fn func , referred to as .Fa name which is invoked when a key which is bound to .Fa name is entered. .Fa help is a description of .Fa name . At invocation time, .Fa ch is the key which caused the invocation. The return value of .Fn func should be one of: .Bl -tag -width "CC_REDISPLAY" .It Dv CC_NORM Add a normal character. .It Dv CC_NEWLINE End of line was entered. .It Dv CC_EOF EOF was entered. .It Dv CC_ARGHACK Expecting further command input as arguments, do nothing visually. .It Dv CC_REFRESH Refresh display. .It Dv CC_REFRESH_BEEP Refresh display, and beep. .It Dv CC_CURSOR Cursor moved, so update and perform .Dv CC_REFRESH . .It Dv CC_REDISPLAY Redisplay entire input line. This is useful if a key binding outputs extra information. .It Dv CC_ERROR An error occurred. Beep, and flush tty. .It Dv CC_FATAL Fatal error, reset tty to known state. .El .It Dv EL_HIST , Fa "History *(*func)(History *, int op, ...)" , \ Fa "const char *ptr" Defines which history function to use, which is usually .Fn history . .Fa ptr should be the value returned by .Fn history_init . .It Dv EL_EDITMODE , Fa "int flag" If .Fa flag is non-zero, editing is enabled (the default). Note that this is only an indication, and does not affect the operation of .Nm . At this time, it is the caller's responsibility to check this (using .Fn el_get ) to determine if editing should be enabled or not. .It Dv EL_UNBUFFERED , Fa "int flag" If .Fa flag is zero, unbuffered mode is disabled (the default). In unbuffered mode, .Fn el_gets will return immediately after processing a single character. .It Dv EL_SAFEREAD , Fa "int flag" If the .Fa flag argument is non-zero, then .Nm editline -attempts to recover from read errors, ignoring the first interrrupted +attempts to recover from read errors, ignoring the first interrupted error, and trying to reset the input file descriptor to reset non-blocking I/O. This is disabled by default, and desirable only when .Nm editline is used in shell-like applications. .It Dv EL_GETCFN , Fa "el_rfunc_t f" Whenever reading a character, use the function .Bd -ragged -offset indent -compact .Ft int .Fo f .Fa "EditLine *e" .Fa "wchar_t *wc" .Fc .Ed which stores the character in .Fa wc and returns 1 on success, 0 on end of file, or \-1 on I/O or encoding errors. Functions internally using it include .Fn el_wgets , .Fn el_wgetc , .Fn el_gets , and .Fn el_getc . Initially, a builtin function is installed, and replacing it is discouraged because writing such a function is very error prone. The builtin function can be restored at any time by passing the special value .Dv EL_BUILTIN_GETCFN instead of a function pointer. .It Dv EL_CLIENTDATA , Fa "void *data" Register .Fa data to be associated with this EditLine structure. It can be retrieved with the corresponding .Fn el_get call. .It Dv EL_SETFP , Fa "int fd" , Fa "FILE *fp" Set the current .Nm editline file pointer for .Dq input .Fa fd = .Dv 0 , .Dq output .Fa fd = .Dv 1 , or .Dq error .Fa fd = .Dv 2 from .Fa fp . .El .It Fn el_get Get .Nm parameters. .Fa op determines which parameter to retrieve into .Fa result . Returns 0 if successful, \-1 otherwise. .Pp The following values for .Fa op are supported, along with actual type of .Fa result : .Bl -tag -width 4n .It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)" , Fa "char *c" Set .Fa f to a pointer to the function that displays the prompt. If .Fa c is not .Dv NULL , set it to the start/stop literal prompt character. .It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)" , Fa "char *c" Set .Fa f to a pointer to the function that displays the prompt. If .Fa c is not .Dv NULL , set it to the start/stop literal prompt character. .It Dv EL_EDITOR , Fa "const char **n" Set the name of the editor in .Fa n , which will be one of .Dq emacs or .Dq vi . .It Dv EL_GETTC , Fa "const char *name" , Fa "void *value" If .Fa name is a valid .Xr termcap 5 capability set .Fa value to the current value of that capability. .It Dv EL_SIGNAL , Fa "int *s" Set .Fa s to non-zero if .Nm has installed private signal handlers (see .Fn el_get above). .It Dv EL_EDITMODE , Fa "int *c" Set .Fa c to non-zero if editing is enabled. .It Dv EL_GETCFN , Fa "el_rfunc_t *f" Set .Fa f to a pointer to the function that reads characters, or to .Dv EL_BUILTIN_GETCFN if the builtin function is in use. .It Dv EL_CLIENTDATA , Fa "void **data" Set .Fa data to the previously registered client data set by an .Fn el_set call. .It Dv EL_UNBUFFERED , Fa "int *c" Set .Fa c to non-zero if unbuffered mode is enabled. .It Dv EL_SAFEREAD , Fa "int *c" Set .Fa c to non-zero if safe read is set. .It Dv EL_GETFP , Fa "int fd", Fa "FILE **fp" Set .Fa fp to the current .Nm editline file pointer for .Dq input .Fa fd = .Dv 0 , .Dq output .Fa fd = .Dv 1 , or .Dq error .Fa fd = .Dv 2 . .El .It Fn el_source Initialize .Nm by reading the contents of .Fa file . .Fn el_parse is called for each line in .Fa file . If .Fa file is .Dv NULL , try .Pa $EDITRC and if that is not set .Pa $HOME/.editrc . Refer to .Xr editrc 5 for details on the format of .Fa file . .Fn el_source returns 0 on success and \-1 on error. .It Fn el_resize Must be called if the terminal size changes. If .Dv EL_SIGNAL has been set with .Fn el_set , then this is done automatically. Otherwise, it's the responsibility of the application to call .Fn el_resize on the appropriate occasions. .It Fn el_cursor Move the cursor to the right (if positive) or to the left (if negative) .Fa count characters. Returns the resulting offset of the cursor from the beginning of the line. .It Fn el_line Return the editing information for the current line in a .Fa LineInfo structure, which is defined as follows: .Bd -literal typedef struct lineinfo { const char *buffer; /* address of buffer */ const char *cursor; /* address of cursor */ const char *lastchar; /* address of last character */ } LineInfo; .Ed .Pp .Fa buffer is not NUL terminated. This function may be called after .Fn el_gets to obtain the .Fa LineInfo structure pertaining to line returned by that function, and from within user defined functions added with .Dv EL_ADDFN . .It Fn el_insertstr Insert .Fa str into the line at the cursor. Returns \-1 if .Fa str is empty or won't fit, and 0 otherwise. .It Fn el_deletestr Delete .Fa count characters before the cursor. .El .Sh HISTORY LIST FUNCTIONS The history functions use a common data structure, .Fa History , which is created by .Fn history_init and freed by .Fn history_end . .Pp The following functions are available: .Bl -tag -width 4n .It Fn history_init Initialize the history list, and return a data structure to be used by all other history list functions, or .Dv NULL on failure. .It Fn history_end Clean up and finish with .Fa h , assumed to have been created with .Fn history_init . .It Fn history Perform operation .Fa op on the history list, with optional arguments as needed by the operation. .Fa ev is changed accordingly to operation. The following values for .Fa op are supported, along with the required argument list: .Bl -tag -width 4n .It Dv H_SETSIZE , Fa "int size" Set size of history to .Fa size elements. .It Dv H_GETSIZE Get number of events currently in history. .It Dv H_END Cleans up and finishes with .Fa h , assumed to be created with .Fn history_init . .It Dv H_CLEAR Clear the history. .It Dv H_FUNC , Fa "void *ptr" , Fa "history_gfun_t first" , \ Fa "history_gfun_t next" , Fa "history_gfun_t last" , \ Fa "history_gfun_t prev" , Fa "history_gfun_t curr" , \ Fa "history_sfun_t set" , Fa "history_vfun_t clear" , \ Fa "history_efun_t enter" , Fa "history_efun_t add" Define functions to perform various history operations. .Fa ptr is the argument given to a function when it's invoked. .It Dv H_FIRST Return the first element in the history. .It Dv H_LAST Return the last element in the history. .It Dv H_PREV Return the previous element in the history. It is newer than the current one. .It Dv H_NEXT Return the next element in the history. It is older than the current one. .It Dv H_CURR Return the current element in the history. .It Dv H_SET , Fa "int position" Set the cursor to point to the requested element. .It Dv H_ADD , Fa "const char *str" Append .Fa str to the current element of the history, or perform the .Dv H_ENTER operation with argument .Fa str if there is no current element. .It Dv H_APPEND , Fa "const char *str" Append .Fa str to the last new element of the history. .It Dv H_ENTER , Fa "const char *str" Add .Fa str as a new element to the history and, if necessary, removing the oldest entry to keep the list to the created size. If .Dv H_SETUNIQUE has been called with a non-zero argument, the element will not be entered into the history if its contents match the ones of the current history element. If the element is entered .Fn history returns 1; if it is ignored as a duplicate returns 0. Finally .Fn history returns \-1 if an error occurred. .It Dv H_PREV_STR , Fa "const char *str" Return the closest previous event that starts with .Fa str . .It Dv H_NEXT_STR , Fa "const char *str" Return the closest next event that starts with .Fa str . .It Dv H_PREV_EVENT , Fa "int e" Return the previous event numbered .Fa e . .It Dv H_NEXT_EVENT , Fa "int e" Return the next event numbered .Fa e . .It Dv H_LOAD , Fa "const char *file" Load the history list stored in .Fa file . .It Dv H_SAVE , Fa "const char *file" Save the history list to .Fa file . .It Dv H_SAVE_FP , Fa "FILE *fp" Save the history list to the opened .Ft FILE pointer .Fa fp . .It Dv H_NSAVE_FP , Fa "size_t n" , Fa "FILE *fp" Save the last .Ft n history entries to the opened .Ft FILE pointer .Fa fp . .It Dv H_SETUNIQUE , Fa "int unique" Set flag that adjacent identical event strings should not be entered into the history. .It Dv H_GETUNIQUE Retrieve the current setting if adjacent identical elements should be entered into the history. .It Dv H_DEL , Fa "int e" Delete the event numbered .Fa e . This function is only provided for .Nm readline compatibility. The caller is responsible for free'ing the string in the returned .Fa HistEvent . .El .Pp .Fn history returns >= 0 if the operation .Fa op succeeds. Otherwise, \-1 is returned and .Fa ev is updated to contain more details about the error. .El .Sh TOKENIZATION FUNCTIONS The tokenization functions use a common data structure, .Fa Tokenizer , which is created by .Fn tok_init and freed by .Fn tok_end . .Pp The following functions are available: .Bl -tag -width 4n .It Fn tok_init Initialize the tokenizer, and return a data structure to be used by all other tokenizer functions. .Fa IFS contains the Input Field Separators, which defaults to .Aq space , .Aq tab , and .Aq newline if .Dv NULL . .It Fn tok_end Clean up and finish with .Fa t , assumed to have been created with .Fn tok_init . .It Fn tok_reset Reset the tokenizer state. Use after a line has been successfully tokenized by .Fn tok_line or .Fn tok_str and before a new line is to be tokenized. .It Fn tok_line Tokenize .Fa li , If successful, modify: .Fa argv to contain the words, .Fa argc to contain the number of words, .Fa cursorc (if not .Dv NULL ) to contain the index of the word containing the cursor, and .Fa cursoro (if not .Dv NULL ) to contain the offset within .Fa argv[cursorc] of the cursor. .Pp Returns 0 if successful, \-1 for an internal error, 1 for an unmatched single quote, 2 for an unmatched double quote, and 3 for a backslash quoted .Aq newline . A positive exit code indicates that another line should be read and tokenization attempted again. . .It Fn tok_str A simpler form of .Fn tok_line ; .Fa str is a NUL terminated string to tokenize. .El . .\"XXX.Sh EXAMPLES .\"XXX: provide some examples .Sh SEE ALSO .Xr sh 1 , .Xr signal 3 , .Xr termcap 3 , .Xr editrc 5 , .Xr termcap 5 , .Xr editline 7 .Sh HISTORY The .Nm library first appeared in .Bx 4.4 . .Dv CC_REDISPLAY appeared in .Nx 1.3 . .Dv CC_REFRESH_BEEP , .Dv EL_EDITMODE and the readline emulation appeared in .Nx 1.4 . .Dv EL_RPROMPT appeared in .Nx 1.5 . .Sh AUTHORS .An -nosplit The .Nm library was written by .An Christos Zoulas . .An Luke Mewburn wrote this manual and implemented .Dv CC_REDISPLAY , .Dv CC_REFRESH_BEEP , .Dv EL_EDITMODE , and .Dv EL_RPROMPT . .An Jaromir Dolecek implemented the readline emulation. .An Johny Mattsson implemented wide-character support. .Sh BUGS At this time, it is the responsibility of the caller to check the result of the .Dv EL_EDITMODE operation of .Fn el_get (after an .Fn el_source or .Fn el_parse ) to determine if .Nm should be used for further input. I.e., .Dv EL_EDITMODE is purely an indication of the result of the most recent .Xr editrc 5 .Ic edit command. diff --git a/contrib/libedit/editline.7 b/contrib/libedit/editline.7 index 863bab96d519..9a96fca47fa5 100644 --- a/contrib/libedit/editline.7 +++ b/contrib/libedit/editline.7 @@ -1,935 +1,935 @@ -.\" $NetBSD: editline.7,v 1.5 2016/05/09 21:27:55 christos Exp $ +.\" $NetBSD: editline.7,v 1.6 2024/04/06 13:36:11 christos Exp $ .\" $OpenBSD: editline.7,v 1.1 2016/04/20 01:11:45 schwarze Exp $ .\" .\" Copyright (c) 2016 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd May 7, 2016 +.Dd April 6, 2024 .Dt EDITLINE 7 .Os .Sh NAME .Nm editline .Nd line editing user interface .Sh DESCRIPTION When a program using the .Xr editline 3 library prompts for an input string using the function .Xr el_wgets 3 , it reads characters from the terminal. Invalid input bytes that do not form characters are silently discarded. For each character read, one editor command is executed. The mapping of input characters to editor commands depends on the editing mode. There are three editing modes: vi insert mode, vi command mode, and emacs mode. The default is vi insert mode. The program can switch the default to emacs mode by using the .Xr el_set 3 or .Xr el_parse 3 functions, and the user can switch to emacs mode either in the .Xr editrc 5 configuration file or interactively with the .Ic ed-command editor command, in all three cases executing the .Ic bind Fl e builtin command. .Pp If trying to read from the terminal results in end of file or an error, the library signals end of file to the program and does not return a string. .Ss Input character bindings All default bindings described below can be overridden by individual programs and can be changed with the .Xr editrc 5 .Ic bind builtin command. .Pp In the following tables, .Sq Ctrl- indicates a character with the bit 0x40 flipped, and .Sq Meta- indicates a character with the bit 0x80 set. In vi insert mode and in emacs mode, all Meta-characters considered printable by the current .Xr locale 1 are bound to .Ic ed-insert instead of to the editor command listed below. Consequently, in UTF-8 mode, most of the Meta-characters are not directly accessible because their code points are occupied by printable Unicode characters, and Meta-characters are usually input using the .Ic em-meta-next editor command. For example, to enter .Sq Meta-B in order to call the .Ic ed-prev-word editor command in emacs mode, call .Ic em-meta-next by pressing and releasing the escape key (or equivalently, Ctrl-[), then press and release the .Sq B key. If you have configured a Meta-key on your keyboard, for example with .Ql setxkbmap -option altwin:left_meta_win , the Ctrl-Meta-characters are directly accessible. For example, to enter .Sq Ctrl-Meta-H in order to call the .Ic ed-delete-prev-word editor command in emacs mode, hold down the keys .Sq Ctrl , .Sq Meta , and .Sq H at the same time. Alternatively, press and release the escape key, then press and release .Sq Ctrl-H . .Pp In vi input mode, input characters are bound to the following editor commands by default: .Bl -column -offset indent "Ctrl-Z, TSTP" "ed-search-next-history" .It Ctrl-D, EOF Ta Ic vi-list-or-eof .It Ctrl-H, BS Ta Ic vi-delete-prev-char .It Ctrl-J, LF Ta Ic ed-newline .It Ctrl-M, CR Ta Ic ed-newline .It Ctrl-Q Ta Ic ed-tty-start-output .It Ctrl-S Ta Ic ed-tty-stop-output .It Ctrl-U Ta Ic vi-kill-line-prev .It Ctrl-V Ta Ic ed-quoted-insert .It Ctrl-W Ta Ic ed-delete-prev-word .It Ctrl-[, ESC Ta Ic vi-command-mode .It Ctrl-\e, QUIT Ta Ic ed-tty-sigquit .It Ctrl-?, DEL Ta Ic vi-delete-prev-char .El .Pp All other input characters except the NUL character (Ctrl-@) are bound to .Ic ed-insert . .Pp In vi command mode, input characters are bound to the following editor commands by default: .Bl -column -offset indent "Ctrl-Z, TSTP" "ed-search-next-history" .It Ctrl-A Ta Ic ed-move-to-beg .It Ctrl-C, INT Ta Ic ed-tty-sigint .It Ctrl-E Ta Ic ed-move-to-end .It Ctrl-H, BS Ta Ic ed-delete-prev-char .It Ctrl-J, LF Ta Ic ed-newline .It Ctrl-K Ta Ic ed-kill-line .It Ctrl-L, FF Ta Ic ed-clear-screen .It Ctrl-M, CR Ta Ic ed-newline .It Ctrl-N Ta Ic ed-next-history .It Ctrl-O Ta Ic ed-tty-flush-output .It Ctrl-P Ta Ic ed-prev-history .It Ctrl-Q Ta Ic ed-tty-start-output .It Ctrl-R Ta Ic ed-redisplay .It Ctrl-S Ta Ic ed-tty-stop-output .It Ctrl-U Ta Ic vi-kill-line-prev .It Ctrl-W Ta Ic ed-delete-prev-word .It Ctrl-[, ESC Ta Ic em-meta-next .It Ctrl-\e, QUIT Ta Ic ed-tty-sigquit .It Space Ta Ic ed-next-char .It # Ta Ic vi-comment-out .It $ Ta Ic ed-move-to-end .It % Ta Ic vi-match .It + Ta Ic ed-next-history .It \&, Ta Ic vi-repeat-prev-char .It - Ta Ic ed-prev-history .It \&. Ta Ic vi-redo .It / Ta Ic vi-search-prev .It 0 Ta Ic vi-zero .It 1 to 9 Ta Ic ed-argument-digit .It \&: Ta Ic ed-command .It \&; Ta Ic vi-repeat-next-char .It \&? Ta Ic vi-search-next .It @ Ta Ic vi-alias .It A Ta Ic vi-add-at-eol .It B Ta Ic vi-prev-big-word .It C Ta Ic vi-change-to-eol .It D Ta Ic ed-kill-line .It E Ta Ic vi-end-big-word .It F Ta Ic vi-prev-char .It G Ta Ic vi-to-history-line .It I Ta Ic vi-insert-at-bol .It J Ta Ic ed-search-next-history .It K Ta Ic ed-search-prev-history .It N Ta Ic vi-repeat-search-prev .It O Ta Ic ed-sequence-lead-in .It P Ta Ic vi-paste-prev .It R Ta Ic vi-replace-mode .It S Ta Ic vi-substitute-line .It T Ta Ic vi-to-prev-char .It U Ta Ic vi-undo-line .It W Ta Ic vi-next-big-word .It X Ta Ic ed-delete-prev-char .It Y Ta Ic vi-yank-end .It \&[ Ta Ic ed-sequence-lead-in .It ^ Ta Ic ed-move-to-beg .It _ Ta Ic vi-history-word .It a Ta Ic vi-add .It b Ta Ic vi-prev-word .It c Ta Ic vi-change-meta .It d Ta Ic vi-delete-meta .It e Ta Ic vi-end-word .It f Ta Ic vi-next-char .It h Ta Ic ed-prev-char .It i Ta Ic vi-insert .It j Ta Ic ed-next-history .It k Ta Ic ed-prev-history .It l Ta Ic ed-next-char .It n Ta Ic vi-repeat-search-next .It p Ta Ic vi-paste-next .It r Ta Ic vi-replace-char .It s Ta Ic vi-substitute-char .It t Ta Ic vi-to-next-char .It u Ta Ic vi-undo .It v Ta Ic vi-histedit .It w Ta Ic vi-next-word .It x Ta Ic ed-delete-next-char .It y Ta Ic vi-yank .It \&| Ta Ic vi-to-column .It ~ Ta Ic vi-change-case .It Ctrl-?, DEL Ta Ic ed-delete-prev-char .It Meta-O Ta Ic ed-sequence-lead-in .It Meta-[ Ta Ic ed-sequence-lead-in .El .Pp In emacs mode, input characters are bound to the following editor commands by default: .Bl -column -offset indent "Ctrl-Z, TSTP" "ed-search-next-history" .It 0 to 9 Ta Ic ed-digit .It Ctrl-@, NUL Ta Ic em-set-mark .It Ctrl-A Ta Ic ed-move-to-beg .It Ctrl-B Ta Ic ed-prev-char .It Ctrl-C, INT Ta Ic ed-tty-sigint .It Ctrl-D, EOF Ta Ic em-delete-or-list .It Ctrl-E Ta Ic ed-move-to-end .It Ctrl-F Ta Ic ed-next-char .It Ctrl-H, BS Ta Ic em-delete-prev-char .It Ctrl-J, LF Ta Ic ed-newline .It Ctrl-K Ta Ic ed-kill-line .It Ctrl-L, FF Ta Ic ed-clear-screen .It Ctrl-M, CR Ta Ic ed-newline .It Ctrl-N Ta Ic ed-next-history .It Ctrl-O Ta Ic ed-tty-flush-output .It Ctrl-P Ta Ic ed-prev-history .It Ctrl-Q Ta Ic ed-tty-start-output .It Ctrl-R Ta Ic ed-redisplay .It Ctrl-S Ta Ic ed-tty-stop-output .It Ctrl-T Ta Ic ed-transpose-chars .It Ctrl-U Ta Ic ed-kill-line .It Ctrl-V Ta Ic ed-quoted-insert .It Ctrl-W Ta Ic em-kill-region .It Ctrl-X Ta Ic ed-sequence-lead-in .It Ctrl-Y Ta Ic em-yank .It Ctrl-Z, TSTP Ta Ic ed-tty-sigtstp .It Ctrl-[, ESC Ta Ic em-meta-next .It Ctrl-\e, QUIT Ta Ic ed-tty-sigquit .It Ctrl-] Ta Ic ed-tty-dsusp .It Ctrl-?, DEL Ta Ic em-delete-prev-char .It Ctrl-Meta-H Ta Ic ed-delete-prev-word .It Ctrl-Meta-L Ta Ic ed-clear-screen .It Ctrl-Meta-_ Ta Ic em-copy-prev-word .It Meta-0 to 9 Ta Ic ed-argument-digit .It Meta-B Ta Ic ed-prev-word .It Meta-C Ta Ic em-capitol-case .It Meta-D Ta Ic em-delete-next-word .It Meta-F Ta Ic em-next-word .It Meta-L Ta Ic em-lower-case .It Meta-N Ta Ic ed-search-next-history .It Meta-O Ta Ic ed-sequence-lead-in .It Meta-P Ta Ic ed-search-prev-history .It Meta-U Ta Ic em-upper-case .It Meta-W Ta Ic em-copy-region .It Meta-X Ta Ic ed-command .It Meta-[ Ta Ic ed-sequence-lead-in .It Meta-b Ta Ic ed-prev-word .It Meta-c Ta Ic em-capitol-case .It Meta-d Ta Ic em-delete-next-word .It Meta-f Ta Ic em-next-word .It Meta-l Ta Ic em-lower-case .It Meta-n Ta Ic ed-search-next-history .It Meta-p Ta Ic ed-search-prev-history .It Meta-u Ta Ic em-upper-case .It Meta-w Ta Ic em-copy-region .It Meta-x Ta Ic ed-command .It Ctrl-Meta-? Ta Ic ed-delete-prev-word .El .Pp The remaining .Xr ascii 7 characters in the range 0x20 to 0x7e are bound to .Ic ed-insert . .Pp If standard output is not connected to a terminal device or .Xr el_set 3 was used to set .Dv EL_EDITMODE to 0, all input character bindings are disabled and all characters typed are appended to the edit buffer. In that case, the edit buffer is returned to the program after a newline or carriage return character is typed, or after the first character typed if .Xr el_set 3 was used to set .Dv EL_UNBUFFERED to non-zero. .Ss Editor commands Most editor commands accept an optional argument. The argument is entered by prefixing the editor command with one or more of the editor commands .Ic ed-argument-digit , .Ic ed-digit , .Ic em-universal-argument , or .Ic vi-zero . When an argument is not provided, it defaults to 1. For most editor commands, the effect of an argument is to repeatedly execute the command that number of times. .Pp When talking about a character string from a left character to a right character, the left character is included in the string, while the right character is not included. .Pp If an editor command causes an error, the input character is discarded, no action occurs, and the terminal bell is rung. In case of a non-fatal error, the terminal bell is also rung, but the editor command takes effect anyway. .Pp In the following list, the default key bindings are listed after each editor command. .Bl -tag -width 4n .It Ic ed-argument-digit Pq vi command: 1 to 9; emacs: Meta-0 to Meta-9 If in argument input mode, append the input digit to the argument being read. Otherwise, switch to argument input mode and use the input digit as the most significant digit of the argument. It is an error if the input character is not a digit or if the existing argument is already greater than a million. .It Ic ed-clear-screen Pq vi command: Ctrl-L; emacs: Ctrl-L, Ctrl-Meta-L Clear the screen and display the edit buffer at the top. Ignore any argument. .It Ic ed-command Pq vi command: So \&: Sc ; emacs: Meta-X, Meta-x Read a line from the terminal bypassing the normal line editing functionality and execute that line as an .Xr editrc 5 builtin command. If in vi command mode, also switch back to vi insert mode. Ignore any argument. .It Ic ed-delete-next-char Pq vi command: x Delete the character at the cursor position. With an argument, delete that number of characters. In emacs mode, it is an error if the cursor is at the end of the edit buffer. In vi mode, the last character in the edit buffer is deleted in that case, and it is an error if the buffer is empty. .It Ic ed-delete-prev-char Pq vi command: X, Ctrl-H, BS, Ctrl-?, DEL Delete the character to the left of the cursor position. With an argument, delete that number of characters. It is an error if the cursor is at the beginning of the edit buffer. .It Ic ed-delete-prev-word Pq vi: Ctrl-W; emacs: Ctrl-Meta-H, Ctrl-Meta-? Move to the left to the closest beginning of a word, delete the string from that position to the cursor, and save it to the cut buffer. With an argument, delete that number of words. It is an error if the cursor is at the beginning of the edit buffer. .It Ic ed-digit Pq emacs: 0 to 9 If in argument input mode, append the input digit to the argument being read. Otherwise, call .Ic ed-insert . It is an error if the input character is not a digit or if the existing argument is already greater than a million. .It Ic ed-end-of-file Pq not bound by default Discard the edit buffer and indicate end of file to the program. Ignore any argument. .It Ic ed-ignore Pq various Discard the input character and do nothing. .It Ic ed-insert Pq vi input: almost all; emacs: printable characters In insert mode, insert the input character left of the cursor position. In replace mode, overwrite the character at the cursor and move the cursor to the right by one character position. Accept an argument to do this repeatedly. It is an error if the input character is the NUL character (Ctrl-@). Failure to enlarge the edit buffer also results in an error. .It Ic ed-kill-line Pq vi command: D, Ctrl-K; emacs: Ctrl-K, Ctrl-U Delete the string from the cursor position to the end of the line and save it to the cut buffer. Ignore any argument. .It Ic ed-move-to-beg Pq vi command: ^, Ctrl-A; emacs: Ctrl-A In vi mode, move the cursor to the first non-space character in the edit buffer. In emacs mode, move the cursor to the beginning of the edit buffer. Ignore any argument. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . .It Ic ed-move-to-end Pq vi command: $, Ctrl-E; emacs: Ctrl-E Move the cursor to the end of the edit buffer. Ignore any argument. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . .It Ic ed-newline Pq all modes: Ctrl-J, LF, Ctrl-M, CR Append a newline character to the edit buffer and return the edit buffer to the program. Ignore any argument. .It Ic ed-next-char Pq vi command: Space, l; emacs: Ctrl-F Move the cursor one character position to the right. With an argument, move by that number of characters. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the end of the edit buffer. .It Ic ed-next-history Pq vi command: j, +, Ctrl-N; emacs: Ctrl-N Replace the edit buffer with the next history line. That line is older than the current line. With an argument, go forward by that number of history lines. It is a non-fatal error to advance by more lines than are available. .It Ic ed-next-line Pq not bound by default Move the cursor down one line. With an argument, move down by that number of lines. It is an error if the edit buffer does not contain enough newline characters to the right of the cursor position. .It Ic ed-prev-char Pq vi command: h; emacs: Ctrl-B Move the cursor one character position to the left. With an argument, move by that number of characters. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the beginning of the edit buffer. .It Ic ed-prev-history Pq vi command: k, -, Ctrl-P; emacs: Ctrl-P Replace the edit buffer with the previous history line. That line is newer than the current line. With an argument, go back by that number of lines. It is a non-fatal error to back up by more lines than are available. .It Ic ed-prev-line Pq not bound by default Move the cursor up one line. With an argument, move up by that number of lines. It is an error if the edit buffer does not contain enough newline characters to the left of the cursor position. .It Ic ed-prev-word Pq emacs: Meta-B, Meta-b Move the cursor to the left to the closest beginning of a word. With an argument, repeat that number of times. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the beginning of the edit buffer. .It Ic ed-quoted-insert Pq vi insert, emacs: Ctrl-V Read one character from the terminal bypassing the normal line editing functionality and call .Ic ed-insert on it. If trying to read the character returns end of file or an error, call .Ic ed-end-of-file instead. .It Ic ed-redisplay Pq vi command, emacs: Ctrl-R Redisplay everything. Ignore any argument. .It Ic ed-search-next-history Pq vi command: J; emacs: Meta-N, Meta-n Replace the edit buffer with the next matching history entry. .It Ic ed-search-prev-history Pq vi command: K; emacs: Meta-P, Meta-p Replace the edit buffer with the previous matching history entry. .It Ic ed-sequence-lead-in Pq vi cmd: O, \&[; emacs: Ctrl-X;\ both: Meta-O, Meta-[ Call a macro. See the section about .Sx Macros below for details. .It Ic ed-start-over Pq not bound by default Discard the contents of the edit buffer and start from scratch. Ignore any argument. .It Ic ed-transpose-chars Pq emacs: Ctrl-T Exchange the character at the cursor position with the one to the left of it and move the cursor to the character to the right of the two exchanged characters. Ignore any argument. It is an error if the cursor is at the beginning of the edit buffer or if the edit buffer contains less than two characters. .It Ic ed-unassigned Pq all characters not listed This editor command always results in an error. .It Ic em-capitol-case Pq emacs: Meta-C, Meta-c Capitalize the string from the cursor to the end of the current word. That is, if it contains at least one alphabetic character, convert the first alphabetic character to upper case, and convert all characters to the right of it to lower case. In any case, move the cursor to the next character after the end of the current word. .It Ic em-copy-prev-word Pq emacs: Ctrl-Meta-_ Copy the string from the beginning of the current word to the cursor and insert it to the left of the cursor. Move the cursor to the character after the inserted string. It is an error if the cursor is at the beginning of the edit buffer. .It Ic em-copy-region Pq emacs: Meta-W, Meta-w Copy the string from the cursor to the mark to the cut buffer. It is an error if the mark is not set. .It Ic em-delete-next-word Pq emacs: Meta-D, Meta-d Delete the string from the cursor to the end of the current word and save it to the cut buffer. It is an error if the cursor is at the end of the edit buffer. .It Ic em-delete-or-list Pq emacs: Ctrl-D, EOF If the cursor is not at the end of the line, delete the character at the cursor. If the edit buffer is empty, indicate end of file to the program. It is an error if the cursor is at the end of the edit buffer and the edit buffer is not empty. .It Ic em-delete-prev-char Pq emacs: Ctrl-H, BS, Ctrl-?, DEL Delete the character to the left of the cursor. It is an error if the cursor is at the beginning of the edit buffer. .It Ic em-exchange-mark Pq not bound by default Exchange the cursor and the mark. .It Ic em-gosmacs-transpose Pq not bound by default Exchange the two characters to the left of the cursor. It is an error if the cursor is on the first or second character of the edit buffer. .It Ic em-inc-search-next Pq not bound by default Emacs incremental next search. .It Ic em-inc-search-prev Pq not bound by default Emacs incremental reverse search. .It Ic em-kill-line Pq not bound by default Delete the entire contents of the edit buffer and save it to the cut buffer. .It Ic em-kill-region Pq emacs: Ctrl-W Delete the string from the cursor to the mark and save it to the cut buffer. It is an error if the mark is not set. .It Ic em-lower-case Pq emacs: Meta-L, Meta-l Convert the characters from the cursor to the end of the current word to lower case. .It Ic em-meta-next Pq vi command, emacs: Ctrl-[, ESC Set the bit 0x80 on the next character typed. Unless the resulting code point is printable, holding down the .Sq Meta- key while typing that character is a simpler way to achieve the same effect. .It Ic em-next-word Pq Meta-F, Meta-f Move the cursor to the end of the current word. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the end of the edit buffer. .It Ic em-set-mark Pq emacs: Ctrl-Q, NUL Set the mark at the current cursor position. -.It Ic em-toggle-overwrite Pq not bound by default +.It Ic em-toggle-overwrite Pq insert Switch from insert to overwrite mode or vice versa. .It Ic em-universal-argument Pq not bound by default If in argument input mode, multiply the argument by 4. Otherwise, switch to argument input mode and set the argument to 4. It is an error if the existing argument is already greater than a million. .It Ic em-upper-case Pq emacs: Meta-U, Meta-u Convert the characters from the cursor to the end of the current word to upper case. .It Ic em-yank Pq emacs: Ctrl-Y Paste the cut buffer to the left of the cursor. .It Ic vi-add Pq vi command: a Switch to vi insert mode. Unless the cursor is already at the end of the edit buffer, move it one character position to the right. .It Ic vi-add-at-eol Pq vi command: A Switch to vi insert mode and move the cursor to the end of the edit buffer. .It Ic vi-alias Pq vi command: @ If an alias function was defined by calling the .Xr el_set 3 or .Xr el_wset 3 function with the argument .Dv EL_ALIAS_TEXT , read one character from the terminal bypassing the normal line editing functionality, call the alias function passing the argument that was specified with .Dv EL_ALIAS_TEXT as the first argument and the character read, with an underscore prepended, as the second argument, and pass the string returned from the alias function to .Xr el_wpush 3 . It is an error if no alias function is defined or if trying to read the character results in end of file or an error. .It Ic vi-change-case Pq vi command: ~ Change the case of the character at the cursor and move the cursor one character position to the right. It is an error if the cursor is already at the end of the edit buffer. .It Ic vi-change-meta Pq vi command: c Delete the string from the cursor to the position specified by the following movement command and save a copy of it to the cut buffer. When given twice in a row, instead delete the whole contents of the edit buffer and save a copy of it to the cut buffer. In either case, switch to vi insert mode after that. .It Ic vi-change-to-eol Pq vi command: C Delete the string from the cursor position to the end of the line and save it to the cut buffer, then switch to vi insert mode. .It Ic vi-command-mode Pq vi insert: Ctrl-[, ESC Discard pending actions and arguments and switch to vi command mode. Unless the cursor is already at the beginning of the edit buffer, move it to the left by one character position. .It Ic vi-comment-out Pq vi command: # Insert a .Sq # character at the beginning of the edit buffer and return the edit buffer to the program. .It Ic vi-delete-meta Pq vi command: d Delete the string from the cursor to the position specified by the following movement command and save a copy of it to the cut buffer. When given twice in a row, instead delete the whole contents of the edit buffer and save a copy of it to the cut buffer. .It Ic vi-delete-prev-char Pq vi insert: Ctrl-H, BS, Ctrl-?, DEL Delete the character to the left of the cursor. It is an error if the cursor is already at the beginning of the edit buffer. .It Ic vi-end-big-word Pq vi command: E Move the cursor to the end of the current space delimited word. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the end of the edit buffer. .It Ic vi-end-word Pq vi command: e Move the cursor to the end of the current word. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the end of the edit buffer. .It Ic vi-history-word Pq vi command: _ Insert the first word from the most recent history entry after the cursor, move the cursor after to the character after the inserted word, and switch to vi insert mode. It is an error if there is no history entry or the most recent history entry is empty. .It Ic vi-insert Pq vi command: i Enter insert mode. .It Ic vi-insert-at-bol Pq vi command: I Move the cursor to the beginning of the edit buffer and switch to vi insert mode. .It Ic vi-kill-line-prev Pq vi: Ctrl-U Delete the string from the beginning of the edit buffer to the cursor and save it to the cut buffer. .It Ic vi-list-or-eof Pq vi insert: Ctrl-D, EOF If the edit buffer is empty, indicate end of file to the program. It is an error if the edit buffer is not empty. .It Ic vi-match Pq vi command: % Consider opening and closing parentheses, braces, and brackets as delimiters. If the cursor is not at a delimiter, move it to the right until it gets to one, then move it to the matching delimiter. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if there is no delimiter at the cursor or in the string to the right of the cursor, or if the first such delimiter has no matching delimiter. .It Ic vi-next-big-word Pq vi command: W Move the cursor to the right to the beginning of the next space delimited word. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the end of the edit buffer or on its last character. .It Ic vi-next-char Pq vi command: f Read one character from the terminal bypassing the normal line editing functionality and move the cursor to the right to the next instance of that character in the edit buffer. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . If trying to read the character results in end of file or an error, call .Ic ed-end-of-file instead. It is an error if the character is not found searching to the right in the edit buffer. .It Ic vi-next-word Pq vi command: w Move the cursor to the right to the beginning of the next word. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the end of the edit buffer or on its last character. .It Ic vi-paste-next Pq vi command: p Insert a copy of the cut buffer to the right of the cursor. It is an error if the cut buffer is empty. .It Ic vi-paste-prev Pq vi command: P Insert a copy of the cut buffer to the left of the cursor. It is an error if the cut buffer is empty. .It Ic vi-prev-big-word Pq vi command: B Move the cursor to the left to the next beginning of a space delimited word. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the beginning of the edit buffer. .It Ic vi-prev-char Pq vi command: F Read one character from the terminal bypassing the normal line editing functionality and move the cursor to the left to the next instance of that character in the edit buffer. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . If trying to read the character results in end of file or an error, call .Ic ed-end-of-file instead. It is an error if the character is not found searching to the left in the edit buffer. .It Ic vi-prev-word Pq vi command: b Move the cursor to the left to the next beginning of a word. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . It is an error if the cursor is already at the beginning of the edit buffer. .It Ic vi-redo Pq vi command: Sq \&. Redo the last non-motion command. .It Ic vi-repeat-next-char Pq vi command: Sq \&; Repeat the most recent character search in the same search direction. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . .It Ic vi-repeat-prev-char Pq vi command: Sq \&, Repeat the most recent character search in the opposite search direction. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . .It Ic vi-repeat-search-next Pq vi command: n Repeat the most recent history search in the same search direction. .It Ic vi-repeat-search-prev Pq vi command: N Repeat the most recent history search in the opposite search direction. .It Ic vi-replace-char Pq vi command: r Switch to vi replace mode, and automatically switch back to vi command mode after the next character typed. See .Ic ed-insert for a description of replace mode. It is an error if the cursor is at the end of the edit buffer. .It Ic vi-replace-mode Pq vi command: R Switch to vi replace mode. This is a variant of vi insert mode; see .Ic ed-insert for the difference. .It Ic vi-search-next Pq vi command: \&? Replace the edit buffer with the next matching history entry. .It Ic vi-search-prev Pq vi command: / Replace the edit buffer with the previous matching history entry. .It Ic vi-substitute-char Pq vi command: s Delete the character at the cursor and switch to vi insert mode. .It Ic vi-substitute-line Pq vi command: S Delete the entire contents of the edit buffer, save a copy of it in the cut buffer, and enter vi insert mode. .It Ic vi-to-column Pq vi command: \&| Move the cursor to the column specified as the argument. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . .It Ic vi-to-history-line Pq vi command: G Replace the edit buffer with the specified history entry. .It Ic vi-to-next-char Pq vi command: t Read one character from the terminal bypassing the normal line editing functionality and move the cursor to the right to the character before the next instance of that character in the edit buffer. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . If trying to read the character results in end of file or an error, call .Ic ed-end-of-file instead. It is an error if the character is not found searching to the right in the edit buffer. .It Ic vi-to-prev-char Pq vi command: T Read one character from the terminal bypassing the normal line editing functionality and move the cursor to the left to the character after the next instance of that character in the edit buffer. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . If trying to read the character results in end of file or an error, call .Ic ed-end-of-file instead. It is an error if the character is not found searching to the left in the edit buffer. .It Ic vi-undo Pq vi command: u Undo the last change. .It Ic vi-undo-line Pq vi command: U Undo all changes to the edit buffer. .It Ic vi-yank Pq vi command: y Copy the string from the cursor to the position specified by the following movement command to the cut buffer. When given twice in a row, instead copy the whole contents of the edit buffer to the cut buffer. .It Ic vi-yank-end Pq vi command: Y Copy the string from the cursor to the end of the edit buffer to the cut buffer. .It Ic vi-zero Pq vi command: 0 If in argument input mode, multiply the argument by ten. Otherwise, move the cursor to the beginning of the edit buffer. Can be used as a movement command after .Ic vi_change_meta , .Ic vi_delete_meta , or .Ic vi_yank . .El .Ss Macros If an input character is bound to the editor command .Ic ed-sequence-lead-in , .Nm attempts to call a macro. If the input character by itself forms the name of a macro, that macro is executed. Otherwise, additional input characters are read until the string read forms the name of a macro, in which case that macro is executed, or until the string read matches the beginning of none of the existing macro names, in which case the string including the final, mismatching character is discarded and the terminal bell is rung. .Pp There are two kinds of macros. Command macros execute a single editor command. Keyboard macros return a string of characters that is appended as a new line to the .Sx Input Queue . .Pp The following command macros are defined by default in vi command mode and in emacs mode: .Bl -column -offset indent "Esc O A, Esc O A" "em-exchange-mark" .It Esc \&[ A, Esc O A Ta Ic ed-prev-history .It Esc \&[ B, Esc O B Ta Ic ed-next-history .It Esc \&[ C, Esc O C Ta Ic ed-next-char .It Esc \&[ D, Esc O D Ta Ic ed-prev-char .It Esc \&[ F, Esc O F Ta Ic ed-move-to-end .It Esc \&[ H, Esc O H Ta Ic ed-move-to-beg .El .Pp In vi command mode, they are also defined by default without the initial escape character. .Pp In addition, the .Nm library tries to bind the strings generated by the arrow keys as reported by the .Xr terminfo 5 database to these editor commands, unless that would clobber user settings. .Pp In emacs mode, the two-character string .Dq Ctrl-X Ctrl-X is bound to the .Ic em-exchange-mark editor command. .Ss Input Queue The .Nm library maintains an input queue operated in FIFO mode. Whenever it needs an input character, it takes the first character from the first line of the input queue. When the queue is empty, it reads from the terminal. .Pp A line can be appended to the end of the input queue in several ways: .Bl -dash -offset indent .It By calling one of the keyboard .Sx Macros . .It By calling the editor command .Ic vi-redo . .It By calling the editor command .Ic vi-alias . .It By pressing a key in emacs incremental search mode that doesn't have a special meaning in that mode but returns to normal emacs mode. .It If an application program directly calls the functions .Xr el_push 3 or .Xr el_wpush 3 , it can provide additional, program-specific ways of appending to the input queue. .El .Sh SEE ALSO .Xr mg 1 , .Xr vi 1 , .Xr editline 3 , .Xr el_wgets 3 , .Xr el_wpush 3 , .Xr el_wset 3 , .Xr editrc 5 .Sh HISTORY This manual page first appeared in .Ox 6.0 and .Nx 8 . .Sh AUTHORS .An -nosplit This manual page was written by .An Ingo Schwarze Aq Mt schwarze@openbsd.org . diff --git a/contrib/libedit/el.c b/contrib/libedit/el.c index 2c06e32de9ff..577fda821870 100644 --- a/contrib/libedit/el.c +++ b/contrib/libedit/el.c @@ -1,658 +1,657 @@ -/* $NetBSD: el.c,v 1.101 2022/10/30 19:11:31 christos Exp $ */ +/* $NetBSD: el.c,v 1.102 2025/01/03 00:40:08 rillig Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94"; #else -__RCSID("$NetBSD: el.c,v 1.101 2022/10/30 19:11:31 christos Exp $"); +__RCSID("$NetBSD: el.c,v 1.102 2025/01/03 00:40:08 rillig Exp $"); #endif #endif /* not lint && not SCCSID */ /* * el.c: EditLine interface functions */ #include #include #include #include #include #include #include #include #include "el.h" #include "parse.h" #include "read.h" /* el_init(): * Initialize editline and set default parameters. */ EditLine * el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) { return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout), fileno(ferr)); } libedit_private EditLine * el_init_internal(const char *prog, FILE *fin, FILE *fout, FILE *ferr, int fdin, int fdout, int fderr, int flags) { EditLine *el = el_calloc(1, sizeof(*el)); if (el == NULL) return NULL; el->el_infile = fin; el->el_outfile = fout; el->el_errfile = ferr; el->el_infd = fdin; el->el_outfd = fdout; el->el_errfd = fderr; el->el_prog = wcsdup(ct_decode_string(prog, &el->el_scratch)); if (el->el_prog == NULL) { el_free(el); return NULL; } /* * Initialize all the modules. Order is important!!! */ el->el_flags = flags; if (terminal_init(el) == -1) { el_free(el->el_prog); el_free(el); return NULL; } (void) keymacro_init(el); (void) map_init(el); if (tty_init(el) == -1) el->el_flags |= NO_TTY; (void) ch_init(el); (void) search_init(el); (void) hist_init(el); (void) prompt_init(el); (void) sig_init(el); (void) literal_init(el); if (read_init(el) == -1) { el_end(el); return NULL; } return el; } EditLine * el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr, int fdin, int fdout, int fderr) { return el_init_internal(prog, fin, fout, ferr, fdin, fdout, fderr, 0); } /* el_end(): * Clean up. */ void el_end(EditLine *el) { if (el == NULL) return; el_reset(el); terminal_end(el); keymacro_end(el); map_end(el); if (!(el->el_flags & NO_TTY)) tty_end(el, TCSAFLUSH); ch_end(el); read_end(el); search_end(el); hist_end(el); prompt_end(el); sig_end(el); literal_end(el); el_free(el->el_prog); el_free(el->el_visual.cbuff); el_free(el->el_visual.wbuff); el_free(el->el_scratch.cbuff); el_free(el->el_scratch.wbuff); el_free(el->el_lgcyconv.cbuff); el_free(el->el_lgcyconv.wbuff); el_free(el); } /* el_reset(): * Reset the tty and the parser */ void el_reset(EditLine *el) { tty_cookedmode(el); ch_reset(el); /* XXX: Do we want that? */ } /* el_set(): * set the editline parameters */ int el_wset(EditLine *el, int op, ...) { va_list ap; int rv = 0; if (el == NULL) return -1; va_start(ap, op); switch (op) { case EL_PROMPT: case EL_RPROMPT: { el_pfunc_t p = va_arg(ap, el_pfunc_t); rv = prompt_set(el, p, 0, op, 1); break; } case EL_RESIZE: { el_zfunc_t p = va_arg(ap, el_zfunc_t); void *arg = va_arg(ap, void *); rv = ch_resizefun(el, p, arg); break; } case EL_ALIAS_TEXT: { el_afunc_t p = va_arg(ap, el_afunc_t); void *arg = va_arg(ap, void *); rv = ch_aliasfun(el, p, arg); break; } case EL_PROMPT_ESC: case EL_RPROMPT_ESC: { el_pfunc_t p = va_arg(ap, el_pfunc_t); int c = va_arg(ap, int); rv = prompt_set(el, p, (wchar_t)c, op, 1); break; } case EL_TERMINAL: rv = terminal_set(el, va_arg(ap, char *)); break; case EL_EDITOR: rv = map_set_editor(el, va_arg(ap, wchar_t *)); break; case EL_SIGNAL: if (va_arg(ap, int)) el->el_flags |= HANDLE_SIGNALS; else el->el_flags &= ~HANDLE_SIGNALS; break; case EL_BIND: case EL_TELLTC: case EL_SETTC: case EL_ECHOTC: case EL_SETTY: { const wchar_t *argv[20]; int i; for (i = 1; i < (int)__arraycount(argv); i++) if ((argv[i] = va_arg(ap, wchar_t *)) == NULL) break; switch (op) { case EL_BIND: argv[0] = L"bind"; rv = map_bind(el, i, argv); break; case EL_TELLTC: argv[0] = L"telltc"; rv = terminal_telltc(el, i, argv); break; case EL_SETTC: argv[0] = L"settc"; rv = terminal_settc(el, i, argv); break; case EL_ECHOTC: argv[0] = L"echotc"; rv = terminal_echotc(el, i, argv); break; case EL_SETTY: argv[0] = L"setty"; rv = tty_stty(el, i, argv); break; default: rv = -1; EL_ABORT((el->el_errfile, "Bad op %d\n", op)); - break; } break; } case EL_ADDFN: { wchar_t *name = va_arg(ap, wchar_t *); wchar_t *help = va_arg(ap, wchar_t *); el_func_t func = va_arg(ap, el_func_t); rv = map_addfunc(el, name, help, func); break; } case EL_HIST: { hist_fun_t func = va_arg(ap, hist_fun_t); void *ptr = va_arg(ap, void *); rv = hist_set(el, func, ptr); if (MB_CUR_MAX == 1) el->el_flags &= ~NARROW_HISTORY; break; } case EL_SAFEREAD: if (va_arg(ap, int)) el->el_flags |= FIXIO; else el->el_flags &= ~FIXIO; rv = 0; break; case EL_EDITMODE: if (va_arg(ap, int)) el->el_flags &= ~EDIT_DISABLED; else el->el_flags |= EDIT_DISABLED; rv = 0; break; case EL_GETCFN: { el_rfunc_t rc = va_arg(ap, el_rfunc_t); rv = el_read_setfn(el->el_read, rc); break; } case EL_CLIENTDATA: el->el_data = va_arg(ap, void *); break; case EL_UNBUFFERED: rv = va_arg(ap, int); if (rv && !(el->el_flags & UNBUFFERED)) { el->el_flags |= UNBUFFERED; read_prepare(el); } else if (!rv && (el->el_flags & UNBUFFERED)) { el->el_flags &= ~UNBUFFERED; read_finish(el); } rv = 0; break; case EL_PREP_TERM: rv = va_arg(ap, int); if (rv) (void) tty_rawmode(el); else (void) tty_cookedmode(el); rv = 0; break; case EL_SETFP: { FILE *fp; int what; what = va_arg(ap, int); fp = va_arg(ap, FILE *); rv = 0; switch (what) { case 0: el->el_infile = fp; el->el_infd = fileno(fp); break; case 1: el->el_outfile = fp; el->el_outfd = fileno(fp); break; case 2: el->el_errfile = fp; el->el_errfd = fileno(fp); break; default: rv = -1; break; } break; } case EL_REFRESH: re_clear_display(el); re_refresh(el); terminal__flush(el); break; default: rv = -1; break; } va_end(ap); return rv; } /* el_get(): * retrieve the editline parameters */ int el_wget(EditLine *el, int op, ...) { va_list ap; int rv; if (el == NULL) return -1; va_start(ap, op); switch (op) { case EL_PROMPT: case EL_RPROMPT: { el_pfunc_t *p = va_arg(ap, el_pfunc_t *); rv = prompt_get(el, p, 0, op); break; } case EL_PROMPT_ESC: case EL_RPROMPT_ESC: { el_pfunc_t *p = va_arg(ap, el_pfunc_t *); wchar_t *c = va_arg(ap, wchar_t *); rv = prompt_get(el, p, c, op); break; } case EL_EDITOR: rv = map_get_editor(el, va_arg(ap, const wchar_t **)); break; case EL_SIGNAL: *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); rv = 0; break; case EL_EDITMODE: *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); rv = 0; break; case EL_SAFEREAD: *va_arg(ap, int *) = (el->el_flags & FIXIO); rv = 0; break; case EL_TERMINAL: terminal_get(el, va_arg(ap, const char **)); rv = 0; break; case EL_GETTC: { static char name[] = "gettc"; char *argv[3]; argv[0] = name; argv[1] = va_arg(ap, char *); argv[2] = va_arg(ap, void *); rv = terminal_gettc(el, 3, argv); break; } case EL_GETCFN: *va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read); rv = 0; break; case EL_CLIENTDATA: *va_arg(ap, void **) = el->el_data; rv = 0; break; case EL_UNBUFFERED: *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0; rv = 0; break; case EL_GETFP: { int what; FILE **fpp; what = va_arg(ap, int); fpp = va_arg(ap, FILE **); rv = 0; switch (what) { case 0: *fpp = el->el_infile; break; case 1: *fpp = el->el_outfile; break; case 2: *fpp = el->el_errfile; break; default: rv = -1; break; } break; } default: rv = -1; break; } va_end(ap); return rv; } /* el_line(): * Return editing info */ const LineInfoW * el_wline(EditLine *el) { return (const LineInfoW *)(void *)&el->el_line; } /* el_source(): * Source a file */ int el_source(EditLine *el, const char *fname) { FILE *fp; size_t len; ssize_t slen; char *ptr; char *path = NULL; const wchar_t *dptr; int error = 0; fp = NULL; if (fname == NULL) { #ifdef HAVE_ISSETUGID if (issetugid()) return -1; if ((fname = getenv("EDITRC")) == NULL) { static const char elpath[] = "/.editrc"; size_t plen = sizeof(elpath); if ((ptr = getenv("HOME")) == NULL) return -1; plen += strlen(ptr); if ((path = el_calloc(plen, sizeof(*path))) == NULL) return -1; (void)snprintf(path, plen, "%s%s", ptr, elpath + (*ptr == '\0')); fname = path; } #else /* * If issetugid() is missing, always return an error, in order * to keep from inadvertently opening up the user to a security * hole. */ return -1; #endif } if (fname[0] == '\0') return -1; if (fp == NULL) fp = fopen(fname, "r"); if (fp == NULL) { el_free(path); return -1; } ptr = NULL; len = 0; while ((slen = getline(&ptr, &len, fp)) != -1) { if (*ptr == '\n') continue; /* Empty line. */ if (slen > 0 && ptr[--slen] == '\n') ptr[slen] = '\0'; dptr = ct_decode_string(ptr, &el->el_scratch); if (!dptr) continue; /* loop until first non-space char or EOL */ while (*dptr != '\0' && iswspace(*dptr)) dptr++; if (*dptr == '#') continue; /* ignore, this is a comment line */ if ((error = parse_line(el, dptr)) == -1) break; } free(ptr); el_free(path); (void) fclose(fp); return error; } /* el_resize(): * Called from program when terminal is resized */ void el_resize(EditLine *el) { int lins, cols; sigset_t oset, nset; (void) sigemptyset(&nset); (void) sigaddset(&nset, SIGWINCH); (void) sigprocmask(SIG_BLOCK, &nset, &oset); /* get the correct window size */ if (terminal_get_size(el, &lins, &cols)) terminal_change_size(el, lins, cols); (void) sigprocmask(SIG_SETMASK, &oset, NULL); } /* el_beep(): * Called from the program to beep */ void el_beep(EditLine *el) { terminal_beep(el); } /* el_editmode() * Set the state of EDIT_DISABLED from the `edit' command. */ libedit_private int /*ARGSUSED*/ el_editmode(EditLine *el, int argc, const wchar_t **argv) { const wchar_t *how; if (argv == NULL || argc != 2 || argv[1] == NULL) return -1; how = argv[1]; if (wcscmp(how, L"on") == 0) { el->el_flags &= ~EDIT_DISABLED; tty_rawmode(el); } else if (wcscmp(how, L"off") == 0) { tty_cookedmode(el); el->el_flags |= EDIT_DISABLED; } else { (void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n", how); return -1; } return 0; } diff --git a/contrib/libedit/el.h b/contrib/libedit/el.h index 757137839eb8..a1e9cea11747 100644 --- a/contrib/libedit/el.h +++ b/contrib/libedit/el.h @@ -1,159 +1,160 @@ -/* $NetBSD: el.h,v 1.46 2021/08/15 10:08:41 christos Exp $ */ +/* $NetBSD: el.h,v 1.48 2025/01/03 00:40:08 rillig Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)el.h 8.1 (Berkeley) 6/4/93 */ /* * el.h: Internal structures. */ #ifndef _h_el #define _h_el /* * Local defaults */ #define KSHVI #define VIDEFAULT #define ANCHOR #include "histedit.h" #include "chartype.h" #define EL_BUFSIZ ((size_t)1024) /* Maximum line size */ #define HANDLE_SIGNALS 0x001 #define NO_TTY 0x002 #define EDIT_DISABLED 0x004 #define UNBUFFERED 0x008 #define NARROW_HISTORY 0x040 #define NO_RESET 0x080 #define FIXIO 0x100 +#define FROM_ELLINE 0x200 typedef unsigned char el_action_t; /* Index to command array */ typedef struct coord_t { /* Position on the screen */ int h; int v; } coord_t; typedef struct el_line_t { wchar_t *buffer; /* Input line */ wchar_t *cursor; /* Cursor position */ wchar_t *lastchar; /* Last character */ const wchar_t *limit; /* Max position */ } el_line_t; /* * Editor state */ typedef struct el_state_t { int inputmode; /* What mode are we in? */ int doingarg; /* Are we getting an argument? */ int argument; /* Numeric argument */ int metanext; /* Is the next char a meta char */ el_action_t lastcmd; /* Previous command */ el_action_t thiscmd; /* this command */ wchar_t thisch; /* char that generated it */ } el_state_t; /* * Until we come up with something better... */ #define el_malloc(a) malloc(a) #define el_calloc(a,b) calloc(a, b) #define el_realloc(a,b) realloc(a, b) #define el_free(a) free(a) #include "tty.h" #include "prompt.h" #include "literal.h" #include "keymacro.h" #include "terminal.h" #include "refresh.h" #include "chared.h" #include "search.h" #include "hist.h" #include "map.h" #include "sig.h" struct el_read_t; struct editline { wchar_t *el_prog; /* the program name */ FILE *el_infile; /* Stdio stuff */ FILE *el_outfile; /* Stdio stuff */ FILE *el_errfile; /* Stdio stuff */ int el_infd; /* Input file descriptor */ int el_outfd; /* Output file descriptor */ int el_errfd; /* Error file descriptor */ int el_flags; /* Various flags. */ coord_t el_cursor; /* Cursor location */ wint_t **el_display; /* Real screen image = what is there */ wint_t **el_vdisplay; /* Virtual screen image = what we see */ void *el_data; /* Client data */ el_line_t el_line; /* The current line information */ el_state_t el_state; /* Current editor state */ el_terminal_t el_terminal; /* Terminal dependent stuff */ el_tty_t el_tty; /* Tty dependent stuff */ el_refresh_t el_refresh; /* Refresh stuff */ el_prompt_t el_prompt; /* Prompt stuff */ el_prompt_t el_rprompt; /* Prompt stuff */ el_literal_t el_literal; /* prompt literal bits */ el_chared_t el_chared; /* Characted editor stuff */ el_map_t el_map; /* Key mapping stuff */ el_keymacro_t el_keymacro; /* Key binding stuff */ el_history_t el_history; /* History stuff */ el_search_t el_search; /* Search stuff */ el_signal_t el_signal; /* Signal handling stuff */ struct el_read_t *el_read; /* Character reading stuff */ ct_buffer_t el_visual; /* Buffer for displayable str */ ct_buffer_t el_scratch; /* Scratch conversion buffer */ ct_buffer_t el_lgcyconv; /* Buffer for legacy wrappers */ LineInfo el_lgcylinfo; /* Legacy LineInfo buffer */ }; libedit_private int el_editmode(EditLine *, int, const wchar_t **); libedit_private EditLine *el_init_internal(const char *, FILE *, FILE *, FILE *, int, int, int, int); #ifdef DEBUG #define EL_ABORT(a) do { \ fprintf(el->el_errfile, "%s, %d: ", \ __FILE__, __LINE__); \ fprintf a; \ abort(); \ - } while( /*CONSTCOND*/0); + } while (0) #else #define EL_ABORT(a) abort() #endif #endif /* _h_el */ diff --git a/contrib/libedit/eln.c b/contrib/libedit/eln.c index 563ec2a672a9..660e1988cc26 100644 --- a/contrib/libedit/eln.c +++ b/contrib/libedit/eln.c @@ -1,394 +1,402 @@ -/* $NetBSD: eln.c,v 1.37 2022/01/11 18:30:15 christos Exp $ */ +/* $NetBSD: eln.c,v 1.38 2024/05/17 02:59:08 christos Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) -__RCSID("$NetBSD: eln.c,v 1.37 2022/01/11 18:30:15 christos Exp $"); +__RCSID("$NetBSD: eln.c,v 1.38 2024/05/17 02:59:08 christos Exp $"); #endif /* not lint && not SCCSID */ #include #include #include #include #include "el.h" int el_getc(EditLine *el, char *cp) { int num_read; wchar_t wc = 0; num_read = el_wgetc(el, &wc); *cp = '\0'; if (num_read <= 0) return num_read; num_read = wctob(wc); if (num_read == EOF) { errno = ERANGE; return -1; } else { *cp = (char)num_read; return 1; } } void el_push(EditLine *el, const char *str) { /* Using multibyte->wide string decoding works fine under single-byte * character sets too, and Does The Right Thing. */ el_wpush(el, ct_decode_string(str, &el->el_lgcyconv)); } const char * el_gets(EditLine *el, int *nread) { const wchar_t *tmp; tmp = el_wgets(el, nread); if (tmp != NULL) { int i; size_t nwread = 0; for (i = 0; i < *nread; i++) nwread += ct_enc_width(tmp[i]); *nread = (int)nwread; } return ct_encode_string(tmp, &el->el_lgcyconv); } int el_parse(EditLine *el, int argc, const char *argv[]) { int ret; const wchar_t **wargv; wargv = (void *)ct_decode_argv(argc, argv, &el->el_lgcyconv); if (!wargv) return -1; ret = el_wparse(el, argc, wargv); el_free(wargv); return ret; } int el_set(EditLine *el, int op, ...) { va_list ap; int ret; if (!el) return -1; va_start(ap, op); switch (op) { case EL_PROMPT: /* el_pfunc_t */ case EL_RPROMPT: { el_pfunc_t p = va_arg(ap, el_pfunc_t); ret = prompt_set(el, p, 0, op, 0); break; } case EL_RESIZE: { el_zfunc_t p = va_arg(ap, el_zfunc_t); void *arg = va_arg(ap, void *); ret = ch_resizefun(el, p, arg); break; } case EL_ALIAS_TEXT: { el_afunc_t p = va_arg(ap, el_afunc_t); void *arg = va_arg(ap, void *); ret = ch_aliasfun(el, p, arg); break; } case EL_PROMPT_ESC: case EL_RPROMPT_ESC: { el_pfunc_t p = va_arg(ap, el_pfunc_t); int c = va_arg(ap, int); ret = prompt_set(el, p, c, op, 0); break; } case EL_TERMINAL: /* const char * */ ret = el_wset(el, op, va_arg(ap, char *)); break; case EL_EDITOR: /* const wchar_t * */ ret = el_wset(el, op, ct_decode_string(va_arg(ap, char *), &el->el_lgcyconv)); break; case EL_SIGNAL: /* int */ case EL_EDITMODE: case EL_SAFEREAD: case EL_UNBUFFERED: case EL_PREP_TERM: ret = el_wset(el, op, va_arg(ap, int)); break; case EL_BIND: /* const char * list -> const wchar_t * list */ case EL_TELLTC: case EL_SETTC: case EL_ECHOTC: case EL_SETTY: { const char *argv[20]; int i; const wchar_t **wargv; for (i = 1; i < (int)__arraycount(argv) - 1; ++i) if ((argv[i] = va_arg(ap, const char *)) == NULL) break; argv[0] = argv[i] = NULL; wargv = (void *)ct_decode_argv(i + 1, argv, &el->el_lgcyconv); if (!wargv) { ret = -1; goto out; } /* * AFAIK we can't portably pass through our new wargv to * el_wset(), so we have to reimplement the body of * el_wset() for these ops. */ switch (op) { case EL_BIND: wargv[0] = L"bind"; ret = map_bind(el, i, wargv); break; case EL_TELLTC: wargv[0] = L"telltc"; ret = terminal_telltc(el, i, wargv); break; case EL_SETTC: wargv[0] = L"settc"; ret = terminal_settc(el, i, wargv); break; case EL_ECHOTC: wargv[0] = L"echotc"; ret = terminal_echotc(el, i, wargv); break; case EL_SETTY: wargv[0] = L"setty"; ret = tty_stty(el, i, wargv); break; default: ret = -1; } el_free(wargv); break; } /* XXX: do we need to change el_func_t too? */ case EL_ADDFN: { /* const char *, const char *, el_func_t */ const char *args[2]; el_func_t func; wchar_t **wargv; args[0] = va_arg(ap, const char *); args[1] = va_arg(ap, const char *); func = va_arg(ap, el_func_t); wargv = ct_decode_argv(2, args, &el->el_lgcyconv); if (!wargv) { ret = -1; goto out; } /* XXX: The two strdup's leak */ ret = map_addfunc(el, wcsdup(wargv[0]), wcsdup(wargv[1]), func); el_free(wargv); break; } case EL_HIST: { /* hist_fun_t, const char * */ hist_fun_t fun = va_arg(ap, hist_fun_t); void *ptr = va_arg(ap, void *); ret = hist_set(el, fun, ptr); el->el_flags |= NARROW_HISTORY; break; } case EL_GETCFN: /* el_rfunc_t */ ret = el_wset(el, op, va_arg(ap, el_rfunc_t)); break; case EL_CLIENTDATA: /* void * */ ret = el_wset(el, op, va_arg(ap, void *)); break; case EL_SETFP: { /* int, FILE * */ int what = va_arg(ap, int); FILE *fp = va_arg(ap, FILE *); ret = el_wset(el, op, what, fp); break; } case EL_REFRESH: re_clear_display(el); re_refresh(el); terminal__flush(el); ret = 0; break; default: ret = -1; break; } out: va_end(ap); return ret; } int el_get(EditLine *el, int op, ...) { va_list ap; int ret; if (!el) return -1; va_start(ap, op); switch (op) { case EL_PROMPT: /* el_pfunc_t * */ case EL_RPROMPT: { el_pfunc_t *p = va_arg(ap, el_pfunc_t *); ret = prompt_get(el, p, 0, op); break; } case EL_PROMPT_ESC: /* el_pfunc_t *, char **/ case EL_RPROMPT_ESC: { el_pfunc_t *p = va_arg(ap, el_pfunc_t *); char *c = va_arg(ap, char *); wchar_t wc = 0; ret = prompt_get(el, p, &wc, op); *c = (char)wc; break; } case EL_EDITOR: { const char **p = va_arg(ap, const char **); const wchar_t *pw; ret = el_wget(el, op, &pw); *p = ct_encode_string(pw, &el->el_lgcyconv); if (!el->el_lgcyconv.csize) ret = -1; break; } case EL_TERMINAL: /* const char ** */ ret = el_wget(el, op, va_arg(ap, const char **)); break; case EL_SIGNAL: /* int * */ case EL_EDITMODE: case EL_SAFEREAD: case EL_UNBUFFERED: case EL_PREP_TERM: ret = el_wget(el, op, va_arg(ap, int *)); break; case EL_GETTC: { char *argv[3]; static char gettc[] = "gettc"; argv[0] = gettc; argv[1] = va_arg(ap, char *); argv[2] = va_arg(ap, void *); ret = terminal_gettc(el, 3, argv); break; } case EL_GETCFN: /* el_rfunc_t */ ret = el_wget(el, op, va_arg(ap, el_rfunc_t *)); break; case EL_CLIENTDATA: /* void ** */ ret = el_wget(el, op, va_arg(ap, void **)); break; case EL_GETFP: { /* int, FILE ** */ int what = va_arg(ap, int); FILE **fpp = va_arg(ap, FILE **); ret = el_wget(el, op, what, fpp); break; } default: ret = -1; break; } va_end(ap); return ret; } const LineInfo * el_line(EditLine *el) { const LineInfoW *winfo = el_wline(el); LineInfo *info = &el->el_lgcylinfo; size_t offset; const wchar_t *p; + if (el->el_flags & FROM_ELLINE) + return info; + + el->el_flags |= FROM_ELLINE; info->buffer = ct_encode_string(winfo->buffer, &el->el_lgcyconv); offset = 0; for (p = winfo->buffer; p < winfo->cursor; p++) offset += ct_enc_width(*p); info->cursor = info->buffer + offset; offset = 0; for (p = winfo->buffer; p < winfo->lastchar; p++) offset += ct_enc_width(*p); info->lastchar = info->buffer + offset; + if (el->el_chared.c_resizefun) + (*el->el_chared.c_resizefun)(el, el->el_chared.c_resizearg); + el->el_flags &= ~FROM_ELLINE; + return info; } int el_insertstr(EditLine *el, const char *str) { return el_winsertstr(el, ct_decode_string(str, &el->el_lgcyconv)); } int el_replacestr(EditLine *el, const char *str) { return el_wreplacestr(el, ct_decode_string(str, &el->el_lgcyconv)); } diff --git a/contrib/libedit/emacs.c b/contrib/libedit/emacs.c index 0636c28b26fc..19a9cf191168 100644 --- a/contrib/libedit/emacs.c +++ b/contrib/libedit/emacs.c @@ -1,512 +1,512 @@ -/* $NetBSD: emacs.c,v 1.36 2016/05/09 21:46:56 christos Exp $ */ +/* $NetBSD: emacs.c,v 1.38 2024/06/29 17:28:07 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)emacs.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: emacs.c,v 1.36 2016/05/09 21:46:56 christos Exp $"); +__RCSID("$NetBSD: emacs.c,v 1.38 2024/06/29 17:28:07 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * emacs.c: Emacs functions */ #include #include "el.h" #include "emacs.h" #include "fcns.h" /* em_delete_or_list(): * Delete character under cursor or list completions if at end of line * [^D] */ libedit_private el_action_t /*ARGSUSED*/ em_delete_or_list(EditLine *el, wint_t c) { if (el->el_line.cursor == el->el_line.lastchar) { /* if I'm at the end */ if (el->el_line.cursor == el->el_line.buffer) { /* and the beginning */ terminal_writec(el, c); /* then do an EOF */ return CC_EOF; } else { /* * Here we could list completions, but it is an * error right now */ terminal_beep(el); return CC_ERROR; } } else { if (el->el_state.doingarg) c_delafter(el, el->el_state.argument); else c_delafter1(el); if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; /* bounds check */ return CC_REFRESH; } } /* em_delete_next_word(): * Cut from cursor to end of current word * [M-d] */ libedit_private el_action_t /*ARGSUSED*/ em_delete_next_word(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *cp, *p, *kp; if (el->el_line.cursor == el->el_line.lastchar) return CC_ERROR; cp = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++) /* save the text */ *kp++ = *p; el->el_chared.c_kill.last = kp; c_delafter(el, (int)(cp - el->el_line.cursor)); /* delete after dot */ if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; /* bounds check */ return CC_REFRESH; } /* em_yank(): * Paste cut buffer at cursor position * [^Y] */ libedit_private el_action_t /*ARGSUSED*/ em_yank(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *kp, *cp; if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf) return CC_NORM; if (el->el_line.lastchar + (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >= el->el_line.limit) return CC_ERROR; el->el_chared.c_kill.mark = el->el_line.cursor; - cp = el->el_line.cursor; /* open the space, */ c_insert(el, (int)(el->el_chared.c_kill.last - el->el_chared.c_kill.buf)); + cp = el->el_line.cursor; /* copy the chars */ for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++) *cp++ = *kp; /* if an arg, cursor at beginning else cursor at end */ if (el->el_state.argument == 1) el->el_line.cursor = cp; return CC_REFRESH; } /* em_kill_line(): * Cut the entire line and save in cut buffer * [^U] */ libedit_private el_action_t /*ARGSUSED*/ em_kill_line(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *kp, *cp; cp = el->el_line.buffer; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.lastchar) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; /* zap! -- delete all of it */ el->el_line.lastchar = el->el_line.buffer; el->el_line.cursor = el->el_line.buffer; return CC_REFRESH; } /* em_kill_region(): * Cut area between mark and cursor and save in cut buffer * [^W] */ libedit_private el_action_t /*ARGSUSED*/ em_kill_region(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *kp, *cp; if (!el->el_chared.c_kill.mark) return CC_ERROR; if (el->el_chared.c_kill.mark > el->el_line.cursor) { cp = el->el_line.cursor; kp = el->el_chared.c_kill.buf; while (cp < el->el_chared.c_kill.mark) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; c_delafter(el, (int)(cp - el->el_line.cursor)); } else { /* mark is before cursor */ cp = el->el_chared.c_kill.mark; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.cursor) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; c_delbefore(el, (int)(cp - el->el_chared.c_kill.mark)); el->el_line.cursor = el->el_chared.c_kill.mark; } return CC_REFRESH; } /* em_copy_region(): * Copy area between mark and cursor to cut buffer * [M-W] */ libedit_private el_action_t /*ARGSUSED*/ em_copy_region(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *kp, *cp; if (!el->el_chared.c_kill.mark) return CC_ERROR; if (el->el_chared.c_kill.mark > el->el_line.cursor) { cp = el->el_line.cursor; kp = el->el_chared.c_kill.buf; while (cp < el->el_chared.c_kill.mark) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; } else { cp = el->el_chared.c_kill.mark; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.cursor) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; } return CC_NORM; } /* em_gosmacs_transpose(): * Exchange the two characters before the cursor * Gosling emacs transpose chars [^T] */ libedit_private el_action_t em_gosmacs_transpose(EditLine *el, wint_t c) { if (el->el_line.cursor > &el->el_line.buffer[1]) { /* must have at least two chars entered */ c = el->el_line.cursor[-2]; el->el_line.cursor[-2] = el->el_line.cursor[-1]; el->el_line.cursor[-1] = c; return CC_REFRESH; } else return CC_ERROR; } /* em_next_word(): * Move next to end of current word * [M-f] */ libedit_private el_action_t /*ARGSUSED*/ em_next_word(EditLine *el, wint_t c __attribute__((__unused__))) { if (el->el_line.cursor == el->el_line.lastchar) return CC_ERROR; el->el_line.cursor = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return CC_REFRESH; } return CC_CURSOR; } /* em_upper_case(): * Uppercase the characters from cursor to end of current word * [M-u] */ libedit_private el_action_t /*ARGSUSED*/ em_upper_case(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *cp, *ep; ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); for (cp = el->el_line.cursor; cp < ep; cp++) if (iswlower(*cp)) *cp = towupper(*cp); el->el_line.cursor = ep; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; return CC_REFRESH; } /* em_capitol_case(): * Capitalize the characters from cursor to end of current word * [M-c] */ libedit_private el_action_t /*ARGSUSED*/ em_capitol_case(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *cp, *ep; ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); for (cp = el->el_line.cursor; cp < ep; cp++) { if (iswalpha(*cp)) { if (iswlower(*cp)) *cp = towupper(*cp); cp++; break; } } for (; cp < ep; cp++) if (iswupper(*cp)) *cp = towlower(*cp); el->el_line.cursor = ep; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; return CC_REFRESH; } /* em_lower_case(): * Lowercase the characters from cursor to end of current word * [M-l] */ libedit_private el_action_t /*ARGSUSED*/ em_lower_case(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *cp, *ep; ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); for (cp = el->el_line.cursor; cp < ep; cp++) if (iswupper(*cp)) *cp = towlower(*cp); el->el_line.cursor = ep; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; return CC_REFRESH; } /* em_set_mark(): * Set the mark at cursor * [^@] */ libedit_private el_action_t /*ARGSUSED*/ em_set_mark(EditLine *el, wint_t c __attribute__((__unused__))) { el->el_chared.c_kill.mark = el->el_line.cursor; return CC_NORM; } /* em_exchange_mark(): * Exchange the cursor and mark * [^X^X] */ libedit_private el_action_t /*ARGSUSED*/ em_exchange_mark(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *cp; cp = el->el_line.cursor; el->el_line.cursor = el->el_chared.c_kill.mark; el->el_chared.c_kill.mark = cp; return CC_CURSOR; } /* em_universal_argument(): * Universal argument (argument times 4) * [^U] */ libedit_private el_action_t /*ARGSUSED*/ em_universal_argument(EditLine *el, wint_t c __attribute__((__unused__))) { /* multiply current argument by 4 */ if (el->el_state.argument > 1000000) return CC_ERROR; el->el_state.doingarg = 1; el->el_state.argument *= 4; return CC_ARGHACK; } /* em_meta_next(): * Add 8th bit to next character typed * [] */ libedit_private el_action_t /*ARGSUSED*/ em_meta_next(EditLine *el, wint_t c __attribute__((__unused__))) { el->el_state.metanext = 1; return CC_ARGHACK; } /* em_toggle_overwrite(): * Switch from insert to overwrite mode or vice versa */ libedit_private el_action_t /*ARGSUSED*/ em_toggle_overwrite(EditLine *el, wint_t c __attribute__((__unused__))) { el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ? MODE_REPLACE : MODE_INSERT; return CC_NORM; } /* em_copy_prev_word(): * Copy current word to cursor */ libedit_private el_action_t /*ARGSUSED*/ em_copy_prev_word(EditLine *el, wint_t c __attribute__((__unused__))) { wchar_t *cp, *oldc, *dp; if (el->el_line.cursor == el->el_line.buffer) return CC_ERROR; - oldc = el->el_line.cursor; /* does a bounds check */ cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, el->el_state.argument, ce__isword); - c_insert(el, (int)(oldc - cp)); + c_insert(el, (int)(el->el_line.cursor - cp)); + oldc = el->el_line.cursor; for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++) *dp++ = *cp; el->el_line.cursor = dp;/* put cursor at end */ return CC_REFRESH; } /* em_inc_search_next(): * Emacs incremental next search */ libedit_private el_action_t /*ARGSUSED*/ em_inc_search_next(EditLine *el, wint_t c __attribute__((__unused__))) { el->el_search.patlen = 0; return ce_inc_search(el, ED_SEARCH_NEXT_HISTORY); } /* em_inc_search_prev(): * Emacs incremental reverse search */ libedit_private el_action_t /*ARGSUSED*/ em_inc_search_prev(EditLine *el, wint_t c __attribute__((__unused__))) { el->el_search.patlen = 0; return ce_inc_search(el, ED_SEARCH_PREV_HISTORY); } /* em_delete_prev_char(): * Delete the character to the left of the cursor * [^?] */ libedit_private el_action_t /*ARGSUSED*/ em_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) { if (el->el_line.cursor <= el->el_line.buffer) return CC_ERROR; if (el->el_state.doingarg) c_delbefore(el, el->el_state.argument); else c_delbefore1(el); el->el_line.cursor -= el->el_state.argument; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; return CC_REFRESH; } diff --git a/contrib/libedit/filecomplete.c b/contrib/libedit/filecomplete.c index ee017fb406d5..a2001a4be955 100644 --- a/contrib/libedit/filecomplete.c +++ b/contrib/libedit/filecomplete.c @@ -1,863 +1,863 @@ -/* $NetBSD: filecomplete.c,v 1.72 2023/02/03 22:01:42 christos Exp $ */ +/* $NetBSD: filecomplete.c,v 1.73 2023/04/25 17:51:32 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) -__RCSID("$NetBSD: filecomplete.c,v 1.72 2023/02/03 22:01:42 christos Exp $"); +__RCSID("$NetBSD: filecomplete.c,v 1.73 2023/04/25 17:51:32 christos Exp $"); #endif /* not lint && not SCCSID */ #include #include #include #include #include #include #include #include #include #include #include #include "el.h" #include "filecomplete.h" static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{("; /********************************/ /* completion functions */ /* * does tilde expansion of strings of type ``~user/foo'' * if ``user'' isn't valid user name or ``txt'' doesn't start * w/ '~', returns pointer to strdup()ed copy of ``txt'' * * it's the caller's responsibility to free() the returned string */ char * fn_tilde_expand(const char *txt) { #if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) struct passwd pwres; char pwbuf[1024]; #endif struct passwd *pass; const char *pos; char *temp; size_t len = 0; if (txt[0] != '~') return strdup(txt); pos = strchr(txt + 1, '/'); if (pos == NULL) { temp = strdup(txt + 1); if (temp == NULL) return NULL; } else { /* text until string after slash */ len = (size_t)(pos - txt + 1); temp = el_calloc(len, sizeof(*temp)); if (temp == NULL) return NULL; (void)strlcpy(temp, txt + 1, len - 1); } if (temp[0] == 0) { #ifdef HAVE_GETPW_R_POSIX if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) pass = NULL; #elif HAVE_GETPW_R_DRAFT pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf)); #else pass = getpwuid(getuid()); #endif } else { #ifdef HAVE_GETPW_R_POSIX if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) pass = NULL; #elif HAVE_GETPW_R_DRAFT pass = getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf)); #else pass = getpwnam(temp); #endif } el_free(temp); /* value no more needed */ if (pass == NULL) return strdup(txt); /* update pointer txt to point at string immedially following */ /* first slash */ txt += len; len = strlen(pass->pw_dir) + 1 + strlen(txt) + 1; temp = el_calloc(len, sizeof(*temp)); if (temp == NULL) return NULL; (void)snprintf(temp, len, "%s/%s", pass->pw_dir, txt); return temp; } static int needs_escaping(wchar_t c) { switch (c) { case '\'': case '"': case '(': case ')': case '\\': case '<': case '>': case '$': case '#': case ' ': case '\n': case '\t': case '?': case ';': case '`': case '@': case '=': case '|': case '{': case '}': case '&': case '*': case '[': return 1; default: return 0; } } static int needs_dquote_escaping(char c) { switch (c) { case '"': case '\\': case '`': case '$': return 1; default: return 0; } } static wchar_t * unescape_string(const wchar_t *string, size_t length) { size_t i; size_t j = 0; wchar_t *unescaped = el_calloc(length + 1, sizeof(*string)); if (unescaped == NULL) return NULL; for (i = 0; i < length ; i++) { if (string[i] == '\\') continue; unescaped[j++] = string[i]; } unescaped[j] = 0; return unescaped; } static char * escape_filename(EditLine * el, const char *filename, int single_match, const char *(*app_func)(const char *)) { size_t original_len = 0; size_t escaped_character_count = 0; size_t offset = 0; size_t newlen; const char *s; char c; size_t s_quoted = 0; /* does the input contain a single quote */ size_t d_quoted = 0; /* does the input contain a double quote */ char *escaped_str; wchar_t *temp = el->el_line.buffer; const char *append_char = NULL; if (filename == NULL) return NULL; while (temp != el->el_line.cursor) { /* * If we see a single quote but have not seen a double quote * so far set/unset s_quote, unless it is already quoted */ if (temp[0] == '\'' && !d_quoted && (temp == el->el_line.buffer || temp[-1] != '\\')) s_quoted = !s_quoted; /* * vice versa to the above condition */ else if (temp[0] == '"' && !s_quoted) d_quoted = !d_quoted; temp++; } /* Count number of special characters so that we can calculate * number of extra bytes needed in the new string */ for (s = filename; *s; s++, original_len++) { c = *s; /* Inside a single quote only single quotes need escaping */ if (s_quoted && c == '\'') { escaped_character_count += 3; continue; } /* Inside double quotes only ", \, ` and $ need escaping */ if (d_quoted && needs_dquote_escaping(c)) { escaped_character_count++; continue; } if (!s_quoted && !d_quoted && needs_escaping(c)) escaped_character_count++; } newlen = original_len + escaped_character_count + 1; if (s_quoted || d_quoted) newlen++; if (single_match && app_func) newlen++; if ((escaped_str = el_malloc(newlen)) == NULL) return NULL; for (s = filename; *s; s++) { c = *s; if (!needs_escaping(c)) { /* no escaping is required continue as usual */ escaped_str[offset++] = c; continue; } /* single quotes inside single quotes require special handling */ if (c == '\'' && s_quoted) { escaped_str[offset++] = '\''; escaped_str[offset++] = '\\'; escaped_str[offset++] = '\''; escaped_str[offset++] = '\''; continue; } /* Otherwise no escaping needed inside single quotes */ if (s_quoted) { escaped_str[offset++] = c; continue; } /* No escaping needed inside a double quoted string either * unless we see a '$', '\', '`', or '"' (itself) */ if (d_quoted && !needs_dquote_escaping(c)) { escaped_str[offset++] = c; continue; } /* If we reach here that means escaping is actually needed */ escaped_str[offset++] = '\\'; escaped_str[offset++] = c; } if (single_match && app_func) { escaped_str[offset] = 0; append_char = app_func(filename); /* we want to append space only if we are not inside quotes */ if (append_char[0] == ' ') { if (!s_quoted && !d_quoted) escaped_str[offset++] = append_char[0]; } else escaped_str[offset++] = append_char[0]; } /* close the quotes if single match and the match is not a directory */ if (single_match && (append_char && append_char[0] == ' ')) { if (s_quoted) escaped_str[offset++] = '\''; else if (d_quoted) escaped_str[offset++] = '"'; } escaped_str[offset] = 0; return escaped_str; } /* * return first found file name starting by the ``text'' or NULL if no * such file can be found * value of ``state'' is ignored * * it's the caller's responsibility to free the returned string */ char * fn_filename_completion_function(const char *text, int state) { static DIR *dir = NULL; static char *filename = NULL, *dirname = NULL, *dirpath = NULL; static size_t filename_len = 0; struct dirent *entry; char *temp; const char *pos; size_t len; if (state == 0 || dir == NULL) { pos = strrchr(text, '/'); if (pos) { char *nptr; pos++; nptr = el_realloc(filename, (strlen(pos) + 1) * sizeof(*nptr)); if (nptr == NULL) { el_free(filename); filename = NULL; return NULL; } filename = nptr; (void)strcpy(filename, pos); len = (size_t)(pos - text); /* including last slash */ nptr = el_realloc(dirname, (len + 1) * sizeof(*nptr)); if (nptr == NULL) { el_free(dirname); dirname = NULL; return NULL; } dirname = nptr; (void)strlcpy(dirname, text, len + 1); } else { el_free(filename); if (*text == 0) filename = NULL; else { filename = strdup(text); if (filename == NULL) return NULL; } el_free(dirname); dirname = NULL; } if (dir != NULL) { (void)closedir(dir); dir = NULL; } /* support for ``~user'' syntax */ el_free(dirpath); dirpath = NULL; if (dirname == NULL) { if ((dirname = strdup("")) == NULL) return NULL; dirpath = strdup("./"); } else if (*dirname == '~') dirpath = fn_tilde_expand(dirname); else dirpath = strdup(dirname); if (dirpath == NULL) return NULL; dir = opendir(dirpath); if (!dir) return NULL; /* cannot open the directory */ /* will be used in cycle */ filename_len = filename ? strlen(filename) : 0; } /* find the match */ while ((entry = readdir(dir)) != NULL) { /* skip . and .. */ if (entry->d_name[0] == '.' && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))) continue; if (filename_len == 0) break; /* otherwise, get first entry where first */ /* filename_len characters are equal */ if (entry->d_name[0] == filename[0] #if HAVE_STRUCT_DIRENT_D_NAMLEN && entry->d_namlen >= filename_len #else && strlen(entry->d_name) >= filename_len #endif && strncmp(entry->d_name, filename, filename_len) == 0) break; } if (entry) { /* match found */ #if HAVE_STRUCT_DIRENT_D_NAMLEN len = entry->d_namlen; #else len = strlen(entry->d_name); #endif len = strlen(dirname) + len + 1; temp = el_calloc(len, sizeof(*temp)); if (temp == NULL) return NULL; (void)snprintf(temp, len, "%s%s", dirname, entry->d_name); } else { (void)closedir(dir); dir = NULL; temp = NULL; } return temp; } static const char * append_char_function(const char *name) { struct stat stbuf; char *expname = *name == '~' ? fn_tilde_expand(name) : NULL; const char *rs = " "; if (stat(expname ? expname : name, &stbuf) == -1) goto out; if (S_ISDIR(stbuf.st_mode)) rs = "/"; out: if (expname) el_free(expname); return rs; } + /* * returns list of completions for text given * non-static for readline. */ -char ** completion_matches(const char *, char *(*)(const char *, int)); char ** completion_matches(const char *text, char *(*genfunc)(const char *, int)) { char **match_list = NULL, *retstr, *prevstr; size_t match_list_len, max_equal, which, i; size_t matches; matches = 0; match_list_len = 1; while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { /* allow for list terminator here */ if (matches + 3 >= match_list_len) { char **nmatch_list; while (matches + 3 >= match_list_len) match_list_len <<= 1; nmatch_list = el_realloc(match_list, match_list_len * sizeof(*nmatch_list)); if (nmatch_list == NULL) { el_free(match_list); return NULL; } match_list = nmatch_list; } match_list[++matches] = retstr; } if (!match_list) return NULL; /* nothing found */ /* find least denominator and insert it to match_list[0] */ which = 2; prevstr = match_list[1]; max_equal = strlen(prevstr); for (; which <= matches; which++) { for (i = 0; i < max_equal && prevstr[i] == match_list[which][i]; i++) continue; max_equal = i; } retstr = el_calloc(max_equal + 1, sizeof(*retstr)); if (retstr == NULL) { el_free(match_list); return NULL; } (void)strlcpy(retstr, match_list[1], max_equal + 1); match_list[0] = retstr; /* add NULL as last pointer to the array */ match_list[matches + 1] = NULL; return match_list; } /* * Sort function for qsort(). Just wrapper around strcasecmp(). */ static int _fn_qsort_string_compare(const void *i1, const void *i2) { const char *s1 = ((const char * const *)i1)[0]; const char *s2 = ((const char * const *)i2)[0]; return strcasecmp(s1, s2); } /* * Display list of strings in columnar format on readline's output stream. * 'matches' is list of strings, 'num' is number of strings in 'matches', * 'width' is maximum length of string in 'matches'. * * matches[0] is not one of the match strings, but it is counted in * num, so the strings are matches[1] *through* matches[num-1]. */ void fn_display_match_list(EditLine * el, char **matches, size_t num, size_t width, const char *(*app_func) (const char *)) { size_t line, lines, col, cols, thisguy; int screenwidth = el->el_terminal.t_size.h; if (app_func == NULL) app_func = append_char_function; /* Ignore matches[0]. Avoid 1-based array logic below. */ matches++; num--; /* * Find out how many entries can be put on one line; count * with one space between strings the same way it's printed. */ cols = (size_t)screenwidth / (width + 2); if (cols == 0) cols = 1; /* how many lines of output, rounded up */ lines = (num + cols - 1) / cols; /* Sort the items. */ qsort(matches, num, sizeof(char *), _fn_qsort_string_compare); /* * On the ith line print elements i, i+lines, i+lines*2, etc. */ for (line = 0; line < lines; line++) { for (col = 0; col < cols; col++) { thisguy = line + col * lines; if (thisguy >= num) break; (void)fprintf(el->el_outfile, "%s%s%s", col == 0 ? "" : " ", matches[thisguy], (*app_func)(matches[thisguy])); (void)fprintf(el->el_outfile, "%-*s", (int) (width - strlen(matches[thisguy])), ""); } (void)fprintf(el->el_outfile, "\n"); } } static wchar_t * find_word_to_complete(const wchar_t * cursor, const wchar_t * buffer, const wchar_t * word_break, const wchar_t * special_prefixes, size_t * length, int do_unescape) { /* We now look backwards for the start of a filename/variable word */ const wchar_t *ctemp = cursor; wchar_t *temp; size_t len; /* if the cursor is placed at a slash or a quote, we need to find the * word before it */ if (ctemp > buffer) { switch (ctemp[-1]) { case '\\': case '\'': case '"': ctemp--; break; default: break; } } for (;;) { if (ctemp <= buffer) break; if (ctemp - buffer >= 2 && ctemp[-2] == '\\' && needs_escaping(ctemp[-1])) { ctemp -= 2; continue; } if (wcschr(word_break, ctemp[-1])) break; if (special_prefixes && wcschr(special_prefixes, ctemp[-1])) break; ctemp--; } len = (size_t) (cursor - ctemp); if (len == 1 && (ctemp[0] == '\'' || ctemp[0] == '"')) { len = 0; ctemp++; } *length = len; if (do_unescape) { wchar_t *unescaped_word = unescape_string(ctemp, len); if (unescaped_word == NULL) return NULL; return unescaped_word; } temp = el_malloc((len + 1) * sizeof(*temp)); if (temp == NULL) return NULL; (void) wcsncpy(temp, ctemp, len); temp[len] = '\0'; return temp; } /* * Complete the word at or before point, * 'what_to_do' says what to do with the completion. * \t means do standard completion. * `?' means list the possible completions. * `*' means insert all of the possible completions. * `!' means to do standard completion, and list all possible completions if * there is more than one. * * Note: '*' support is not implemented * '!' could never be invoked */ int fn_complete2(EditLine *el, char *(*complete_func)(const char *, int), char **(*attempted_completion_function)(const char *, int, int), const wchar_t *word_break, const wchar_t *special_prefixes, const char *(*app_func)(const char *), size_t query_items, int *completion_type, int *over, int *point, int *end, unsigned int flags) { const LineInfoW *li; wchar_t *temp; char **matches; char *completion; size_t len; int what_to_do = '\t'; int retval = CC_NORM; int do_unescape = flags & FN_QUOTE_MATCH; if (el->el_state.lastcmd == el->el_state.thiscmd) what_to_do = '?'; /* readline's rl_complete() has to be told what we did... */ if (completion_type != NULL) *completion_type = what_to_do; if (!complete_func) complete_func = fn_filename_completion_function; if (!app_func) app_func = append_char_function; li = el_wline(el); temp = find_word_to_complete(li->cursor, li->buffer, word_break, special_prefixes, &len, do_unescape); if (temp == NULL) goto out; /* these can be used by function called in completion_matches() */ /* or (*attempted_completion_function)() */ if (point != NULL) *point = (int)(li->cursor - li->buffer); if (end != NULL) *end = (int)(li->lastchar - li->buffer); if (attempted_completion_function) { int cur_off = (int)(li->cursor - li->buffer); matches = (*attempted_completion_function)( ct_encode_string(temp, &el->el_scratch), cur_off - (int)len, cur_off); } else matches = NULL; if (!attempted_completion_function || (over != NULL && !*over && !matches)) matches = completion_matches( ct_encode_string(temp, &el->el_scratch), complete_func); if (over != NULL) *over = 0; if (matches == NULL) { goto out; } int i; size_t matches_num, maxlen, match_len, match_display=1; int single_match = matches[2] == NULL && (matches[1] == NULL || strcmp(matches[0], matches[1]) == 0); retval = CC_REFRESH; if (matches[0][0] != '\0') { el_deletestr(el, (int)len); if (flags & FN_QUOTE_MATCH) completion = escape_filename(el, matches[0], single_match, app_func); else completion = strdup(matches[0]); if (completion == NULL) goto out2; /* * Replace the completed string with the common part of * all possible matches if there is a possible completion. */ el_winsertstr(el, ct_decode_string(completion, &el->el_scratch)); if (single_match && attempted_completion_function && !(flags & FN_QUOTE_MATCH)) { /* * We found an exact match. Add a space after * it, unless we do filename completion and the * object is a directory. Also do necessary * escape quoting */ el_winsertstr(el, ct_decode_string( (*app_func)(completion), &el->el_scratch)); } free(completion); } if (!single_match && (what_to_do == '!' || what_to_do == '?')) { /* * More than one match and requested to list possible * matches. */ for(i = 1, maxlen = 0; matches[i]; i++) { match_len = strlen(matches[i]); if (match_len > maxlen) maxlen = match_len; } /* matches[1] through matches[i-1] are available */ matches_num = (size_t)(i - 1); /* newline to get on next line from command line */ (void)fprintf(el->el_outfile, "\n"); /* * If there are too many items, ask user for display * confirmation. */ if (matches_num > query_items) { (void)fprintf(el->el_outfile, "Display all %zu possibilities? (y or n) ", matches_num); (void)fflush(el->el_outfile); if (getc(stdin) != 'y') match_display = 0; (void)fprintf(el->el_outfile, "\n"); } if (match_display) { /* * Interface of this function requires the * strings be matches[1..num-1] for compat. * We have matches_num strings not counting * the prefix in matches[0], so we need to * add 1 to matches_num for the call. */ fn_display_match_list(el, matches, matches_num+1, maxlen, app_func); } retval = CC_REDISPLAY; } else if (matches[0][0]) { /* * There was some common match, but the name was * not complete enough. Next tab will print possible * completions. */ el_beep(el); } else { /* lcd is not a valid object - further specification */ /* is needed */ el_beep(el); retval = CC_NORM; } /* free elements of array and the array itself */ out2: for (i = 0; matches[i]; i++) el_free(matches[i]); el_free(matches); matches = NULL; out: el_free(temp); return retval; } int fn_complete(EditLine *el, char *(*complete_func)(const char *, int), char **(*attempted_completion_function)(const char *, int, int), const wchar_t *word_break, const wchar_t *special_prefixes, const char *(*app_func)(const char *), size_t query_items, int *completion_type, int *over, int *point, int *end) { return fn_complete2(el, complete_func, attempted_completion_function, word_break, special_prefixes, app_func, query_items, completion_type, over, point, end, attempted_completion_function ? 0 : FN_QUOTE_MATCH); } /* * el-compatible wrapper around rl_complete; needed for key binding */ /* ARGSUSED */ unsigned char _el_fn_complete(EditLine *el, int ch __attribute__((__unused__))) { return (unsigned char)fn_complete(el, NULL, NULL, break_chars, NULL, NULL, (size_t)100, NULL, NULL, NULL, NULL); } /* * el-compatible wrapper around rl_complete; needed for key binding */ /* ARGSUSED */ unsigned char _el_fn_sh_complete(EditLine *el, int ch) { return _el_fn_complete(el, ch); } diff --git a/contrib/libedit/filecomplete.h b/contrib/libedit/filecomplete.h index 796ae7ab3276..884586c438f7 100644 --- a/contrib/libedit/filecomplete.h +++ b/contrib/libedit/filecomplete.h @@ -1,51 +1,54 @@ -/* $NetBSD: filecomplete.h,v 1.14 2021/09/26 13:45:54 christos Exp $ */ +/* $NetBSD: filecomplete.h,v 1.15 2023/04/25 17:51:32 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _FILECOMPLETE_H_ #define _FILECOMPLETE_H_ int fn_complete(EditLine *, char *(*)(const char *, int), char **(*)(const char *, int, int), const wchar_t *, const wchar_t *, const char *(*)(const char *), size_t, int *, int *, int *, int *); int fn_complete2(EditLine *, char *(*)(const char *, int), char **(*)(const char *, int, int), const wchar_t *, const wchar_t *, const char *(*)(const char *), size_t, int *, int *, int *, int *, unsigned int); #define FN_QUOTE_MATCH 1U /* Quote the returned match */ void fn_display_match_list(EditLine *, char **, size_t, size_t, const char *(*)(const char *)); char *fn_tilde_expand(const char *); char *fn_filename_completion_function(const char *, int); +/* XXX: readline */ +char **completion_matches(const char *, char *(*)(const char *, int)); + #endif diff --git a/contrib/libedit/history.c b/contrib/libedit/history.c index 73cfa6b8ead9..8395b329784d 100644 --- a/contrib/libedit/history.c +++ b/contrib/libedit/history.c @@ -1,1180 +1,1183 @@ -/* $NetBSD: history.c,v 1.63 2019/10/08 19:17:57 christos Exp $ */ +/* $NetBSD: history.c,v 1.64 2024/07/11 05:41:24 kre Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: history.c,v 1.63 2019/10/08 19:17:57 christos Exp $"); +__RCSID("$NetBSD: history.c,v 1.64 2024/07/11 05:41:24 kre Exp $"); #endif #endif /* not lint && not SCCSID */ /* * hist.c: TYPE(History) access functions */ #include +#include #include #include #include #include static const char hist_cookie[] = "_HiStOrY_V2_\n"; #include "histedit.h" #ifdef NARROWCHAR #define Char char #define FUN(prefix, rest) prefix ## _ ## rest #define FUNW(type) type #define TYPE(type) type #define STR(x) x #define Strlen(s) strlen(s) #define Strdup(s) strdup(s) #define Strcmp(d, s) strcmp(d, s) #define Strncmp(d, s, n) strncmp(d, s, n) #define Strncpy(d, s, n) strncpy(d, s, n) #define Strncat(d, s, n) strncat(d, s, n) #define ct_decode_string(s, b) (s) #define ct_encode_string(s, b) (s) #else #include "chartype.h" #define Char wchar_t #define FUN(prefix, rest) prefix ## _w ## rest #define FUNW(type) type ## _w #define TYPE(type) type ## W #define STR(x) L ## x #define Strlen(s) wcslen(s) #define Strdup(s) wcsdup(s) #define Strcmp(d, s) wcscmp(d, s) #define Strncmp(d, s, n) wcsncmp(d, s, n) #define Strncpy(d, s, n) wcsncpy(d, s, n) #define Strncat(d, s, n) wcsncat(d, s, n) #endif typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *); typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *); typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *); typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int); struct TYPE(history) { void *h_ref; /* Argument for history fcns */ int h_ent; /* Last entry point for history */ history_gfun_t h_first; /* Get the first element */ history_gfun_t h_next; /* Get the next element */ history_gfun_t h_last; /* Get the last element */ history_gfun_t h_prev; /* Get the previous element */ history_gfun_t h_curr; /* Get the current element */ history_sfun_t h_set; /* Set the current element */ history_sfun_t h_del; /* Set the given element */ history_vfun_t h_clear; /* Clear the history list */ history_efun_t h_enter; /* Add an element */ history_efun_t h_add; /* Append to an element */ }; #define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev) #define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev) #define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev) #define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev) #define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev) #define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n) #define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev) #define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str) #define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str) #define HDEL(h, ev, n) (*(h)->h_del)((h)->h_ref, ev, n) #define h_strdup(a) Strdup(a) #define h_malloc(a) malloc(a) #define h_realloc(a, b) realloc((a), (b)) #define h_free(a) free(a) typedef struct { int num; Char *str; } HistEventPrivate; static int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int); static int history_getsize(TYPE(History) *, TYPE(HistEvent) *); static int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int); static int history_getunique(TYPE(History) *, TYPE(HistEvent) *); static int history_set_fun(TYPE(History) *, TYPE(History) *); static int history_load(TYPE(History) *, const char *); static int history_save(TYPE(History) *, const char *); static int history_save_fp(TYPE(History) *, size_t, FILE *); static int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int); static int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int); static int history_next_string(TYPE(History) *, TYPE(HistEvent) *, const Char *); static int history_prev_string(TYPE(History) *, TYPE(HistEvent) *, const Char *); /***********************************************************************/ /* * Builtin- history implementation */ typedef struct hentry_t { TYPE(HistEvent) ev; /* What we return */ void *data; /* data */ struct hentry_t *next; /* Next entry */ struct hentry_t *prev; /* Previous entry */ } hentry_t; typedef struct history_t { hentry_t list; /* Fake list header element */ hentry_t *cursor; /* Current element in the list */ int max; /* Maximum number of events */ int cur; /* Current number of events */ int eventid; /* For generation of unique event id */ int flags; /* TYPE(History) flags */ #define H_UNIQUE 1 /* Store only unique elements */ } history_t; static int history_def_next(void *, TYPE(HistEvent) *); static int history_def_first(void *, TYPE(HistEvent) *); static int history_def_prev(void *, TYPE(HistEvent) *); static int history_def_last(void *, TYPE(HistEvent) *); static int history_def_curr(void *, TYPE(HistEvent) *); static int history_def_set(void *, TYPE(HistEvent) *, const int); static void history_def_clear(void *, TYPE(HistEvent) *); static int history_def_enter(void *, TYPE(HistEvent) *, const Char *); static int history_def_add(void *, TYPE(HistEvent) *, const Char *); static int history_def_del(void *, TYPE(HistEvent) *, const int); static int history_def_init(void **, TYPE(HistEvent) *, int); static int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *); static void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *); static int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **); static int history_set_nth(void *, TYPE(HistEvent) *, int); #define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num)) #define history_def_getsize(p) (((history_t *)p)->cur) #define history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0) #define history_def_setunique(p, uni) \ if (uni) \ (((history_t *)p)->flags) |= H_UNIQUE; \ else \ (((history_t *)p)->flags) &= ~H_UNIQUE #define he_strerror(code) he_errlist[code] #define he_seterrev(evp, code) {\ evp->num = code;\ evp->str = he_strerror(code);\ } /* error messages */ static const Char *const he_errlist[] = { STR("OK"), STR("unknown error"), STR("malloc() failed"), STR("first event not found"), STR("last event not found"), STR("empty list"), STR("no next event"), STR("no previous event"), STR("current event is invalid"), STR("event not found"), STR("can't read history from file"), STR("can't write history"), STR("required parameter(s) not supplied"), STR("history size negative"), STR("function not allowed with other history-functions-set the default"), STR("bad parameters") }; /* error codes */ #define _HE_OK 0 #define _HE_UNKNOWN 1 #define _HE_MALLOC_FAILED 2 #define _HE_FIRST_NOTFOUND 3 #define _HE_LAST_NOTFOUND 4 #define _HE_EMPTY_LIST 5 #define _HE_END_REACHED 6 #define _HE_START_REACHED 7 #define _HE_CURR_INVALID 8 #define _HE_NOT_FOUND 9 #define _HE_HIST_READ 10 #define _HE_HIST_WRITE 11 #define _HE_PARAM_MISSING 12 #define _HE_SIZE_NEGATIVE 13 #define _HE_NOT_ALLOWED 14 #define _HE_BAD_PARAM 15 /* history_def_first(): * Default function to return the first event in the history. */ static int history_def_first(void *p, TYPE(HistEvent) *ev) { history_t *h = (history_t *) p; h->cursor = h->list.next; if (h->cursor != &h->list) *ev = h->cursor->ev; else { he_seterrev(ev, _HE_FIRST_NOTFOUND); return -1; } return 0; } /* history_def_last(): * Default function to return the last event in the history. */ static int history_def_last(void *p, TYPE(HistEvent) *ev) { history_t *h = (history_t *) p; h->cursor = h->list.prev; if (h->cursor != &h->list) *ev = h->cursor->ev; else { he_seterrev(ev, _HE_LAST_NOTFOUND); return -1; } return 0; } /* history_def_next(): * Default function to return the next event in the history. */ static int history_def_next(void *p, TYPE(HistEvent) *ev) { history_t *h = (history_t *) p; if (h->cursor == &h->list) { he_seterrev(ev, _HE_EMPTY_LIST); return -1; } if (h->cursor->next == &h->list) { he_seterrev(ev, _HE_END_REACHED); return -1; } h->cursor = h->cursor->next; *ev = h->cursor->ev; return 0; } /* history_def_prev(): * Default function to return the previous event in the history. */ static int history_def_prev(void *p, TYPE(HistEvent) *ev) { history_t *h = (history_t *) p; if (h->cursor == &h->list) { he_seterrev(ev, (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST); return -1; } if (h->cursor->prev == &h->list) { he_seterrev(ev, _HE_START_REACHED); return -1; } h->cursor = h->cursor->prev; *ev = h->cursor->ev; return 0; } /* history_def_curr(): * Default function to return the current event in the history. */ static int history_def_curr(void *p, TYPE(HistEvent) *ev) { history_t *h = (history_t *) p; if (h->cursor != &h->list) *ev = h->cursor->ev; else { he_seterrev(ev, (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST); return -1; } return 0; } /* history_def_set(): * Default function to set the current event in the history to the * given one. */ static int history_def_set(void *p, TYPE(HistEvent) *ev, const int n) { history_t *h = (history_t *) p; if (h->cur == 0) { he_seterrev(ev, _HE_EMPTY_LIST); return -1; } if (h->cursor == &h->list || h->cursor->ev.num != n) { for (h->cursor = h->list.next; h->cursor != &h->list; h->cursor = h->cursor->next) if (h->cursor->ev.num == n) break; } if (h->cursor == &h->list) { he_seterrev(ev, _HE_NOT_FOUND); return -1; } return 0; } /* history_set_nth(): * Default function to set the current event in the history to the * n-th one. */ static int history_set_nth(void *p, TYPE(HistEvent) *ev, int n) { history_t *h = (history_t *) p; if (h->cur == 0) { he_seterrev(ev, _HE_EMPTY_LIST); return -1; } for (h->cursor = h->list.prev; h->cursor != &h->list; h->cursor = h->cursor->prev) if (n-- <= 0) break; if (h->cursor == &h->list) { he_seterrev(ev, _HE_NOT_FOUND); return -1; } return 0; } /* history_def_add(): * Append string to element */ static int history_def_add(void *p, TYPE(HistEvent) *ev, const Char *str) { history_t *h = (history_t *) p; size_t len, elen, slen; Char *s; HistEventPrivate *evp = (void *)&h->cursor->ev; if (h->cursor == &h->list) return history_def_enter(p, ev, str); elen = Strlen(evp->str); slen = Strlen(str); len = elen + slen + 1; s = h_malloc(len * sizeof(*s)); if (s == NULL) { he_seterrev(ev, _HE_MALLOC_FAILED); return -1; } memcpy(s, evp->str, elen * sizeof(*s)); memcpy(s + elen, str, slen * sizeof(*s)); s[len - 1] = '\0'; h_free(evp->str); evp->str = s; *ev = h->cursor->ev; return 0; } static int history_deldata_nth(history_t *h, TYPE(HistEvent) *ev, int num, void **data) { if (history_set_nth(h, ev, num) != 0) return -1; /* magic value to skip delete (just set to n-th history) */ if (data == (void **)-1) return 0; ev->str = Strdup(h->cursor->ev.str); ev->num = h->cursor->ev.num; if (data) *data = h->cursor->data; history_def_delete(h, ev, h->cursor); return 0; } /* history_def_del(): * Delete element hp of the h list */ /* ARGSUSED */ static int history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)), const int num) { history_t *h = (history_t *) p; if (history_def_set(h, ev, num) != 0) return -1; ev->str = Strdup(h->cursor->ev.str); ev->num = h->cursor->ev.num; history_def_delete(h, ev, h->cursor); return 0; } /* history_def_delete(): * Delete element hp of the h list */ /* ARGSUSED */ static void history_def_delete(history_t *h, TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp) { HistEventPrivate *evp = (void *)&hp->ev; if (hp == &h->list) abort(); if (h->cursor == hp) { h->cursor = hp->prev; if (h->cursor == &h->list) h->cursor = hp->next; } hp->prev->next = hp->next; hp->next->prev = hp->prev; h_free(evp->str); h_free(hp); h->cur--; } /* history_def_insert(): * Insert element with string str in the h list */ static int history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str) { hentry_t *c; c = h_malloc(sizeof(*c)); if (c == NULL) goto oomem; if ((c->ev.str = h_strdup(str)) == NULL) { h_free(c); goto oomem; } c->data = NULL; c->ev.num = ++h->eventid; c->next = h->list.next; c->prev = &h->list; h->list.next->prev = c; h->list.next = c; h->cur++; h->cursor = c; *ev = c->ev; return 0; oomem: he_seterrev(ev, _HE_MALLOC_FAILED); return -1; } /* history_def_enter(): * Default function to enter an item in the history */ static int history_def_enter(void *p, TYPE(HistEvent) *ev, const Char *str) { history_t *h = (history_t *) p; if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list && Strcmp(h->list.next->ev.str, str) == 0) return 0; if (history_def_insert(h, ev, str) == -1) return -1; /* error, keep error message */ /* * Always keep at least one entry. * This way we don't have to check for the empty list. */ while (h->cur > h->max && h->cur > 0) history_def_delete(h, ev, h->list.prev); return 1; } /* history_def_init(): * Default history initialization function */ /* ARGSUSED */ static int history_def_init(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n) { history_t *h = (history_t *) h_malloc(sizeof(*h)); if (h == NULL) return -1; if (n <= 0) n = 0; h->eventid = 0; h->cur = 0; h->max = n; h->list.next = h->list.prev = &h->list; h->list.ev.str = NULL; h->list.ev.num = 0; h->cursor = &h->list; h->flags = 0; *p = h; return 0; } /* history_def_clear(): * Default history cleanup function */ static void history_def_clear(void *p, TYPE(HistEvent) *ev) { history_t *h = (history_t *) p; while (h->list.prev != &h->list) history_def_delete(h, ev, h->list.prev); h->cursor = &h->list; h->eventid = 0; h->cur = 0; } /************************************************************************/ /* history_init(): * Initialization function. */ TYPE(History) * FUN(history,init)(void) { TYPE(HistEvent) ev; TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h)); if (h == NULL) return NULL; if (history_def_init(&h->h_ref, &ev, 0) == -1) { h_free(h); return NULL; } h->h_ent = -1; h->h_next = history_def_next; h->h_first = history_def_first; h->h_last = history_def_last; h->h_prev = history_def_prev; h->h_curr = history_def_curr; h->h_set = history_def_set; h->h_clear = history_def_clear; h->h_enter = history_def_enter; h->h_add = history_def_add; h->h_del = history_def_del; return h; } /* history_end(): * clean up history; */ void FUN(history,end)(TYPE(History) *h) { TYPE(HistEvent) ev; if (h->h_next == history_def_next) history_def_clear(h->h_ref, &ev); h_free(h->h_ref); h_free(h); } /* history_setsize(): * Set history number of events */ static int history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num) { if (h->h_next != history_def_next) { he_seterrev(ev, _HE_NOT_ALLOWED); return -1; } if (num < 0) { he_seterrev(ev, _HE_BAD_PARAM); return -1; } history_def_setsize(h->h_ref, num); return 0; } /* history_getsize(): * Get number of events currently in history */ static int history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev) { if (h->h_next != history_def_next) { he_seterrev(ev, _HE_NOT_ALLOWED); return -1; } ev->num = history_def_getsize(h->h_ref); if (ev->num < -1) { he_seterrev(ev, _HE_SIZE_NEGATIVE); return -1; } return 0; } /* history_setunique(): * Set if adjacent equal events should not be entered in history. */ static int history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni) { if (h->h_next != history_def_next) { he_seterrev(ev, _HE_NOT_ALLOWED); return -1; } history_def_setunique(h->h_ref, uni); return 0; } /* history_getunique(): * Get if adjacent equal events should not be entered in history. */ static int history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev) { if (h->h_next != history_def_next) { he_seterrev(ev, _HE_NOT_ALLOWED); return -1; } ev->num = history_def_getunique(h->h_ref); return 0; } /* history_set_fun(): * Set history functions */ static int history_set_fun(TYPE(History) *h, TYPE(History) *nh) { TYPE(HistEvent) ev; if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL || nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL || nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL || nh->h_del == NULL || nh->h_ref == NULL) { if (h->h_next != history_def_next) { if (history_def_init(&h->h_ref, &ev, 0) == -1) return -1; h->h_first = history_def_first; h->h_next = history_def_next; h->h_last = history_def_last; h->h_prev = history_def_prev; h->h_curr = history_def_curr; h->h_set = history_def_set; h->h_clear = history_def_clear; h->h_enter = history_def_enter; h->h_add = history_def_add; h->h_del = history_def_del; } return -1; } if (h->h_next == history_def_next) history_def_clear(h->h_ref, &ev); h->h_ent = -1; h->h_first = nh->h_first; h->h_next = nh->h_next; h->h_last = nh->h_last; h->h_prev = nh->h_prev; h->h_curr = nh->h_curr; h->h_set = nh->h_set; h->h_clear = nh->h_clear; h->h_enter = nh->h_enter; h->h_add = nh->h_add; h->h_del = nh->h_del; return 0; } /* history_load(): * TYPE(History) load function */ static int history_load(TYPE(History) *h, const char *fname) { FILE *fp; char *line; size_t llen; ssize_t sz; size_t max_size; char *ptr; int i = -1; TYPE(HistEvent) ev; Char *decode_result; #ifndef NARROWCHAR static ct_buffer_t conv; #endif if ((fp = fopen(fname, "r")) == NULL) return i; line = NULL; llen = 0; if ((sz = getline(&line, &llen, fp)) == -1) goto done; if (strncmp(line, hist_cookie, (size_t)sz) != 0) goto done; ptr = h_malloc((max_size = 1024) * sizeof(*ptr)); if (ptr == NULL) goto done; for (i = 0; (sz = getline(&line, &llen, fp)) != -1; i++) { if (sz > 0 && line[sz - 1] == '\n') line[--sz] = '\0'; if (max_size < (size_t)sz) { char *nptr; max_size = ((size_t)sz + 1024) & (size_t)~1023; nptr = h_realloc(ptr, max_size * sizeof(*ptr)); if (nptr == NULL) { i = -1; goto oomem; } ptr = nptr; } (void) strunvis(ptr, line); decode_result = ct_decode_string(ptr, &conv); if (decode_result == NULL) continue; if (HENTER(h, &ev, decode_result) == -1) { i = -1; goto oomem; } } oomem: h_free(ptr); done: free(line); (void) fclose(fp); return i; } /* history_save_fp(): * TYPE(History) save function */ static int history_save_fp(TYPE(History) *h, size_t nelem, FILE *fp) { TYPE(HistEvent) ev; int i = -1, retval; size_t len, max_size; char *ptr; const char *str; #ifndef NARROWCHAR static ct_buffer_t conv; #endif - if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) - goto done; if (ftell(fp) == 0 && fputs(hist_cookie, fp) == EOF) goto done; ptr = h_malloc((max_size = 1024) * sizeof(*ptr)); if (ptr == NULL) goto done; if (nelem != (size_t)-1) { for (retval = HFIRST(h, &ev); retval != -1 && nelem-- > 0; retval = HNEXT(h, &ev)) continue; } else retval = -1; if (retval == -1) retval = HLAST(h, &ev); for (i = 0; retval != -1; retval = HPREV(h, &ev), i++) { str = ct_encode_string(ev.str, &conv); len = strlen(str) * 4 + 1; if (len > max_size) { char *nptr; max_size = (len + 1024) & (size_t)~1023; nptr = h_realloc(ptr, max_size * sizeof(*ptr)); if (nptr == NULL) { i = -1; goto oomem; } ptr = nptr; } (void) strvis(ptr, str, VIS_WHITE); (void) fprintf(fp, "%s\n", ptr); } oomem: h_free(ptr); done: return i; } /* history_save(): * History save function */ static int history_save(TYPE(History) *h, const char *fname) { FILE *fp; int i; - if ((fp = fopen(fname, "w")) == NULL) + if ((i = open(fname, O_WRONLY|O_CREAT|O_TRUNC, + S_IRUSR|S_IWUSR)) == -1) + return -1; + + if ((fp = fdopen(i, "w")) == NULL) return -1; i = history_save_fp(h, (size_t)-1, fp); (void) fclose(fp); return i; } /* history_prev_event(): * Find the previous event, with number given */ static int history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num) { int retval; for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) if (ev->num == num) return 0; he_seterrev(ev, _HE_NOT_FOUND); return -1; } static int history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d) { int retval; for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) if (ev->num == num) { if (d) *d = ((history_t *)h->h_ref)->cursor->data; return 0; } he_seterrev(ev, _HE_NOT_FOUND); return -1; } /* history_next_event(): * Find the next event, with number given */ static int history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num) { int retval; for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) if (ev->num == num) return 0; he_seterrev(ev, _HE_NOT_FOUND); return -1; } /* history_prev_string(): * Find the previous event beginning with string */ static int history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str) { size_t len = Strlen(str); int retval; for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) if (Strncmp(str, ev->str, len) == 0) return 0; he_seterrev(ev, _HE_NOT_FOUND); return -1; } /* history_next_string(): * Find the next event beginning with string */ static int history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str) { size_t len = Strlen(str); int retval; for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) if (Strncmp(str, ev->str, len) == 0) return 0; he_seterrev(ev, _HE_NOT_FOUND); return -1; } /* history(): * User interface to history functions. */ int FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...) { va_list va; const Char *str; int retval; va_start(va, fun); he_seterrev(ev, _HE_OK); switch (fun) { case H_GETSIZE: retval = history_getsize(h, ev); break; case H_SETSIZE: retval = history_setsize(h, ev, va_arg(va, int)); break; case H_GETUNIQUE: retval = history_getunique(h, ev); break; case H_SETUNIQUE: retval = history_setunique(h, ev, va_arg(va, int)); break; case H_ADD: str = va_arg(va, const Char *); retval = HADD(h, ev, str); break; case H_DEL: retval = HDEL(h, ev, va_arg(va, const int)); break; case H_ENTER: str = va_arg(va, const Char *); if ((retval = HENTER(h, ev, str)) != -1) h->h_ent = ev->num; break; case H_APPEND: str = va_arg(va, const Char *); if ((retval = HSET(h, ev, h->h_ent)) != -1) retval = HADD(h, ev, str); break; case H_FIRST: retval = HFIRST(h, ev); break; case H_NEXT: retval = HNEXT(h, ev); break; case H_LAST: retval = HLAST(h, ev); break; case H_PREV: retval = HPREV(h, ev); break; case H_CURR: retval = HCURR(h, ev); break; case H_SET: retval = HSET(h, ev, va_arg(va, const int)); break; case H_CLEAR: HCLEAR(h, ev); retval = 0; break; case H_LOAD: retval = history_load(h, va_arg(va, const char *)); if (retval == -1) he_seterrev(ev, _HE_HIST_READ); break; case H_SAVE: retval = history_save(h, va_arg(va, const char *)); if (retval == -1) he_seterrev(ev, _HE_HIST_WRITE); break; case H_SAVE_FP: retval = history_save_fp(h, (size_t)-1, va_arg(va, FILE *)); if (retval == -1) he_seterrev(ev, _HE_HIST_WRITE); break; case H_NSAVE_FP: { size_t sz = va_arg(va, size_t); retval = history_save_fp(h, sz, va_arg(va, FILE *)); if (retval == -1) he_seterrev(ev, _HE_HIST_WRITE); break; } case H_PREV_EVENT: retval = history_prev_event(h, ev, va_arg(va, int)); break; case H_NEXT_EVENT: retval = history_next_event(h, ev, va_arg(va, int)); break; case H_PREV_STR: retval = history_prev_string(h, ev, va_arg(va, const Char *)); break; case H_NEXT_STR: retval = history_next_string(h, ev, va_arg(va, const Char *)); break; case H_FUNC: { TYPE(History) hf; hf.h_ref = va_arg(va, void *); h->h_ent = -1; hf.h_first = va_arg(va, history_gfun_t); hf.h_next = va_arg(va, history_gfun_t); hf.h_last = va_arg(va, history_gfun_t); hf.h_prev = va_arg(va, history_gfun_t); hf.h_curr = va_arg(va, history_gfun_t); hf.h_set = va_arg(va, history_sfun_t); hf.h_clear = va_arg(va, history_vfun_t); hf.h_enter = va_arg(va, history_efun_t); hf.h_add = va_arg(va, history_efun_t); hf.h_del = va_arg(va, history_sfun_t); if ((retval = history_set_fun(h, &hf)) == -1) he_seterrev(ev, _HE_PARAM_MISSING); break; } case H_END: FUN(history,end)(h); retval = 0; break; case H_NEXT_EVDATA: { int num = va_arg(va, int); void **d = va_arg(va, void **); retval = history_next_evdata(h, ev, num, d); break; } case H_DELDATA: { int num = va_arg(va, int); void **d = va_arg(va, void **); retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d); break; } case H_REPLACE: /* only use after H_NEXT_EVDATA */ { const Char *line = va_arg(va, const Char *); void *d = va_arg(va, void *); const Char *s; if(!line || !(s = Strdup(line))) { retval = -1; break; } ((history_t *)h->h_ref)->cursor->ev.str = s; ((history_t *)h->h_ref)->cursor->data = d; retval = 0; break; } default: retval = -1; he_seterrev(ev, _HE_UNKNOWN); break; } va_end(va); return retval; } diff --git a/contrib/libedit/keymacro.c b/contrib/libedit/keymacro.c index cef24a1752e6..38ae9be58df9 100644 --- a/contrib/libedit/keymacro.c +++ b/contrib/libedit/keymacro.c @@ -1,669 +1,665 @@ -/* $NetBSD: keymacro.c,v 1.24 2019/07/23 10:18:52 christos Exp $ */ +/* $NetBSD: keymacro.c,v 1.25 2025/01/03 00:40:08 rillig Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)key.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: keymacro.c,v 1.24 2019/07/23 10:18:52 christos Exp $"); +__RCSID("$NetBSD: keymacro.c,v 1.25 2025/01/03 00:40:08 rillig Exp $"); #endif #endif /* not lint && not SCCSID */ /* * keymacro.c: This module contains the procedures for maintaining * the extended-key map. * * An extended-key (key) is a sequence of keystrokes introduced * with a sequence introducer and consisting of an arbitrary * number of characters. This module maintains a map (the * el->el_keymacro.map) * to convert these extended-key sequences into input strs * (XK_STR) or editor functions (XK_CMD). * * Warning: * If key is a substr of some other keys, then the longer * keys are lost!! That is, if the keys "abcd" and "abcef" * are in el->el_keymacro.map, adding the key "abc" will cause * the first two definitions to be lost. * * Restrictions: * ------------- * 1) It is not possible to have one key that is a * substr of another. */ #include #include #include "el.h" #include "fcns.h" /* * The Nodes of the el->el_keymacro.map. The el->el_keymacro.map is a * linked list of these node elements */ struct keymacro_node_t { wchar_t ch; /* single character of key */ int type; /* node type */ keymacro_value_t val; /* command code or pointer to str, */ /* if this is a leaf */ struct keymacro_node_t *next; /* ptr to next char of this key */ struct keymacro_node_t *sibling;/* ptr to another key with same prefix*/ }; static int node_trav(EditLine *, keymacro_node_t *, wchar_t *, keymacro_value_t *); static int node__try(EditLine *, keymacro_node_t *, const wchar_t *, keymacro_value_t *, int); static keymacro_node_t *node__get(wint_t); static void node__free(keymacro_node_t *); static void node__put(EditLine *, keymacro_node_t *); static int node__delete(EditLine *, keymacro_node_t **, const wchar_t *); static int node_lookup(EditLine *, const wchar_t *, keymacro_node_t *, size_t); static int node_enum(EditLine *, keymacro_node_t *, size_t); #define KEY_BUFSIZ EL_BUFSIZ /* keymacro_init(): * Initialize the key maps */ libedit_private int keymacro_init(EditLine *el) { el->el_keymacro.buf = el_calloc(KEY_BUFSIZ, sizeof(*el->el_keymacro.buf)); if (el->el_keymacro.buf == NULL) return -1; el->el_keymacro.map = NULL; keymacro_reset(el); return 0; } /* keymacro_end(): * Free the key maps */ libedit_private void keymacro_end(EditLine *el) { el_free(el->el_keymacro.buf); el->el_keymacro.buf = NULL; node__free(el->el_keymacro.map); } /* keymacro_map_cmd(): * Associate cmd with a key value */ libedit_private keymacro_value_t * keymacro_map_cmd(EditLine *el, int cmd) { el->el_keymacro.val.cmd = (el_action_t) cmd; return &el->el_keymacro.val; } /* keymacro_map_str(): * Associate str with a key value */ libedit_private keymacro_value_t * keymacro_map_str(EditLine *el, wchar_t *str) { el->el_keymacro.val.str = str; return &el->el_keymacro.val; } /* keymacro_reset(): * Takes all nodes on el->el_keymacro.map and puts them on free list. * Then initializes el->el_keymacro.map with arrow keys * [Always bind the ansi arrow keys?] */ libedit_private void keymacro_reset(EditLine *el) { node__put(el, el->el_keymacro.map); el->el_keymacro.map = NULL; return; } /* keymacro_get(): * Calls the recursive function with entry point el->el_keymacro.map * Looks up *ch in map and then reads characters until a * complete match is found or a mismatch occurs. Returns the * type of the match found (XK_STR or XK_CMD). * Returns NULL in val.str and XK_STR for no match. * Returns XK_NOD for end of file or read error. * The last character read is returned in *ch. */ libedit_private int keymacro_get(EditLine *el, wchar_t *ch, keymacro_value_t *val) { return node_trav(el, el->el_keymacro.map, ch, val); } /* keymacro_add(): * Adds key to the el->el_keymacro.map and associates the value in * val with it. If key is already is in el->el_keymacro.map, the new * code is applied to the existing key. Ntype specifies if code is a * command, an out str or a unix command. */ libedit_private void keymacro_add(EditLine *el, const wchar_t *key, keymacro_value_t *val, int ntype) { if (key[0] == '\0') { (void) fprintf(el->el_errfile, "keymacro_add: Null extended-key not allowed.\n"); return; } if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) { (void) fprintf(el->el_errfile, "keymacro_add: sequence-lead-in command not allowed\n"); return; } if (el->el_keymacro.map == NULL) /* tree is initially empty. Set up new node to match key[0] */ el->el_keymacro.map = node__get(key[0]); /* it is properly initialized */ /* Now recurse through el->el_keymacro.map */ (void) node__try(el, el->el_keymacro.map, key, val, ntype); return; } /* keymacro_clear(): * */ libedit_private void keymacro_clear(EditLine *el, el_action_t *map, const wchar_t *in) { if (*in > N_KEYS) /* can't be in the map */ return; if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) && ((map == el->el_map.key && el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) || (map == el->el_map.alt && el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN))) (void) keymacro_delete(el, in); } /* keymacro_delete(): * Delete the key and all longer keys staring with key, if * they exists. */ libedit_private int keymacro_delete(EditLine *el, const wchar_t *key) { if (key[0] == '\0') { (void) fprintf(el->el_errfile, "keymacro_delete: Null extended-key not allowed.\n"); return -1; } if (el->el_keymacro.map == NULL) return 0; (void) node__delete(el, &el->el_keymacro.map, key); return 0; } /* keymacro_print(): * Print the binding associated with key key. * Print entire el->el_keymacro.map if null */ libedit_private void keymacro_print(EditLine *el, const wchar_t *key) { /* do nothing if el->el_keymacro.map is empty and null key specified */ if (el->el_keymacro.map == NULL && *key == 0) return; el->el_keymacro.buf[0] = '"'; if (node_lookup(el, key, el->el_keymacro.map, (size_t)1) <= -1) /* key is not bound */ (void) fprintf(el->el_errfile, "Unbound extended key \"%ls" "\"\n", key); return; } /* node_trav(): * recursively traverses node in tree until match or mismatch is * found. May read in more characters. */ static int node_trav(EditLine *el, keymacro_node_t *ptr, wchar_t *ch, keymacro_value_t *val) { if (ptr->ch == *ch) { /* match found */ if (ptr->next) { /* key not complete so get next char */ if (el_wgetc(el, ch) != 1) return XK_NOD; return node_trav(el, ptr->next, ch, val); } else { *val = ptr->val; if (ptr->type != XK_CMD) *ch = '\0'; return ptr->type; } } else { /* no match found here */ if (ptr->sibling) { /* try next sibling */ return node_trav(el, ptr->sibling, ch, val); } else { /* no next sibling -- mismatch */ val->str = NULL; return XK_STR; } } } /* node__try(): * Find a node that matches *str or allocate a new one */ static int node__try(EditLine *el, keymacro_node_t *ptr, const wchar_t *str, keymacro_value_t *val, int ntype) { if (ptr->ch != *str) { keymacro_node_t *xm; for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) if (xm->sibling->ch == *str) break; if (xm->sibling == NULL) xm->sibling = node__get(*str); /* setup new node */ ptr = xm->sibling; } if (*++str == '\0') { /* we're there */ if (ptr->next != NULL) { node__put(el, ptr->next); /* lose longer keys with this prefix */ ptr->next = NULL; } switch (ptr->type) { case XK_CMD: case XK_NOD: break; case XK_STR: if (ptr->val.str) el_free(ptr->val.str); break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type)); - break; } switch (ptr->type = ntype) { case XK_CMD: ptr->val = *val; break; case XK_STR: if ((ptr->val.str = wcsdup(val->str)) == NULL) return -1; break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); - break; } } else { /* still more chars to go */ if (ptr->next == NULL) ptr->next = node__get(*str); /* setup new node */ (void) node__try(el, ptr->next, str, val, ntype); } return 0; } /* node__delete(): * Delete node that matches str */ static int node__delete(EditLine *el, keymacro_node_t **inptr, const wchar_t *str) { keymacro_node_t *ptr; keymacro_node_t *prev_ptr = NULL; ptr = *inptr; if (ptr->ch != *str) { keymacro_node_t *xm; for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) if (xm->sibling->ch == *str) break; if (xm->sibling == NULL) return 0; prev_ptr = xm; ptr = xm->sibling; } if (*++str == '\0') { /* we're there */ if (prev_ptr == NULL) *inptr = ptr->sibling; else prev_ptr->sibling = ptr->sibling; ptr->sibling = NULL; node__put(el, ptr); return 1; } else if (ptr->next != NULL && node__delete(el, &ptr->next, str) == 1) { if (ptr->next != NULL) return 0; if (prev_ptr == NULL) *inptr = ptr->sibling; else prev_ptr->sibling = ptr->sibling; ptr->sibling = NULL; node__put(el, ptr); return 1; } else { return 0; } } /* node__put(): * Puts a tree of nodes onto free list using free(3). */ static void node__put(EditLine *el, keymacro_node_t *ptr) { if (ptr == NULL) return; if (ptr->next != NULL) { node__put(el, ptr->next); ptr->next = NULL; } node__put(el, ptr->sibling); switch (ptr->type) { case XK_CMD: case XK_NOD: break; case XK_STR: if (ptr->val.str != NULL) el_free(ptr->val.str); break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type)); - break; } el_free(ptr); } /* node__get(): * Returns pointer to a keymacro_node_t for ch. */ static keymacro_node_t * node__get(wint_t ch) { keymacro_node_t *ptr; ptr = el_malloc(sizeof(*ptr)); if (ptr == NULL) return NULL; ptr->ch = ch; ptr->type = XK_NOD; ptr->val.str = NULL; ptr->next = NULL; ptr->sibling = NULL; return ptr; } static void node__free(keymacro_node_t *k) { if (k == NULL) return; node__free(k->sibling); node__free(k->next); el_free(k); } /* node_lookup(): * look for the str starting at node ptr. * Print if last node */ static int node_lookup(EditLine *el, const wchar_t *str, keymacro_node_t *ptr, size_t cnt) { ssize_t used; if (ptr == NULL) return -1; /* cannot have null ptr */ if (!str || *str == 0) { /* no more chars in str. node_enum from here. */ (void) node_enum(el, ptr, cnt); return 0; } else { /* If match put this char into el->el_keymacro.buf. Recurse */ if (ptr->ch == *str) { /* match found */ used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt, ptr->ch); if (used == -1) return -1; /* ran out of buffer space */ if (ptr->next != NULL) /* not yet at leaf */ return (node_lookup(el, str + 1, ptr->next, (size_t)used + cnt)); else { /* next node is null so key should be complete */ if (str[1] == 0) { size_t px = cnt + (size_t)used; el->el_keymacro.buf[px] = '"'; el->el_keymacro.buf[px + 1] = '\0'; keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type); return 0; } else return -1; /* mismatch -- str still has chars */ } } else { /* no match found try sibling */ if (ptr->sibling) return (node_lookup(el, str, ptr->sibling, cnt)); else return -1; } } } /* node_enum(): * Traverse the node printing the characters it is bound in buffer */ static int node_enum(EditLine *el, keymacro_node_t *ptr, size_t cnt) { ssize_t used; if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */ el->el_keymacro.buf[++cnt] = '"'; el->el_keymacro.buf[++cnt] = '\0'; (void) fprintf(el->el_errfile, "Some extended keys too long for internal print buffer"); (void) fprintf(el->el_errfile, " \"%ls...\"\n", el->el_keymacro.buf); return 0; } if (ptr == NULL) { #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, "node_enum: BUG!! Null ptr passed\n!"); #endif return -1; } /* put this char at end of str */ used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt, ptr->ch); if (ptr->next == NULL) { /* print this key and function */ el->el_keymacro.buf[cnt + (size_t)used ] = '"'; el->el_keymacro.buf[cnt + (size_t)used + 1] = '\0'; keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type); } else (void) node_enum(el, ptr->next, cnt + (size_t)used); /* go to sibling if there is one */ if (ptr->sibling) (void) node_enum(el, ptr->sibling, cnt); return 0; } /* keymacro_kprint(): * Print the specified key and its associated * function specified by val */ libedit_private void keymacro_kprint(EditLine *el, const wchar_t *key, keymacro_value_t *val, int ntype) { el_bindings_t *fp; char unparsbuf[EL_BUFSIZ]; static const char fmt[] = "%-15s-> %s\n"; if (val != NULL) switch (ntype) { case XK_STR: (void) keymacro__decode_str(val->str, unparsbuf, sizeof(unparsbuf), ntype == XK_STR ? "\"\"" : "[]"); (void) fprintf(el->el_outfile, fmt, ct_encode_string(key, &el->el_scratch), unparsbuf); break; case XK_CMD: for (fp = el->el_map.help; fp->name; fp++) if (val->cmd == fp->func) { wcstombs(unparsbuf, fp->name, sizeof(unparsbuf)); unparsbuf[sizeof(unparsbuf) -1] = '\0'; (void) fprintf(el->el_outfile, fmt, ct_encode_string(key, &el->el_scratch), unparsbuf); break; } #ifdef DEBUG_KEY if (fp->name == NULL) (void) fprintf(el->el_outfile, "BUG! Command not found.\n"); #endif break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); - break; } else (void) fprintf(el->el_outfile, fmt, ct_encode_string(key, &el->el_scratch), "no input"); } #define ADDC(c) \ if (b < eb) \ *b++ = c; \ else \ b++ /* keymacro__decode_str(): * Make a printable version of the ey */ libedit_private size_t keymacro__decode_str(const wchar_t *str, char *buf, size_t len, const char *sep) { char *b = buf, *eb = b + len; const wchar_t *p; b = buf; if (sep[0] != '\0') { ADDC(sep[0]); } if (*str == '\0') { ADDC('^'); ADDC('@'); goto add_endsep; } for (p = str; *p != 0; p++) { wchar_t dbuf[VISUAL_WIDTH_MAX]; wchar_t *p2 = dbuf; ssize_t l = ct_visual_char(dbuf, VISUAL_WIDTH_MAX, *p); while (l-- > 0) { ssize_t n = ct_encode_char(b, (size_t)(eb - b), *p2++); if (n == -1) /* ran out of space */ goto add_endsep; else b += n; } } add_endsep: if (sep[0] != '\0' && sep[1] != '\0') { ADDC(sep[1]); } ADDC('\0'); if ((size_t)(b - buf) >= len) buf[len - 1] = '\0'; return (size_t)(b - buf); } diff --git a/contrib/libedit/libedit.pc b/contrib/libedit/libedit.pc new file mode 100644 index 000000000000..c08a8ba70b12 --- /dev/null +++ b/contrib/libedit/libedit.pc @@ -0,0 +1,12 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libedit +Description: command line editor library providing generic line editing, history, and tokenization functions. +Version: 3.1 +Requires: +Libs: -Wl,-R${libdir} -L${libdir} -ledit +Libs.private: -ltermcap +Cflags: -I${includedir} -I${includedir}/readline diff --git a/contrib/libedit/literal.c b/contrib/libedit/literal.c index f6eac26937e8..f6f63cb3fb06 100644 --- a/contrib/libedit/literal.c +++ b/contrib/libedit/literal.c @@ -1,136 +1,136 @@ -/* $NetBSD: literal.c,v 1.5 2019/07/23 13:10:11 christos Exp $ */ +/* $NetBSD: literal.c,v 1.6 2024/12/05 22:21:53 christos Exp $ */ /*- * Copyright (c) 2017 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) -__RCSID("$NetBSD: literal.c,v 1.5 2019/07/23 13:10:11 christos Exp $"); +__RCSID("$NetBSD: literal.c,v 1.6 2024/12/05 22:21:53 christos Exp $"); #endif /* not lint && not SCCSID */ /* * literal.c: Literal sequences handling. */ #include #include #include #include #include "el.h" libedit_private void literal_init(EditLine *el) { el_literal_t *l = &el->el_literal; memset(l, 0, sizeof(*l)); } libedit_private void literal_end(EditLine *el) { literal_clear(el); } libedit_private void literal_clear(EditLine *el) { el_literal_t *l = &el->el_literal; size_t i; if (l->l_len == 0) return; for (i = 0; i < l->l_idx; i++) el_free(l->l_buf[i]); el_free(l->l_buf); l->l_buf = NULL; l->l_len = 0; l->l_idx = 0; } libedit_private wint_t literal_add(EditLine *el, const wchar_t *buf, const wchar_t *end, int *wp) { el_literal_t *l = &el->el_literal; size_t i, len; ssize_t w, n; char *b; w = wcwidth(end[1]); /* column width of the visible char */ *wp = (int)w; - if (w <= 0) /* we require something to be printed */ + if (w < 0) /* non-printable characters are negative */ return 0; len = (size_t)(end - buf); for (w = 0, i = 0; i < len; i++) w += ct_enc_width(buf[i]); w += ct_enc_width(end[1]); b = el_malloc((size_t)(w + 1)); if (b == NULL) return 0; for (n = 0, i = 0; i < len; i++) n += ct_encode_char(b + n, (size_t)(w - n), buf[i]); n += ct_encode_char(b + n, (size_t)(w - n), end[1]); b[n] = '\0'; /* * Then save this literal string in the list of such strings, * and return a "magic character" to put into the terminal buffer. * When that magic char is 'printed' the saved string (which includes * the char that belongs in that position) gets sent instead. */ if (l->l_idx == l->l_len) { char **bp; l->l_len += 4; bp = el_realloc(l->l_buf, sizeof(*l->l_buf) * l->l_len); if (bp == NULL) { free(b); l->l_len -= 4; return 0; } l->l_buf = bp; } l->l_buf[l->l_idx++] = b; return EL_LITERAL | (wint_t)(l->l_idx - 1); } libedit_private const char * literal_get(EditLine *el, wint_t idx) { el_literal_t *l = &el->el_literal; assert(idx & EL_LITERAL); idx &= ~EL_LITERAL; assert(l->l_idx > (size_t)idx); return l->l_buf[idx]; } diff --git a/contrib/libedit/map.c b/contrib/libedit/map.c index 57d3038ab2e9..2ac661993baf 100644 --- a/contrib/libedit/map.c +++ b/contrib/libedit/map.c @@ -1,1430 +1,1429 @@ -/* $NetBSD: map.c,v 1.55 2022/10/30 19:11:31 christos Exp $ */ +/* $NetBSD: map.c,v 1.56 2025/01/03 00:40:08 rillig Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: map.c,v 1.55 2022/10/30 19:11:31 christos Exp $"); +__RCSID("$NetBSD: map.c,v 1.56 2025/01/03 00:40:08 rillig Exp $"); #endif #endif /* not lint && not SCCSID */ /* * map.c: Editor function definitions */ #include #include #include #include "el.h" #include "common.h" #include "emacs.h" #include "vi.h" #include "fcns.h" #include "func.h" #include "help.h" #include "parse.h" static void map_print_key(EditLine *, el_action_t *, const wchar_t *); static void map_print_some_keys(EditLine *, el_action_t *, wint_t, wint_t); static void map_print_all_keys(EditLine *); static void map_init_nls(EditLine *); static void map_init_meta(EditLine *); /* keymap tables ; should be N_KEYS*sizeof(KEYCMD) bytes long */ static const el_action_t el_map_emacs[] = { /* 0 */ EM_SET_MARK, /* ^@ */ /* 1 */ ED_MOVE_TO_BEG, /* ^A */ /* 2 */ ED_PREV_CHAR, /* ^B */ /* 3 */ ED_IGNORE, /* ^C */ /* 4 */ EM_DELETE_OR_LIST, /* ^D */ /* 5 */ ED_MOVE_TO_END, /* ^E */ /* 6 */ ED_NEXT_CHAR, /* ^F */ /* 7 */ ED_UNASSIGNED, /* ^G */ /* 8 */ EM_DELETE_PREV_CHAR, /* ^H */ /* 9 */ ED_UNASSIGNED, /* ^I */ /* 10 */ ED_NEWLINE, /* ^J */ /* 11 */ ED_KILL_LINE, /* ^K */ /* 12 */ ED_CLEAR_SCREEN, /* ^L */ /* 13 */ ED_NEWLINE, /* ^M */ /* 14 */ ED_NEXT_HISTORY, /* ^N */ /* 15 */ ED_IGNORE, /* ^O */ /* 16 */ ED_PREV_HISTORY, /* ^P */ /* 17 */ ED_IGNORE, /* ^Q */ /* 18 */ EM_INC_SEARCH_PREV, /* ^R */ /* 19 */ ED_IGNORE, /* ^S */ /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */ /* 21 */ EM_KILL_LINE, /* ^U */ /* 22 */ ED_QUOTED_INSERT, /* ^V */ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ /* 24 */ ED_SEQUENCE_LEAD_IN, /* ^X */ /* 25 */ EM_YANK, /* ^Y */ /* 26 */ ED_IGNORE, /* ^Z */ /* 27 */ EM_META_NEXT, /* ^[ */ /* 28 */ ED_IGNORE, /* ^\ */ /* 29 */ ED_IGNORE, /* ^] */ /* 30 */ ED_UNASSIGNED, /* ^^ */ /* 31 */ ED_UNASSIGNED, /* ^_ */ /* 32 */ ED_INSERT, /* SPACE */ /* 33 */ ED_INSERT, /* ! */ /* 34 */ ED_INSERT, /* " */ /* 35 */ ED_INSERT, /* # */ /* 36 */ ED_INSERT, /* $ */ /* 37 */ ED_INSERT, /* % */ /* 38 */ ED_INSERT, /* & */ /* 39 */ ED_INSERT, /* ' */ /* 40 */ ED_INSERT, /* ( */ /* 41 */ ED_INSERT, /* ) */ /* 42 */ ED_INSERT, /* * */ /* 43 */ ED_INSERT, /* + */ /* 44 */ ED_INSERT, /* , */ /* 45 */ ED_INSERT, /* - */ /* 46 */ ED_INSERT, /* . */ /* 47 */ ED_INSERT, /* / */ /* 48 */ ED_DIGIT, /* 0 */ /* 49 */ ED_DIGIT, /* 1 */ /* 50 */ ED_DIGIT, /* 2 */ /* 51 */ ED_DIGIT, /* 3 */ /* 52 */ ED_DIGIT, /* 4 */ /* 53 */ ED_DIGIT, /* 5 */ /* 54 */ ED_DIGIT, /* 6 */ /* 55 */ ED_DIGIT, /* 7 */ /* 56 */ ED_DIGIT, /* 8 */ /* 57 */ ED_DIGIT, /* 9 */ /* 58 */ ED_INSERT, /* : */ /* 59 */ ED_INSERT, /* ; */ /* 60 */ ED_INSERT, /* < */ /* 61 */ ED_INSERT, /* = */ /* 62 */ ED_INSERT, /* > */ /* 63 */ ED_INSERT, /* ? */ /* 64 */ ED_INSERT, /* @ */ /* 65 */ ED_INSERT, /* A */ /* 66 */ ED_INSERT, /* B */ /* 67 */ ED_INSERT, /* C */ /* 68 */ ED_INSERT, /* D */ /* 69 */ ED_INSERT, /* E */ /* 70 */ ED_INSERT, /* F */ /* 71 */ ED_INSERT, /* G */ /* 72 */ ED_INSERT, /* H */ /* 73 */ ED_INSERT, /* I */ /* 74 */ ED_INSERT, /* J */ /* 75 */ ED_INSERT, /* K */ /* 76 */ ED_INSERT, /* L */ /* 77 */ ED_INSERT, /* M */ /* 78 */ ED_INSERT, /* N */ /* 79 */ ED_INSERT, /* O */ /* 80 */ ED_INSERT, /* P */ /* 81 */ ED_INSERT, /* Q */ /* 82 */ ED_INSERT, /* R */ /* 83 */ ED_INSERT, /* S */ /* 84 */ ED_INSERT, /* T */ /* 85 */ ED_INSERT, /* U */ /* 86 */ ED_INSERT, /* V */ /* 87 */ ED_INSERT, /* W */ /* 88 */ ED_INSERT, /* X */ /* 89 */ ED_INSERT, /* Y */ /* 90 */ ED_INSERT, /* Z */ /* 91 */ ED_INSERT, /* [ */ /* 92 */ ED_INSERT, /* \ */ /* 93 */ ED_INSERT, /* ] */ /* 94 */ ED_INSERT, /* ^ */ /* 95 */ ED_INSERT, /* _ */ /* 96 */ ED_INSERT, /* ` */ /* 97 */ ED_INSERT, /* a */ /* 98 */ ED_INSERT, /* b */ /* 99 */ ED_INSERT, /* c */ /* 100 */ ED_INSERT, /* d */ /* 101 */ ED_INSERT, /* e */ /* 102 */ ED_INSERT, /* f */ /* 103 */ ED_INSERT, /* g */ /* 104 */ ED_INSERT, /* h */ /* 105 */ ED_INSERT, /* i */ /* 106 */ ED_INSERT, /* j */ /* 107 */ ED_INSERT, /* k */ /* 108 */ ED_INSERT, /* l */ /* 109 */ ED_INSERT, /* m */ /* 110 */ ED_INSERT, /* n */ /* 111 */ ED_INSERT, /* o */ /* 112 */ ED_INSERT, /* p */ /* 113 */ ED_INSERT, /* q */ /* 114 */ ED_INSERT, /* r */ /* 115 */ ED_INSERT, /* s */ /* 116 */ ED_INSERT, /* t */ /* 117 */ ED_INSERT, /* u */ /* 118 */ ED_INSERT, /* v */ /* 119 */ ED_INSERT, /* w */ /* 120 */ ED_INSERT, /* x */ /* 121 */ ED_INSERT, /* y */ /* 122 */ ED_INSERT, /* z */ /* 123 */ ED_INSERT, /* { */ /* 124 */ ED_INSERT, /* | */ /* 125 */ ED_INSERT, /* } */ /* 126 */ ED_INSERT, /* ~ */ /* 127 */ EM_DELETE_PREV_CHAR, /* ^? */ /* 128 */ ED_UNASSIGNED, /* M-^@ */ /* 129 */ ED_UNASSIGNED, /* M-^A */ /* 130 */ ED_UNASSIGNED, /* M-^B */ /* 131 */ ED_UNASSIGNED, /* M-^C */ /* 132 */ ED_UNASSIGNED, /* M-^D */ /* 133 */ ED_UNASSIGNED, /* M-^E */ /* 134 */ ED_UNASSIGNED, /* M-^F */ /* 135 */ ED_UNASSIGNED, /* M-^G */ /* 136 */ ED_DELETE_PREV_WORD, /* M-^H */ /* 137 */ ED_UNASSIGNED, /* M-^I */ /* 138 */ ED_UNASSIGNED, /* M-^J */ /* 139 */ ED_UNASSIGNED, /* M-^K */ /* 140 */ ED_CLEAR_SCREEN, /* M-^L */ /* 141 */ ED_UNASSIGNED, /* M-^M */ /* 142 */ ED_UNASSIGNED, /* M-^N */ /* 143 */ ED_UNASSIGNED, /* M-^O */ /* 144 */ ED_UNASSIGNED, /* M-^P */ /* 145 */ ED_UNASSIGNED, /* M-^Q */ /* 146 */ ED_UNASSIGNED, /* M-^R */ /* 147 */ ED_UNASSIGNED, /* M-^S */ /* 148 */ ED_UNASSIGNED, /* M-^T */ /* 149 */ ED_UNASSIGNED, /* M-^U */ /* 150 */ ED_UNASSIGNED, /* M-^V */ /* 151 */ ED_UNASSIGNED, /* M-^W */ /* 152 */ ED_UNASSIGNED, /* M-^X */ /* 153 */ ED_UNASSIGNED, /* M-^Y */ /* 154 */ ED_UNASSIGNED, /* M-^Z */ /* 155 */ ED_UNASSIGNED, /* M-^[ */ /* 156 */ ED_UNASSIGNED, /* M-^\ */ /* 157 */ ED_UNASSIGNED, /* M-^] */ /* 158 */ ED_UNASSIGNED, /* M-^^ */ /* 159 */ EM_COPY_PREV_WORD, /* M-^_ */ /* 160 */ ED_UNASSIGNED, /* M-SPACE */ /* 161 */ ED_UNASSIGNED, /* M-! */ /* 162 */ ED_UNASSIGNED, /* M-" */ /* 163 */ ED_UNASSIGNED, /* M-# */ /* 164 */ ED_UNASSIGNED, /* M-$ */ /* 165 */ ED_UNASSIGNED, /* M-% */ /* 166 */ ED_UNASSIGNED, /* M-& */ /* 167 */ ED_UNASSIGNED, /* M-' */ /* 168 */ ED_UNASSIGNED, /* M-( */ /* 169 */ ED_UNASSIGNED, /* M-) */ /* 170 */ ED_UNASSIGNED, /* M-* */ /* 171 */ ED_UNASSIGNED, /* M-+ */ /* 172 */ ED_UNASSIGNED, /* M-, */ /* 173 */ ED_UNASSIGNED, /* M-- */ /* 174 */ ED_UNASSIGNED, /* M-. */ /* 175 */ ED_UNASSIGNED, /* M-/ */ /* 176 */ ED_ARGUMENT_DIGIT, /* M-0 */ /* 177 */ ED_ARGUMENT_DIGIT, /* M-1 */ /* 178 */ ED_ARGUMENT_DIGIT, /* M-2 */ /* 179 */ ED_ARGUMENT_DIGIT, /* M-3 */ /* 180 */ ED_ARGUMENT_DIGIT, /* M-4 */ /* 181 */ ED_ARGUMENT_DIGIT, /* M-5 */ /* 182 */ ED_ARGUMENT_DIGIT, /* M-6 */ /* 183 */ ED_ARGUMENT_DIGIT, /* M-7 */ /* 184 */ ED_ARGUMENT_DIGIT, /* M-8 */ /* 185 */ ED_ARGUMENT_DIGIT, /* M-9 */ /* 186 */ ED_UNASSIGNED, /* M-: */ /* 187 */ ED_UNASSIGNED, /* M-; */ /* 188 */ ED_UNASSIGNED, /* M-< */ /* 189 */ ED_UNASSIGNED, /* M-= */ /* 190 */ ED_UNASSIGNED, /* M-> */ /* 191 */ ED_UNASSIGNED, /* M-? */ /* 192 */ ED_UNASSIGNED, /* M-@ */ /* 193 */ ED_UNASSIGNED, /* M-A */ /* 194 */ ED_PREV_WORD, /* M-B */ /* 195 */ EM_CAPITOL_CASE, /* M-C */ /* 196 */ EM_DELETE_NEXT_WORD, /* M-D */ /* 197 */ ED_UNASSIGNED, /* M-E */ /* 198 */ EM_NEXT_WORD, /* M-F */ /* 199 */ ED_UNASSIGNED, /* M-G */ /* 200 */ ED_UNASSIGNED, /* M-H */ /* 201 */ ED_UNASSIGNED, /* M-I */ /* 202 */ ED_UNASSIGNED, /* M-J */ /* 203 */ ED_UNASSIGNED, /* M-K */ /* 204 */ EM_LOWER_CASE, /* M-L */ /* 205 */ ED_UNASSIGNED, /* M-M */ /* 206 */ ED_SEARCH_NEXT_HISTORY, /* M-N */ /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */ /* 208 */ ED_SEARCH_PREV_HISTORY, /* M-P */ /* 209 */ ED_UNASSIGNED, /* M-Q */ /* 210 */ ED_UNASSIGNED, /* M-R */ /* 211 */ ED_UNASSIGNED, /* M-S */ /* 212 */ ED_UNASSIGNED, /* M-T */ /* 213 */ EM_UPPER_CASE, /* M-U */ /* 214 */ ED_UNASSIGNED, /* M-V */ /* 215 */ EM_COPY_REGION, /* M-W */ /* 216 */ ED_COMMAND, /* M-X */ /* 217 */ ED_UNASSIGNED, /* M-Y */ /* 218 */ ED_UNASSIGNED, /* M-Z */ /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */ /* 220 */ ED_UNASSIGNED, /* M-\ */ /* 221 */ ED_UNASSIGNED, /* M-] */ /* 222 */ ED_UNASSIGNED, /* M-^ */ /* 223 */ ED_UNASSIGNED, /* M-_ */ /* 223 */ ED_UNASSIGNED, /* M-` */ /* 224 */ ED_UNASSIGNED, /* M-a */ /* 225 */ ED_PREV_WORD, /* M-b */ /* 226 */ EM_CAPITOL_CASE, /* M-c */ /* 227 */ EM_DELETE_NEXT_WORD, /* M-d */ /* 228 */ ED_UNASSIGNED, /* M-e */ /* 229 */ EM_NEXT_WORD, /* M-f */ /* 230 */ ED_UNASSIGNED, /* M-g */ /* 231 */ ED_UNASSIGNED, /* M-h */ /* 232 */ ED_UNASSIGNED, /* M-i */ /* 233 */ ED_UNASSIGNED, /* M-j */ /* 234 */ ED_UNASSIGNED, /* M-k */ /* 235 */ EM_LOWER_CASE, /* M-l */ /* 236 */ ED_UNASSIGNED, /* M-m */ /* 237 */ ED_SEARCH_NEXT_HISTORY, /* M-n */ /* 238 */ ED_UNASSIGNED, /* M-o */ /* 239 */ ED_SEARCH_PREV_HISTORY, /* M-p */ /* 240 */ ED_UNASSIGNED, /* M-q */ /* 241 */ ED_UNASSIGNED, /* M-r */ /* 242 */ ED_UNASSIGNED, /* M-s */ /* 243 */ ED_UNASSIGNED, /* M-t */ /* 244 */ EM_UPPER_CASE, /* M-u */ /* 245 */ ED_UNASSIGNED, /* M-v */ /* 246 */ EM_COPY_REGION, /* M-w */ /* 247 */ ED_COMMAND, /* M-x */ /* 248 */ ED_UNASSIGNED, /* M-y */ /* 249 */ ED_UNASSIGNED, /* M-z */ /* 250 */ ED_UNASSIGNED, /* M-{ */ /* 251 */ ED_UNASSIGNED, /* M-| */ /* 252 */ ED_UNASSIGNED, /* M-} */ /* 253 */ ED_UNASSIGNED, /* M-~ */ /* 254 */ ED_DELETE_PREV_WORD /* M-^? */ /* 255 */ }; /* * keymap table for vi. Each index into above tbl; should be * N_KEYS entries long. Vi mode uses a sticky-extend to do command mode: * insert mode characters are in the normal keymap, and command mode * in the extended keymap. */ static const el_action_t el_map_vi_insert[] = { #ifdef KSHVI /* 0 */ ED_UNASSIGNED, /* ^@ */ /* 1 */ ED_INSERT, /* ^A */ /* 2 */ ED_INSERT, /* ^B */ /* 3 */ ED_INSERT, /* ^C */ /* 4 */ VI_LIST_OR_EOF, /* ^D */ /* 5 */ ED_INSERT, /* ^E */ /* 6 */ ED_INSERT, /* ^F */ /* 7 */ ED_INSERT, /* ^G */ /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */ /* 9 */ ED_INSERT, /* ^I */ /* Tab Key */ /* 10 */ ED_NEWLINE, /* ^J */ /* 11 */ ED_INSERT, /* ^K */ /* 12 */ ED_INSERT, /* ^L */ /* 13 */ ED_NEWLINE, /* ^M */ /* 14 */ ED_INSERT, /* ^N */ /* 15 */ ED_INSERT, /* ^O */ /* 16 */ ED_INSERT, /* ^P */ /* 17 */ ED_IGNORE, /* ^Q */ /* 18 */ ED_INSERT, /* ^R */ /* 19 */ ED_IGNORE, /* ^S */ /* 20 */ ED_INSERT, /* ^T */ /* 21 */ VI_KILL_LINE_PREV, /* ^U */ /* 22 */ ED_QUOTED_INSERT, /* ^V */ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ /* ED_DELETE_PREV_WORD: Only until strt edit pos */ /* 24 */ ED_INSERT, /* ^X */ /* 25 */ ED_INSERT, /* ^Y */ /* 26 */ ED_INSERT, /* ^Z */ /* 27 */ VI_COMMAND_MODE, /* ^[ */ /* [ Esc ] key */ /* 28 */ ED_IGNORE, /* ^\ */ /* 29 */ ED_INSERT, /* ^] */ /* 30 */ ED_INSERT, /* ^^ */ /* 31 */ ED_INSERT, /* ^_ */ #else /* !KSHVI */ /* * NOTE: These mappings do NOT Correspond well * to the KSH VI editing assignments. * On the other and they are convenient and * many people have have gotten used to them. */ /* 0 */ ED_UNASSIGNED, /* ^@ */ /* 1 */ ED_MOVE_TO_BEG, /* ^A */ /* 2 */ ED_PREV_CHAR, /* ^B */ /* 3 */ ED_IGNORE, /* ^C */ /* 4 */ VI_LIST_OR_EOF, /* ^D */ /* 5 */ ED_MOVE_TO_END, /* ^E */ /* 6 */ ED_NEXT_CHAR, /* ^F */ /* 7 */ ED_UNASSIGNED, /* ^G */ /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */ /* 9 */ ED_UNASSIGNED, /* ^I */ /* Tab Key */ /* 10 */ ED_NEWLINE, /* ^J */ /* 11 */ ED_KILL_LINE, /* ^K */ /* 12 */ ED_CLEAR_SCREEN, /* ^L */ /* 13 */ ED_NEWLINE, /* ^M */ /* 14 */ ED_NEXT_HISTORY, /* ^N */ /* 15 */ ED_IGNORE, /* ^O */ /* 16 */ ED_PREV_HISTORY, /* ^P */ /* 17 */ ED_IGNORE, /* ^Q */ /* 18 */ ED_REDISPLAY, /* ^R */ /* 19 */ ED_IGNORE, /* ^S */ /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */ /* 21 */ VI_KILL_LINE_PREV, /* ^U */ /* 22 */ ED_QUOTED_INSERT, /* ^V */ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ /* 24 */ ED_UNASSIGNED, /* ^X */ /* 25 */ ED_IGNORE, /* ^Y */ /* 26 */ ED_IGNORE, /* ^Z */ /* 27 */ VI_COMMAND_MODE, /* ^[ */ /* 28 */ ED_IGNORE, /* ^\ */ /* 29 */ ED_UNASSIGNED, /* ^] */ /* 30 */ ED_UNASSIGNED, /* ^^ */ /* 31 */ ED_UNASSIGNED, /* ^_ */ #endif /* KSHVI */ /* 32 */ ED_INSERT, /* SPACE */ /* 33 */ ED_INSERT, /* ! */ /* 34 */ ED_INSERT, /* " */ /* 35 */ ED_INSERT, /* # */ /* 36 */ ED_INSERT, /* $ */ /* 37 */ ED_INSERT, /* % */ /* 38 */ ED_INSERT, /* & */ /* 39 */ ED_INSERT, /* ' */ /* 40 */ ED_INSERT, /* ( */ /* 41 */ ED_INSERT, /* ) */ /* 42 */ ED_INSERT, /* * */ /* 43 */ ED_INSERT, /* + */ /* 44 */ ED_INSERT, /* , */ /* 45 */ ED_INSERT, /* - */ /* 46 */ ED_INSERT, /* . */ /* 47 */ ED_INSERT, /* / */ /* 48 */ ED_INSERT, /* 0 */ /* 49 */ ED_INSERT, /* 1 */ /* 50 */ ED_INSERT, /* 2 */ /* 51 */ ED_INSERT, /* 3 */ /* 52 */ ED_INSERT, /* 4 */ /* 53 */ ED_INSERT, /* 5 */ /* 54 */ ED_INSERT, /* 6 */ /* 55 */ ED_INSERT, /* 7 */ /* 56 */ ED_INSERT, /* 8 */ /* 57 */ ED_INSERT, /* 9 */ /* 58 */ ED_INSERT, /* : */ /* 59 */ ED_INSERT, /* ; */ /* 60 */ ED_INSERT, /* < */ /* 61 */ ED_INSERT, /* = */ /* 62 */ ED_INSERT, /* > */ /* 63 */ ED_INSERT, /* ? */ /* 64 */ ED_INSERT, /* @ */ /* 65 */ ED_INSERT, /* A */ /* 66 */ ED_INSERT, /* B */ /* 67 */ ED_INSERT, /* C */ /* 68 */ ED_INSERT, /* D */ /* 69 */ ED_INSERT, /* E */ /* 70 */ ED_INSERT, /* F */ /* 71 */ ED_INSERT, /* G */ /* 72 */ ED_INSERT, /* H */ /* 73 */ ED_INSERT, /* I */ /* 74 */ ED_INSERT, /* J */ /* 75 */ ED_INSERT, /* K */ /* 76 */ ED_INSERT, /* L */ /* 77 */ ED_INSERT, /* M */ /* 78 */ ED_INSERT, /* N */ /* 79 */ ED_INSERT, /* O */ /* 80 */ ED_INSERT, /* P */ /* 81 */ ED_INSERT, /* Q */ /* 82 */ ED_INSERT, /* R */ /* 83 */ ED_INSERT, /* S */ /* 84 */ ED_INSERT, /* T */ /* 85 */ ED_INSERT, /* U */ /* 86 */ ED_INSERT, /* V */ /* 87 */ ED_INSERT, /* W */ /* 88 */ ED_INSERT, /* X */ /* 89 */ ED_INSERT, /* Y */ /* 90 */ ED_INSERT, /* Z */ /* 91 */ ED_INSERT, /* [ */ /* 92 */ ED_INSERT, /* \ */ /* 93 */ ED_INSERT, /* ] */ /* 94 */ ED_INSERT, /* ^ */ /* 95 */ ED_INSERT, /* _ */ /* 96 */ ED_INSERT, /* ` */ /* 97 */ ED_INSERT, /* a */ /* 98 */ ED_INSERT, /* b */ /* 99 */ ED_INSERT, /* c */ /* 100 */ ED_INSERT, /* d */ /* 101 */ ED_INSERT, /* e */ /* 102 */ ED_INSERT, /* f */ /* 103 */ ED_INSERT, /* g */ /* 104 */ ED_INSERT, /* h */ /* 105 */ ED_INSERT, /* i */ /* 106 */ ED_INSERT, /* j */ /* 107 */ ED_INSERT, /* k */ /* 108 */ ED_INSERT, /* l */ /* 109 */ ED_INSERT, /* m */ /* 110 */ ED_INSERT, /* n */ /* 111 */ ED_INSERT, /* o */ /* 112 */ ED_INSERT, /* p */ /* 113 */ ED_INSERT, /* q */ /* 114 */ ED_INSERT, /* r */ /* 115 */ ED_INSERT, /* s */ /* 116 */ ED_INSERT, /* t */ /* 117 */ ED_INSERT, /* u */ /* 118 */ ED_INSERT, /* v */ /* 119 */ ED_INSERT, /* w */ /* 120 */ ED_INSERT, /* x */ /* 121 */ ED_INSERT, /* y */ /* 122 */ ED_INSERT, /* z */ /* 123 */ ED_INSERT, /* { */ /* 124 */ ED_INSERT, /* | */ /* 125 */ ED_INSERT, /* } */ /* 126 */ ED_INSERT, /* ~ */ /* 127 */ VI_DELETE_PREV_CHAR, /* ^? */ /* 128 */ ED_INSERT, /* M-^@ */ /* 129 */ ED_INSERT, /* M-^A */ /* 130 */ ED_INSERT, /* M-^B */ /* 131 */ ED_INSERT, /* M-^C */ /* 132 */ ED_INSERT, /* M-^D */ /* 133 */ ED_INSERT, /* M-^E */ /* 134 */ ED_INSERT, /* M-^F */ /* 135 */ ED_INSERT, /* M-^G */ /* 136 */ ED_INSERT, /* M-^H */ /* 137 */ ED_INSERT, /* M-^I */ /* 138 */ ED_INSERT, /* M-^J */ /* 139 */ ED_INSERT, /* M-^K */ /* 140 */ ED_INSERT, /* M-^L */ /* 141 */ ED_INSERT, /* M-^M */ /* 142 */ ED_INSERT, /* M-^N */ /* 143 */ ED_INSERT, /* M-^O */ /* 144 */ ED_INSERT, /* M-^P */ /* 145 */ ED_INSERT, /* M-^Q */ /* 146 */ ED_INSERT, /* M-^R */ /* 147 */ ED_INSERT, /* M-^S */ /* 148 */ ED_INSERT, /* M-^T */ /* 149 */ ED_INSERT, /* M-^U */ /* 150 */ ED_INSERT, /* M-^V */ /* 151 */ ED_INSERT, /* M-^W */ /* 152 */ ED_INSERT, /* M-^X */ /* 153 */ ED_INSERT, /* M-^Y */ /* 154 */ ED_INSERT, /* M-^Z */ /* 155 */ ED_INSERT, /* M-^[ */ /* 156 */ ED_INSERT, /* M-^\ */ /* 157 */ ED_INSERT, /* M-^] */ /* 158 */ ED_INSERT, /* M-^^ */ /* 159 */ ED_INSERT, /* M-^_ */ /* 160 */ ED_INSERT, /* M-SPACE */ /* 161 */ ED_INSERT, /* M-! */ /* 162 */ ED_INSERT, /* M-" */ /* 163 */ ED_INSERT, /* M-# */ /* 164 */ ED_INSERT, /* M-$ */ /* 165 */ ED_INSERT, /* M-% */ /* 166 */ ED_INSERT, /* M-& */ /* 167 */ ED_INSERT, /* M-' */ /* 168 */ ED_INSERT, /* M-( */ /* 169 */ ED_INSERT, /* M-) */ /* 170 */ ED_INSERT, /* M-* */ /* 171 */ ED_INSERT, /* M-+ */ /* 172 */ ED_INSERT, /* M-, */ /* 173 */ ED_INSERT, /* M-- */ /* 174 */ ED_INSERT, /* M-. */ /* 175 */ ED_INSERT, /* M-/ */ /* 176 */ ED_INSERT, /* M-0 */ /* 177 */ ED_INSERT, /* M-1 */ /* 178 */ ED_INSERT, /* M-2 */ /* 179 */ ED_INSERT, /* M-3 */ /* 180 */ ED_INSERT, /* M-4 */ /* 181 */ ED_INSERT, /* M-5 */ /* 182 */ ED_INSERT, /* M-6 */ /* 183 */ ED_INSERT, /* M-7 */ /* 184 */ ED_INSERT, /* M-8 */ /* 185 */ ED_INSERT, /* M-9 */ /* 186 */ ED_INSERT, /* M-: */ /* 187 */ ED_INSERT, /* M-; */ /* 188 */ ED_INSERT, /* M-< */ /* 189 */ ED_INSERT, /* M-= */ /* 190 */ ED_INSERT, /* M-> */ /* 191 */ ED_INSERT, /* M-? */ /* 192 */ ED_INSERT, /* M-@ */ /* 193 */ ED_INSERT, /* M-A */ /* 194 */ ED_INSERT, /* M-B */ /* 195 */ ED_INSERT, /* M-C */ /* 196 */ ED_INSERT, /* M-D */ /* 197 */ ED_INSERT, /* M-E */ /* 198 */ ED_INSERT, /* M-F */ /* 199 */ ED_INSERT, /* M-G */ /* 200 */ ED_INSERT, /* M-H */ /* 201 */ ED_INSERT, /* M-I */ /* 202 */ ED_INSERT, /* M-J */ /* 203 */ ED_INSERT, /* M-K */ /* 204 */ ED_INSERT, /* M-L */ /* 205 */ ED_INSERT, /* M-M */ /* 206 */ ED_INSERT, /* M-N */ /* 207 */ ED_INSERT, /* M-O */ /* 208 */ ED_INSERT, /* M-P */ /* 209 */ ED_INSERT, /* M-Q */ /* 210 */ ED_INSERT, /* M-R */ /* 211 */ ED_INSERT, /* M-S */ /* 212 */ ED_INSERT, /* M-T */ /* 213 */ ED_INSERT, /* M-U */ /* 214 */ ED_INSERT, /* M-V */ /* 215 */ ED_INSERT, /* M-W */ /* 216 */ ED_INSERT, /* M-X */ /* 217 */ ED_INSERT, /* M-Y */ /* 218 */ ED_INSERT, /* M-Z */ /* 219 */ ED_INSERT, /* M-[ */ /* 220 */ ED_INSERT, /* M-\ */ /* 221 */ ED_INSERT, /* M-] */ /* 222 */ ED_INSERT, /* M-^ */ /* 223 */ ED_INSERT, /* M-_ */ /* 224 */ ED_INSERT, /* M-` */ /* 225 */ ED_INSERT, /* M-a */ /* 226 */ ED_INSERT, /* M-b */ /* 227 */ ED_INSERT, /* M-c */ /* 228 */ ED_INSERT, /* M-d */ /* 229 */ ED_INSERT, /* M-e */ /* 230 */ ED_INSERT, /* M-f */ /* 231 */ ED_INSERT, /* M-g */ /* 232 */ ED_INSERT, /* M-h */ /* 233 */ ED_INSERT, /* M-i */ /* 234 */ ED_INSERT, /* M-j */ /* 235 */ ED_INSERT, /* M-k */ /* 236 */ ED_INSERT, /* M-l */ /* 237 */ ED_INSERT, /* M-m */ /* 238 */ ED_INSERT, /* M-n */ /* 239 */ ED_INSERT, /* M-o */ /* 240 */ ED_INSERT, /* M-p */ /* 241 */ ED_INSERT, /* M-q */ /* 242 */ ED_INSERT, /* M-r */ /* 243 */ ED_INSERT, /* M-s */ /* 244 */ ED_INSERT, /* M-t */ /* 245 */ ED_INSERT, /* M-u */ /* 246 */ ED_INSERT, /* M-v */ /* 247 */ ED_INSERT, /* M-w */ /* 248 */ ED_INSERT, /* M-x */ /* 249 */ ED_INSERT, /* M-y */ /* 250 */ ED_INSERT, /* M-z */ /* 251 */ ED_INSERT, /* M-{ */ /* 252 */ ED_INSERT, /* M-| */ /* 253 */ ED_INSERT, /* M-} */ /* 254 */ ED_INSERT, /* M-~ */ /* 255 */ ED_INSERT /* M-^? */ }; static const el_action_t el_map_vi_command[] = { /* 0 */ ED_UNASSIGNED, /* ^@ */ /* 1 */ ED_MOVE_TO_BEG, /* ^A */ /* 2 */ ED_UNASSIGNED, /* ^B */ /* 3 */ ED_IGNORE, /* ^C */ /* 4 */ ED_UNASSIGNED, /* ^D */ /* 5 */ ED_MOVE_TO_END, /* ^E */ /* 6 */ ED_UNASSIGNED, /* ^F */ /* 7 */ ED_UNASSIGNED, /* ^G */ /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */ /* 9 */ ED_UNASSIGNED, /* ^I */ /* 10 */ ED_NEWLINE, /* ^J */ /* 11 */ ED_KILL_LINE, /* ^K */ /* 12 */ ED_CLEAR_SCREEN, /* ^L */ /* 13 */ ED_NEWLINE, /* ^M */ /* 14 */ ED_NEXT_HISTORY, /* ^N */ /* 15 */ ED_IGNORE, /* ^O */ /* 16 */ ED_PREV_HISTORY, /* ^P */ /* 17 */ ED_IGNORE, /* ^Q */ /* 18 */ ED_REDISPLAY, /* ^R */ /* 19 */ ED_IGNORE, /* ^S */ /* 20 */ ED_UNASSIGNED, /* ^T */ /* 21 */ VI_KILL_LINE_PREV, /* ^U */ /* 22 */ ED_UNASSIGNED, /* ^V */ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ /* 24 */ ED_UNASSIGNED, /* ^X */ /* 25 */ ED_UNASSIGNED, /* ^Y */ /* 26 */ ED_UNASSIGNED, /* ^Z */ /* 27 */ EM_META_NEXT, /* ^[ */ /* 28 */ ED_IGNORE, /* ^\ */ /* 29 */ ED_UNASSIGNED, /* ^] */ /* 30 */ ED_UNASSIGNED, /* ^^ */ /* 31 */ ED_UNASSIGNED, /* ^_ */ /* 32 */ ED_NEXT_CHAR, /* SPACE */ /* 33 */ ED_UNASSIGNED, /* ! */ /* 34 */ ED_UNASSIGNED, /* " */ /* 35 */ VI_COMMENT_OUT, /* # */ /* 36 */ ED_MOVE_TO_END, /* $ */ /* 37 */ VI_MATCH, /* % */ /* 38 */ ED_UNASSIGNED, /* & */ /* 39 */ ED_UNASSIGNED, /* ' */ /* 40 */ ED_UNASSIGNED, /* ( */ /* 41 */ ED_UNASSIGNED, /* ) */ /* 42 */ ED_UNASSIGNED, /* * */ /* 43 */ ED_NEXT_HISTORY, /* + */ /* 44 */ VI_REPEAT_PREV_CHAR, /* , */ /* 45 */ ED_PREV_HISTORY, /* - */ /* 46 */ VI_REDO, /* . */ /* 47 */ VI_SEARCH_PREV, /* / */ /* 48 */ VI_ZERO, /* 0 */ /* 49 */ ED_ARGUMENT_DIGIT, /* 1 */ /* 50 */ ED_ARGUMENT_DIGIT, /* 2 */ /* 51 */ ED_ARGUMENT_DIGIT, /* 3 */ /* 52 */ ED_ARGUMENT_DIGIT, /* 4 */ /* 53 */ ED_ARGUMENT_DIGIT, /* 5 */ /* 54 */ ED_ARGUMENT_DIGIT, /* 6 */ /* 55 */ ED_ARGUMENT_DIGIT, /* 7 */ /* 56 */ ED_ARGUMENT_DIGIT, /* 8 */ /* 57 */ ED_ARGUMENT_DIGIT, /* 9 */ /* 58 */ ED_COMMAND, /* : */ /* 59 */ VI_REPEAT_NEXT_CHAR, /* ; */ /* 60 */ ED_UNASSIGNED, /* < */ /* 61 */ ED_UNASSIGNED, /* = */ /* 62 */ ED_UNASSIGNED, /* > */ /* 63 */ VI_SEARCH_NEXT, /* ? */ /* 64 */ VI_ALIAS, /* @ */ /* 65 */ VI_ADD_AT_EOL, /* A */ /* 66 */ VI_PREV_BIG_WORD, /* B */ /* 67 */ VI_CHANGE_TO_EOL, /* C */ /* 68 */ ED_KILL_LINE, /* D */ /* 69 */ VI_END_BIG_WORD, /* E */ /* 70 */ VI_PREV_CHAR, /* F */ /* 71 */ VI_TO_HISTORY_LINE, /* G */ /* 72 */ ED_UNASSIGNED, /* H */ /* 73 */ VI_INSERT_AT_BOL, /* I */ /* 74 */ ED_SEARCH_NEXT_HISTORY, /* J */ /* 75 */ ED_SEARCH_PREV_HISTORY, /* K */ /* 76 */ ED_UNASSIGNED, /* L */ /* 77 */ ED_UNASSIGNED, /* M */ /* 78 */ VI_REPEAT_SEARCH_PREV, /* N */ /* 79 */ ED_SEQUENCE_LEAD_IN, /* O */ /* 80 */ VI_PASTE_PREV, /* P */ /* 81 */ ED_UNASSIGNED, /* Q */ /* 82 */ VI_REPLACE_MODE, /* R */ /* 83 */ VI_SUBSTITUTE_LINE, /* S */ /* 84 */ VI_TO_PREV_CHAR, /* T */ /* 85 */ VI_UNDO_LINE, /* U */ /* 86 */ ED_UNASSIGNED, /* V */ /* 87 */ VI_NEXT_BIG_WORD, /* W */ /* 88 */ ED_DELETE_PREV_CHAR, /* X */ /* 89 */ VI_YANK_END, /* Y */ /* 90 */ ED_UNASSIGNED, /* Z */ /* 91 */ ED_SEQUENCE_LEAD_IN, /* [ */ /* 92 */ ED_UNASSIGNED, /* \ */ /* 93 */ ED_UNASSIGNED, /* ] */ /* 94 */ ED_MOVE_TO_BEG, /* ^ */ /* 95 */ VI_HISTORY_WORD, /* _ */ /* 96 */ ED_UNASSIGNED, /* ` */ /* 97 */ VI_ADD, /* a */ /* 98 */ VI_PREV_WORD, /* b */ /* 99 */ VI_CHANGE_META, /* c */ /* 100 */ VI_DELETE_META, /* d */ /* 101 */ VI_END_WORD, /* e */ /* 102 */ VI_NEXT_CHAR, /* f */ /* 103 */ ED_UNASSIGNED, /* g */ /* 104 */ ED_PREV_CHAR, /* h */ /* 105 */ VI_INSERT, /* i */ /* 106 */ ED_NEXT_HISTORY, /* j */ /* 107 */ ED_PREV_HISTORY, /* k */ /* 108 */ ED_NEXT_CHAR, /* l */ /* 109 */ ED_UNASSIGNED, /* m */ /* 110 */ VI_REPEAT_SEARCH_NEXT, /* n */ /* 111 */ ED_UNASSIGNED, /* o */ /* 112 */ VI_PASTE_NEXT, /* p */ /* 113 */ ED_UNASSIGNED, /* q */ /* 114 */ VI_REPLACE_CHAR, /* r */ /* 115 */ VI_SUBSTITUTE_CHAR, /* s */ /* 116 */ VI_TO_NEXT_CHAR, /* t */ /* 117 */ VI_UNDO, /* u */ /* 118 */ VI_HISTEDIT, /* v */ /* 119 */ VI_NEXT_WORD, /* w */ /* 120 */ ED_DELETE_NEXT_CHAR, /* x */ /* 121 */ VI_YANK, /* y */ /* 122 */ ED_UNASSIGNED, /* z */ /* 123 */ ED_UNASSIGNED, /* { */ /* 124 */ VI_TO_COLUMN, /* | */ /* 125 */ ED_UNASSIGNED, /* } */ /* 126 */ VI_CHANGE_CASE, /* ~ */ /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */ /* 128 */ ED_UNASSIGNED, /* M-^@ */ /* 129 */ ED_UNASSIGNED, /* M-^A */ /* 130 */ ED_UNASSIGNED, /* M-^B */ /* 131 */ ED_UNASSIGNED, /* M-^C */ /* 132 */ ED_UNASSIGNED, /* M-^D */ /* 133 */ ED_UNASSIGNED, /* M-^E */ /* 134 */ ED_UNASSIGNED, /* M-^F */ /* 135 */ ED_UNASSIGNED, /* M-^G */ /* 136 */ ED_UNASSIGNED, /* M-^H */ /* 137 */ ED_UNASSIGNED, /* M-^I */ /* 138 */ ED_UNASSIGNED, /* M-^J */ /* 139 */ ED_UNASSIGNED, /* M-^K */ /* 140 */ ED_UNASSIGNED, /* M-^L */ /* 141 */ ED_UNASSIGNED, /* M-^M */ /* 142 */ ED_UNASSIGNED, /* M-^N */ /* 143 */ ED_UNASSIGNED, /* M-^O */ /* 144 */ ED_UNASSIGNED, /* M-^P */ /* 145 */ ED_UNASSIGNED, /* M-^Q */ /* 146 */ ED_UNASSIGNED, /* M-^R */ /* 147 */ ED_UNASSIGNED, /* M-^S */ /* 148 */ ED_UNASSIGNED, /* M-^T */ /* 149 */ ED_UNASSIGNED, /* M-^U */ /* 150 */ ED_UNASSIGNED, /* M-^V */ /* 151 */ ED_UNASSIGNED, /* M-^W */ /* 152 */ ED_UNASSIGNED, /* M-^X */ /* 153 */ ED_UNASSIGNED, /* M-^Y */ /* 154 */ ED_UNASSIGNED, /* M-^Z */ /* 155 */ ED_UNASSIGNED, /* M-^[ */ /* 156 */ ED_UNASSIGNED, /* M-^\ */ /* 157 */ ED_UNASSIGNED, /* M-^] */ /* 158 */ ED_UNASSIGNED, /* M-^^ */ /* 159 */ ED_UNASSIGNED, /* M-^_ */ /* 160 */ ED_UNASSIGNED, /* M-SPACE */ /* 161 */ ED_UNASSIGNED, /* M-! */ /* 162 */ ED_UNASSIGNED, /* M-" */ /* 163 */ ED_UNASSIGNED, /* M-# */ /* 164 */ ED_UNASSIGNED, /* M-$ */ /* 165 */ ED_UNASSIGNED, /* M-% */ /* 166 */ ED_UNASSIGNED, /* M-& */ /* 167 */ ED_UNASSIGNED, /* M-' */ /* 168 */ ED_UNASSIGNED, /* M-( */ /* 169 */ ED_UNASSIGNED, /* M-) */ /* 170 */ ED_UNASSIGNED, /* M-* */ /* 171 */ ED_UNASSIGNED, /* M-+ */ /* 172 */ ED_UNASSIGNED, /* M-, */ /* 173 */ ED_UNASSIGNED, /* M-- */ /* 174 */ ED_UNASSIGNED, /* M-. */ /* 175 */ ED_UNASSIGNED, /* M-/ */ /* 176 */ ED_UNASSIGNED, /* M-0 */ /* 177 */ ED_UNASSIGNED, /* M-1 */ /* 178 */ ED_UNASSIGNED, /* M-2 */ /* 179 */ ED_UNASSIGNED, /* M-3 */ /* 180 */ ED_UNASSIGNED, /* M-4 */ /* 181 */ ED_UNASSIGNED, /* M-5 */ /* 182 */ ED_UNASSIGNED, /* M-6 */ /* 183 */ ED_UNASSIGNED, /* M-7 */ /* 184 */ ED_UNASSIGNED, /* M-8 */ /* 185 */ ED_UNASSIGNED, /* M-9 */ /* 186 */ ED_UNASSIGNED, /* M-: */ /* 187 */ ED_UNASSIGNED, /* M-; */ /* 188 */ ED_UNASSIGNED, /* M-< */ /* 189 */ ED_UNASSIGNED, /* M-= */ /* 190 */ ED_UNASSIGNED, /* M-> */ /* 191 */ ED_UNASSIGNED, /* M-? */ /* 192 */ ED_UNASSIGNED, /* M-@ */ /* 193 */ ED_UNASSIGNED, /* M-A */ /* 194 */ ED_UNASSIGNED, /* M-B */ /* 195 */ ED_UNASSIGNED, /* M-C */ /* 196 */ ED_UNASSIGNED, /* M-D */ /* 197 */ ED_UNASSIGNED, /* M-E */ /* 198 */ ED_UNASSIGNED, /* M-F */ /* 199 */ ED_UNASSIGNED, /* M-G */ /* 200 */ ED_UNASSIGNED, /* M-H */ /* 201 */ ED_UNASSIGNED, /* M-I */ /* 202 */ ED_UNASSIGNED, /* M-J */ /* 203 */ ED_UNASSIGNED, /* M-K */ /* 204 */ ED_UNASSIGNED, /* M-L */ /* 205 */ ED_UNASSIGNED, /* M-M */ /* 206 */ ED_UNASSIGNED, /* M-N */ /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */ /* 208 */ ED_UNASSIGNED, /* M-P */ /* 209 */ ED_UNASSIGNED, /* M-Q */ /* 210 */ ED_UNASSIGNED, /* M-R */ /* 211 */ ED_UNASSIGNED, /* M-S */ /* 212 */ ED_UNASSIGNED, /* M-T */ /* 213 */ ED_UNASSIGNED, /* M-U */ /* 214 */ ED_UNASSIGNED, /* M-V */ /* 215 */ ED_UNASSIGNED, /* M-W */ /* 216 */ ED_UNASSIGNED, /* M-X */ /* 217 */ ED_UNASSIGNED, /* M-Y */ /* 218 */ ED_UNASSIGNED, /* M-Z */ /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */ /* 220 */ ED_UNASSIGNED, /* M-\ */ /* 221 */ ED_UNASSIGNED, /* M-] */ /* 222 */ ED_UNASSIGNED, /* M-^ */ /* 223 */ ED_UNASSIGNED, /* M-_ */ /* 224 */ ED_UNASSIGNED, /* M-` */ /* 225 */ ED_UNASSIGNED, /* M-a */ /* 226 */ ED_UNASSIGNED, /* M-b */ /* 227 */ ED_UNASSIGNED, /* M-c */ /* 228 */ ED_UNASSIGNED, /* M-d */ /* 229 */ ED_UNASSIGNED, /* M-e */ /* 230 */ ED_UNASSIGNED, /* M-f */ /* 231 */ ED_UNASSIGNED, /* M-g */ /* 232 */ ED_UNASSIGNED, /* M-h */ /* 233 */ ED_UNASSIGNED, /* M-i */ /* 234 */ ED_UNASSIGNED, /* M-j */ /* 235 */ ED_UNASSIGNED, /* M-k */ /* 236 */ ED_UNASSIGNED, /* M-l */ /* 237 */ ED_UNASSIGNED, /* M-m */ /* 238 */ ED_UNASSIGNED, /* M-n */ /* 239 */ ED_UNASSIGNED, /* M-o */ /* 240 */ ED_UNASSIGNED, /* M-p */ /* 241 */ ED_UNASSIGNED, /* M-q */ /* 242 */ ED_UNASSIGNED, /* M-r */ /* 243 */ ED_UNASSIGNED, /* M-s */ /* 244 */ ED_UNASSIGNED, /* M-t */ /* 245 */ ED_UNASSIGNED, /* M-u */ /* 246 */ ED_UNASSIGNED, /* M-v */ /* 247 */ ED_UNASSIGNED, /* M-w */ /* 248 */ ED_UNASSIGNED, /* M-x */ /* 249 */ ED_UNASSIGNED, /* M-y */ /* 250 */ ED_UNASSIGNED, /* M-z */ /* 251 */ ED_UNASSIGNED, /* M-{ */ /* 252 */ ED_UNASSIGNED, /* M-| */ /* 253 */ ED_UNASSIGNED, /* M-} */ /* 254 */ ED_UNASSIGNED, /* M-~ */ /* 255 */ ED_UNASSIGNED /* M-^? */ }; /* map_init(): * Initialize and allocate the maps */ libedit_private int map_init(EditLine *el) { /* * Make sure those are correct before starting. */ #ifdef MAP_DEBUG if (sizeof(el_map_emacs) != N_KEYS * sizeof(el_action_t)) EL_ABORT((el->el_errfile, "Emacs map incorrect\n")); if (sizeof(el_map_vi_command) != N_KEYS * sizeof(el_action_t)) EL_ABORT((el->el_errfile, "Vi command map incorrect\n")); if (sizeof(el_map_vi_insert) != N_KEYS * sizeof(el_action_t)) EL_ABORT((el->el_errfile, "Vi insert map incorrect\n")); #endif el->el_map.alt = el_calloc(N_KEYS, sizeof(*el->el_map.alt)); if (el->el_map.alt == NULL) return -1; el->el_map.key = el_calloc(N_KEYS, sizeof(*el->el_map.key)); if (el->el_map.key == NULL) goto out; el->el_map.emacs = el_map_emacs; el->el_map.vic = el_map_vi_command; el->el_map.vii = el_map_vi_insert; el->el_map.help = el_calloc(EL_NUM_FCNS, sizeof(*el->el_map.help)); if (el->el_map.help == NULL) goto out; (void) memcpy(el->el_map.help, el_func_help, sizeof(*el->el_map.help) * EL_NUM_FCNS); el->el_map.func = el_calloc(EL_NUM_FCNS, sizeof(*el->el_map.func)); if (el->el_map.func == NULL) goto out; memcpy(el->el_map.func, el_func, sizeof(*el->el_map.func) * EL_NUM_FCNS); el->el_map.nfunc = EL_NUM_FCNS; #ifdef VIDEFAULT map_init_vi(el); #else map_init_emacs(el); #endif /* VIDEFAULT */ return 0; out: map_end(el); return -1; } /* map_end(): * Free the space taken by the editor maps */ libedit_private void map_end(EditLine *el) { el_free(el->el_map.alt); el->el_map.alt = NULL; el_free(el->el_map.key); el->el_map.key = NULL; el->el_map.emacs = NULL; el->el_map.vic = NULL; el->el_map.vii = NULL; el_free(el->el_map.help); el->el_map.help = NULL; el_free(el->el_map.func); el->el_map.func = NULL; } /* map_init_nls(): * Find all the printable keys and bind them to self insert */ static void map_init_nls(EditLine *el) { int i; el_action_t *map = el->el_map.key; for (i = 0200; i <= 0377; i++) if (iswprint(i)) map[i] = ED_INSERT; } /* map_init_meta(): * Bind all the meta keys to the appropriate ESC- sequence */ static void map_init_meta(EditLine *el) { wchar_t buf[3]; int i; el_action_t *map = el->el_map.key; el_action_t *alt = el->el_map.alt; for (i = 0; i <= 0377 && map[i] != EM_META_NEXT; i++) continue; if (i > 0377) { for (i = 0; i <= 0377 && alt[i] != EM_META_NEXT; i++) continue; if (i > 0377) { i = 033; if (el->el_map.type == MAP_VI) map = alt; } else map = alt; } buf[0] = (wchar_t)i; buf[2] = 0; for (i = 0200; i <= 0377; i++) switch (map[i]) { case ED_INSERT: case ED_UNASSIGNED: case ED_SEQUENCE_LEAD_IN: break; default: buf[1] = i & 0177; keymacro_add(el, buf, keymacro_map_cmd(el, (int) map[i]), XK_CMD); break; } map[(int) buf[0]] = ED_SEQUENCE_LEAD_IN; } /* map_init_vi(): * Initialize the vi bindings */ libedit_private void map_init_vi(EditLine *el) { int i; el_action_t *key = el->el_map.key; el_action_t *alt = el->el_map.alt; const el_action_t *vii = el->el_map.vii; const el_action_t *vic = el->el_map.vic; el->el_map.type = MAP_VI; el->el_map.current = el->el_map.key; keymacro_reset(el); for (i = 0; i < N_KEYS; i++) { key[i] = vii[i]; alt[i] = vic[i]; } map_init_meta(el); map_init_nls(el); tty_bind_char(el, 1); terminal_bind_arrow(el); } /* map_init_emacs(): * Initialize the emacs bindings */ libedit_private void map_init_emacs(EditLine *el) { int i; wchar_t buf[3]; el_action_t *key = el->el_map.key; el_action_t *alt = el->el_map.alt; const el_action_t *emacs = el->el_map.emacs; el->el_map.type = MAP_EMACS; el->el_map.current = el->el_map.key; keymacro_reset(el); for (i = 0; i < N_KEYS; i++) { key[i] = emacs[i]; alt[i] = ED_UNASSIGNED; } map_init_meta(el); map_init_nls(el); buf[0] = CONTROL('X'); buf[1] = CONTROL('X'); buf[2] = 0; keymacro_add(el, buf, keymacro_map_cmd(el, EM_EXCHANGE_MARK), XK_CMD); tty_bind_char(el, 1); terminal_bind_arrow(el); } /* map_set_editor(): * Set the editor */ libedit_private int map_set_editor(EditLine *el, wchar_t *editor) { if (wcscmp(editor, L"emacs") == 0) { map_init_emacs(el); return 0; } if (wcscmp(editor, L"vi") == 0) { map_init_vi(el); return 0; } return -1; } /* map_get_editor(): * Retrieve the editor */ libedit_private int map_get_editor(EditLine *el, const wchar_t **editor) { if (editor == NULL) return -1; switch (el->el_map.type) { case MAP_EMACS: *editor = L"emacs"; return 0; case MAP_VI: *editor = L"vi"; return 0; } return -1; } /* map_print_key(): * Print the function description for 1 key */ static void map_print_key(EditLine *el, el_action_t *map, const wchar_t *in) { char outbuf[EL_BUFSIZ]; el_bindings_t *bp, *ep; if (in[0] == '\0' || in[1] == '\0') { (void) keymacro__decode_str(in, outbuf, sizeof(outbuf), ""); ep = &el->el_map.help[el->el_map.nfunc]; for (bp = el->el_map.help; bp < ep; bp++) if (bp->func == map[(unsigned char) *in]) { (void) fprintf(el->el_outfile, "%s\t->\t%ls\n", outbuf, bp->name); return; } } else keymacro_print(el, in); } /* map_print_some_keys(): * Print keys from first to last */ static void map_print_some_keys(EditLine *el, el_action_t *map, wint_t first, wint_t last) { el_bindings_t *bp, *ep; wchar_t firstbuf[2], lastbuf[2]; char unparsbuf[EL_BUFSIZ], extrabuf[EL_BUFSIZ]; firstbuf[0] = first; firstbuf[1] = 0; lastbuf[0] = last; lastbuf[1] = 0; if (map[first] == ED_UNASSIGNED) { if (first == last) { (void) keymacro__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "%-15s-> is undefined\n", unparsbuf); } return; } ep = &el->el_map.help[el->el_map.nfunc]; for (bp = el->el_map.help; bp < ep; bp++) { if (bp->func == map[first]) { if (first == last) { (void) keymacro__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "%-15s-> %ls\n", unparsbuf, bp->name); } else { (void) keymacro__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) keymacro__decode_str(lastbuf, extrabuf, sizeof(extrabuf), STRQQ); (void) fprintf(el->el_outfile, "%-4s to %-7s-> %ls\n", unparsbuf, extrabuf, bp->name); } return; } } #ifdef MAP_DEBUG if (map == el->el_map.key) { (void) keymacro__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "BUG!!! %s isn't bound to anything.\n", unparsbuf); (void) fprintf(el->el_outfile, "el->el_map.key[%d] == %d\n", first, el->el_map.key[first]); } else { (void) keymacro__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "BUG!!! %s isn't bound to anything.\n", unparsbuf); (void) fprintf(el->el_outfile, "el->el_map.alt[%d] == %d\n", first, el->el_map.alt[first]); } #endif EL_ABORT((el->el_errfile, "Error printing keys\n")); } /* map_print_all_keys(): * Print the function description for all keys. */ static void map_print_all_keys(EditLine *el) { int prev, i; (void) fprintf(el->el_outfile, "Standard key bindings\n"); prev = 0; for (i = 0; i < N_KEYS; i++) { if (el->el_map.key[prev] == el->el_map.key[i]) continue; map_print_some_keys(el, el->el_map.key, prev, i - 1); prev = i; } map_print_some_keys(el, el->el_map.key, prev, i - 1); (void) fprintf(el->el_outfile, "Alternative key bindings\n"); prev = 0; for (i = 0; i < N_KEYS; i++) { if (el->el_map.alt[prev] == el->el_map.alt[i]) continue; map_print_some_keys(el, el->el_map.alt, prev, i - 1); prev = i; } map_print_some_keys(el, el->el_map.alt, prev, i - 1); (void) fprintf(el->el_outfile, "Multi-character bindings\n"); keymacro_print(el, L""); (void) fprintf(el->el_outfile, "Arrow key bindings\n"); terminal_print_arrow(el, L""); } /* map_bind(): * Add/remove/change bindings */ libedit_private int map_bind(EditLine *el, int argc, const wchar_t **argv) { el_action_t *map; int ntype, rem; const wchar_t *p; wchar_t inbuf[EL_BUFSIZ]; wchar_t outbuf[EL_BUFSIZ]; const wchar_t *in = NULL; wchar_t *out; el_bindings_t *bp, *ep; int cmd; int key; if (argv == NULL) return -1; map = el->el_map.key; ntype = XK_CMD; key = rem = 0; for (argc = 1; (p = argv[argc]) != NULL; argc++) if (p[0] == '-') switch (p[1]) { case 'a': map = el->el_map.alt; break; case 's': ntype = XK_STR; break; case 'k': key = 1; break; case 'r': rem = 1; break; case 'v': map_init_vi(el); return 0; case 'e': map_init_emacs(el); return 0; case 'l': ep = &el->el_map.help[el->el_map.nfunc]; for (bp = el->el_map.help; bp < ep; bp++) (void) fprintf(el->el_outfile, "%ls\n\t%ls\n", bp->name, bp->description); return 0; default: (void) fprintf(el->el_errfile, "%ls: Invalid switch `%lc'.\n", argv[0], (wint_t)p[1]); } else break; if (argv[argc] == NULL) { map_print_all_keys(el); return 0; } if (key) in = argv[argc++]; else if ((in = parse__string(inbuf, argv[argc++])) == NULL) { (void) fprintf(el->el_errfile, "%ls: Invalid \\ or ^ in instring.\n", argv[0]); return -1; } if (rem) { if (key) { (void) terminal_clear_arrow(el, in); return -1; } if (in[1]) (void) keymacro_delete(el, in); else if (map[(unsigned char) *in] == ED_SEQUENCE_LEAD_IN) (void) keymacro_delete(el, in); else map[(unsigned char) *in] = ED_UNASSIGNED; return 0; } if (argv[argc] == NULL) { if (key) terminal_print_arrow(el, in); else map_print_key(el, map, in); return 0; } #ifdef notyet if (argv[argc + 1] != NULL) { bindkeymacro_usage(); return -1; } #endif switch (ntype) { case XK_STR: if ((out = parse__string(outbuf, argv[argc])) == NULL) { (void) fprintf(el->el_errfile, "%ls: Invalid \\ or ^ in outstring.\n", argv[0]); return -1; } if (key) terminal_set_arrow(el, in, keymacro_map_str(el, out), ntype); else keymacro_add(el, in, keymacro_map_str(el, out), ntype); map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN; break; case XK_CMD: if ((cmd = parse_cmd(el, argv[argc])) == -1) { (void) fprintf(el->el_errfile, "%ls: Invalid command `%ls'.\n", argv[0], argv[argc]); return -1; } if (key) terminal_set_arrow(el, in, keymacro_map_cmd(el, cmd), ntype); else { if (in[1]) { keymacro_add(el, in, keymacro_map_cmd(el, cmd), ntype); map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN; } else { keymacro_clear(el, map, in); map[(unsigned char) *in] = (el_action_t)cmd; } } break; /* coverity[dead_error_begin] */ default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); - break; } return 0; } /* map_addfunc(): * add a user defined function */ libedit_private int map_addfunc(EditLine *el, const wchar_t *name, const wchar_t *help, el_func_t func) { void *p; size_t nf = el->el_map.nfunc + 1; if (name == NULL || help == NULL || func == NULL) return -1; if ((p = el_realloc(el->el_map.func, nf * sizeof(*el->el_map.func))) == NULL) return -1; el->el_map.func = p; if ((p = el_realloc(el->el_map.help, nf * sizeof(*el->el_map.help))) == NULL) return -1; el->el_map.help = p; nf = (size_t)el->el_map.nfunc; el->el_map.func[nf] = func; el->el_map.help[nf].name = name; el->el_map.help[nf].func = (int)nf; el->el_map.help[nf].description = help; el->el_map.nfunc++; return 0; } diff --git a/contrib/libedit/read.c b/contrib/libedit/read.c index 8026ca4a209b..4d068e9f4097 100644 --- a/contrib/libedit/read.c +++ b/contrib/libedit/read.c @@ -1,624 +1,623 @@ -/* $NetBSD: read.c,v 1.108 2022/10/30 19:11:31 christos Exp $ */ +/* $NetBSD: read.c,v 1.109 2025/01/03 00:40:08 rillig Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: read.c,v 1.108 2022/10/30 19:11:31 christos Exp $"); +__RCSID("$NetBSD: read.c,v 1.109 2025/01/03 00:40:08 rillig Exp $"); #endif #endif /* not lint && not SCCSID */ /* * read.c: Terminal read functions */ #include #include #include #include #include #include #include #include "el.h" #include "fcns.h" #include "read.h" #define EL_MAXMACRO 10 struct macros { wchar_t **macro; int level; int offset; }; struct el_read_t { struct macros macros; el_rfunc_t read_char; /* Function to read a character. */ int read_errno; }; static int read__fixio(int, int); static int read_char(EditLine *, wchar_t *); static int read_getcmd(EditLine *, el_action_t *, wchar_t *); static void read_clearmacros(struct macros *); static void read_pop(struct macros *); static const wchar_t *noedit_wgets(EditLine *, int *); /* read_init(): * Initialize the read stuff */ libedit_private int read_init(EditLine *el) { struct macros *ma; if ((el->el_read = el_malloc(sizeof(*el->el_read))) == NULL) return -1; ma = &el->el_read->macros; if ((ma->macro = el_calloc(EL_MAXMACRO, sizeof(*ma->macro))) == NULL) goto out; ma->level = -1; ma->offset = 0; /* builtin read_char */ el->el_read->read_char = read_char; return 0; out: read_end(el); return -1; } /* el_read_end(): * Free the data structures used by the read stuff. */ libedit_private void read_end(EditLine *el) { read_clearmacros(&el->el_read->macros); el_free(el->el_read->macros.macro); el->el_read->macros.macro = NULL; el_free(el->el_read); el->el_read = NULL; } /* el_read_setfn(): * Set the read char function to the one provided. * If it is set to EL_BUILTIN_GETCFN, then reset to the builtin one. */ libedit_private int el_read_setfn(struct el_read_t *el_read, el_rfunc_t rc) { el_read->read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc; return 0; } /* el_read_getfn(): * return the current read char function, or EL_BUILTIN_GETCFN * if it is the default one */ libedit_private el_rfunc_t el_read_getfn(struct el_read_t *el_read) { return el_read->read_char == read_char ? EL_BUILTIN_GETCFN : el_read->read_char; } /* read__fixio(): * Try to recover from a read error */ /* ARGSUSED */ static int read__fixio(int fd __attribute__((__unused__)), int e) { switch (e) { case -1: /* Make sure that the code is reachable */ #ifdef EWOULDBLOCK case EWOULDBLOCK: #ifndef TRY_AGAIN #define TRY_AGAIN #endif #endif /* EWOULDBLOCK */ #if defined(POSIX) && defined(EAGAIN) #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EAGAIN: #ifndef TRY_AGAIN #define TRY_AGAIN #endif #endif /* EWOULDBLOCK && EWOULDBLOCK != EAGAIN */ #endif /* POSIX && EAGAIN */ e = 0; #ifdef TRY_AGAIN #if defined(F_SETFL) && defined(O_NDELAY) if ((e = fcntl(fd, F_GETFL, 0)) == -1) return -1; if (fcntl(fd, F_SETFL, e & ~O_NDELAY) == -1) return -1; else e = 1; #endif /* F_SETFL && O_NDELAY */ #ifdef FIONBIO { int zero = 0; if (ioctl(fd, FIONBIO, &zero) == -1) return -1; else e = 1; } #endif /* FIONBIO */ #endif /* TRY_AGAIN */ return e ? 0 : -1; case EINTR: return 0; default: return -1; } } /* el_push(): * Push a macro */ void el_wpush(EditLine *el, const wchar_t *str) { struct macros *ma = &el->el_read->macros; if (str != NULL && ma->level + 1 < EL_MAXMACRO) { ma->level++; if ((ma->macro[ma->level] = wcsdup(str)) != NULL) return; ma->level--; } terminal_beep(el); terminal__flush(el); } /* read_getcmd(): * Get next command from the input stream, * return 0 on success or -1 on EOF or error. * Character values > 255 are not looked up in the map, but inserted. */ static int read_getcmd(EditLine *el, el_action_t *cmdnum, wchar_t *ch) { static const wchar_t meta = (wchar_t)0x80; el_action_t cmd; do { if (el_wgetc(el, ch) != 1) return -1; #ifdef KANJI if ((*ch & meta)) { el->el_state.metanext = 0; cmd = CcViMap[' ']; break; } else #endif /* KANJI */ if (el->el_state.metanext) { el->el_state.metanext = 0; *ch |= meta; } if (*ch >= N_KEYS) cmd = ED_INSERT; else cmd = el->el_map.current[(unsigned char) *ch]; if (cmd == ED_SEQUENCE_LEAD_IN) { keymacro_value_t val; switch (keymacro_get(el, ch, &val)) { case XK_CMD: cmd = val.cmd; break; case XK_STR: el_wpush(el, val.str); break; case XK_NOD: return -1; default: EL_ABORT((el->el_errfile, "Bad XK_ type \n")); - break; } } } while (cmd == ED_SEQUENCE_LEAD_IN); *cmdnum = cmd; return 0; } /* read_char(): * Read a character from the tty. */ static int read_char(EditLine *el, wchar_t *cp) { ssize_t num_read; int tried = (el->el_flags & FIXIO) == 0; char cbuf[MB_LEN_MAX]; size_t cbp = 0; int save_errno = errno; again: el->el_signal->sig_no = 0; while ((num_read = read(el->el_infd, cbuf + cbp, (size_t)1)) == -1) { int e = errno; switch (el->el_signal->sig_no) { case SIGCONT: el_wset(el, EL_REFRESH); /*FALLTHROUGH*/ case SIGWINCH: sig_set(el); goto again; default: break; } if (!tried && read__fixio(el->el_infd, e) == 0) { errno = save_errno; tried = 1; } else { errno = e; *cp = L'\0'; return -1; } } /* Test for EOF */ if (num_read == 0) { *cp = L'\0'; return 0; } for (;;) { mbstate_t mbs; ++cbp; /* This only works because UTF8 is stateless. */ memset(&mbs, 0, sizeof(mbs)); switch (mbrtowc(cp, cbuf, cbp, &mbs)) { case (size_t)-1: if (cbp > 1) { /* * Invalid sequence, discard all bytes * except the last one. */ cbuf[0] = cbuf[cbp - 1]; cbp = 0; break; } else { /* Invalid byte, discard it. */ cbp = 0; goto again; } case (size_t)-2: if (cbp >= MB_LEN_MAX) { errno = EILSEQ; *cp = L'\0'; return -1; } /* Incomplete sequence, read another byte. */ goto again; default: /* Valid character, process it. */ return 1; } } } /* read_pop(): * Pop a macro from the stack */ static void read_pop(struct macros *ma) { int i; el_free(ma->macro[0]); for (i = 0; i < ma->level; i++) ma->macro[i] = ma->macro[i + 1]; ma->level--; ma->offset = 0; } static void read_clearmacros(struct macros *ma) { while (ma->level >= 0) el_free(ma->macro[ma->level--]); ma->offset = 0; } /* el_wgetc(): * Read a wide character */ int el_wgetc(EditLine *el, wchar_t *cp) { struct macros *ma = &el->el_read->macros; int num_read; terminal__flush(el); for (;;) { if (ma->level < 0) break; if (ma->macro[0][ma->offset] == '\0') { read_pop(ma); continue; } *cp = ma->macro[0][ma->offset++]; if (ma->macro[0][ma->offset] == '\0') { /* Needed for QuoteMode On */ read_pop(ma); } return 1; } if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */ return 0; num_read = (*el->el_read->read_char)(el, cp); /* * Remember the original reason of a read failure * such that el_wgets() can restore it after doing * various cleanup operation that might change errno. */ if (num_read < 0) el->el_read->read_errno = errno; return num_read; } libedit_private void read_prepare(EditLine *el) { if (el->el_flags & HANDLE_SIGNALS) sig_set(el); if (el->el_flags & NO_TTY) return; if ((el->el_flags & (UNBUFFERED|EDIT_DISABLED)) == UNBUFFERED) tty_rawmode(el); /* This is relatively cheap, and things go terribly wrong if we have the wrong size. */ el_resize(el); re_clear_display(el); /* reset the display stuff */ ch_reset(el); re_refresh(el); /* print the prompt */ if (el->el_flags & UNBUFFERED) terminal__flush(el); } libedit_private void read_finish(EditLine *el) { if ((el->el_flags & UNBUFFERED) == 0) (void) tty_cookedmode(el); if (el->el_flags & HANDLE_SIGNALS) sig_clr(el); } static const wchar_t * noedit_wgets(EditLine *el, int *nread) { el_line_t *lp = &el->el_line; int num; while ((num = (*el->el_read->read_char)(el, lp->lastchar)) == 1) { if (lp->lastchar + 1 >= lp->limit && !ch_enlargebufs(el, (size_t)2)) break; lp->lastchar++; if (el->el_flags & UNBUFFERED || lp->lastchar[-1] == '\r' || lp->lastchar[-1] == '\n') break; } if (num == -1 && errno == EINTR) lp->lastchar = lp->buffer; lp->cursor = lp->lastchar; *lp->lastchar = '\0'; *nread = (int)(lp->lastchar - lp->buffer); return *nread ? lp->buffer : NULL; } const wchar_t * el_wgets(EditLine *el, int *nread) { int retval; el_action_t cmdnum = 0; int num; /* how many chars we have read at NL */ wchar_t ch; int nrb; if (nread == NULL) nread = &nrb; *nread = 0; el->el_read->read_errno = 0; if (el->el_flags & NO_TTY) { el->el_line.lastchar = el->el_line.buffer; return noedit_wgets(el, nread); } #ifdef FIONREAD if (el->el_tty.t_mode == EX_IO && el->el_read->macros.level < 0) { int chrs = 0; (void) ioctl(el->el_infd, FIONREAD, &chrs); if (chrs == 0) { if (tty_rawmode(el) < 0) { errno = 0; *nread = 0; return NULL; } } } #endif /* FIONREAD */ if ((el->el_flags & UNBUFFERED) == 0) read_prepare(el); if (el->el_flags & EDIT_DISABLED) { if ((el->el_flags & UNBUFFERED) == 0) el->el_line.lastchar = el->el_line.buffer; terminal__flush(el); return noedit_wgets(el, nread); } for (num = -1; num == -1;) { /* while still editing this line */ /* if EOF or error */ if (read_getcmd(el, &cmdnum, &ch) == -1) break; if ((size_t)cmdnum >= el->el_map.nfunc) /* BUG CHECK command */ continue; /* try again */ /* now do the real command */ /* vi redo needs these way down the levels... */ el->el_state.thiscmd = cmdnum; el->el_state.thisch = ch; if (el->el_map.type == MAP_VI && el->el_map.current == el->el_map.key && el->el_chared.c_redo.pos < el->el_chared.c_redo.lim) { if (cmdnum == VI_DELETE_PREV_CHAR && el->el_chared.c_redo.pos != el->el_chared.c_redo.buf && iswprint(el->el_chared.c_redo.pos[-1])) el->el_chared.c_redo.pos--; else *el->el_chared.c_redo.pos++ = ch; } retval = (*el->el_map.func[cmdnum]) (el, ch); /* save the last command here */ el->el_state.lastcmd = cmdnum; /* use any return value */ switch (retval) { case CC_CURSOR: re_refresh_cursor(el); break; case CC_REDISPLAY: re_clear_lines(el); re_clear_display(el); /* FALLTHROUGH */ case CC_REFRESH: re_refresh(el); break; case CC_REFRESH_BEEP: re_refresh(el); terminal_beep(el); break; case CC_NORM: /* normal char */ break; case CC_ARGHACK: /* Suggested by Rich Salz */ /* */ continue; /* keep going... */ case CC_EOF: /* end of file typed */ if ((el->el_flags & UNBUFFERED) == 0) num = 0; else if (num == -1) { *el->el_line.lastchar++ = CONTROL('d'); el->el_line.cursor = el->el_line.lastchar; num = 1; } break; case CC_NEWLINE: /* normal end of line */ num = (int)(el->el_line.lastchar - el->el_line.buffer); break; case CC_FATAL: /* fatal error, reset to known state */ /* put (real) cursor in a known place */ re_clear_display(el); /* reset the display stuff */ ch_reset(el); /* reset the input pointers */ read_clearmacros(&el->el_read->macros); re_refresh(el); /* print the prompt again */ break; case CC_ERROR: default: /* functions we don't know about */ terminal_beep(el); terminal__flush(el); break; } el->el_state.argument = 1; el->el_state.doingarg = 0; el->el_chared.c_vcmd.action = NOP; if (el->el_flags & UNBUFFERED) break; } terminal__flush(el); /* flush any buffered output */ /* make sure the tty is set up correctly */ if ((el->el_flags & UNBUFFERED) == 0) { read_finish(el); *nread = num != -1 ? num : 0; } else *nread = (int)(el->el_line.lastchar - el->el_line.buffer); if (*nread == 0) { if (num == -1) { *nread = -1; if (el->el_read->read_errno) errno = el->el_read->read_errno; } return NULL; } else return el->el_line.buffer; } diff --git a/contrib/libedit/readline.c b/contrib/libedit/readline.c index ef3abd4b6daa..3b9b6ac6afcb 100644 --- a/contrib/libedit/readline.c +++ b/contrib/libedit/readline.c @@ -1,2623 +1,2625 @@ -/* $NetBSD: readline.c,v 1.178 2022/12/02 19:23:15 christos Exp $ */ +/* $NetBSD: readline.c,v 1.182 2024/03/26 18:02:04 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) -__RCSID("$NetBSD: readline.c,v 1.178 2022/12/02 19:23:15 christos Exp $"); +__RCSID("$NetBSD: readline.c,v 1.182 2024/03/26 18:02:04 christos Exp $"); #endif /* not lint && not SCCSID */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define completion_matches xxx_completion_matches #include "readline/readline.h" +#undef completion_matches #include "el.h" #include "fcns.h" #include "filecomplete.h" void rl_prep_terminal(int); void rl_deprep_terminal(void); /* for rl_complete() */ #define TAB '\r' /* see comment at the #ifdef for sense of this */ /* #define GDB_411_HACK */ /* readline compatibility stuff - look at readline sources/documentation */ /* to see what these variables mean */ const char *rl_library_version = "EditLine wrapper"; int rl_readline_version = RL_READLINE_VERSION; static char empty[] = { '\0' }; static char expand_chars[] = { ' ', '\t', '\n', '=', '(', '\0' }; static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' }; const char *rl_readline_name = empty; FILE *rl_instream = NULL; FILE *rl_outstream = NULL; int rl_point = 0; int rl_end = 0; char *rl_line_buffer = NULL; rl_vcpfunc_t *rl_linefunc = NULL; int rl_done = 0; rl_hook_func_t *rl_event_hook = NULL; KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap; /* * The following is not implemented; we always catch signals in the * libedit fashion: set handlers on entry to el_gets() and clear them * on the way out. This simplistic approach works for most cases; if * it does not work for your application, please let us know. */ int rl_catch_signals = 1; int rl_catch_sigwinch = 1; int history_base = 1; /* probably never subject to change */ int history_length = 0; int history_offset = 0; int max_input_history = 0; char history_expansion_char = '!'; char history_subst_char = '^'; char *history_no_expand_chars = expand_chars; -Function *history_inhibit_expansion_function = NULL; +rl_linebuf_func_t *history_inhibit_expansion_function = NULL; char *history_arg_extract(int start, int end, const char *str); int rl_inhibit_completion = 0; int rl_attempted_completion_over = 0; const char *rl_basic_word_break_characters = break_chars; char *rl_completer_word_break_characters = NULL; const char *rl_completer_quote_characters = NULL; const char *rl_basic_quote_characters = "\"'"; rl_compentry_func_t *rl_completion_entry_function = NULL; char *(*rl_completion_word_break_hook)(void) = NULL; rl_completion_func_t *rl_attempted_completion_function = NULL; rl_hook_func_t *rl_pre_input_hook = NULL; rl_hook_func_t *rl_startup1_hook = NULL; int (*rl_getc_function)(FILE *) = NULL; char *rl_terminal_name = NULL; int rl_already_prompted = 0; int rl_filename_completion_desired = 0; int rl_ignore_completion_duplicates = 0; int readline_echoing_p = 1; int _rl_print_completions_horizontally = 0; -VFunction *rl_redisplay_function = NULL; +rl_voidfunc_t *rl_redisplay_function = NULL; rl_hook_func_t *rl_startup_hook = NULL; -VFunction *rl_completion_display_matches_hook = NULL; -VFunction *rl_prep_term_function = (VFunction *)rl_prep_terminal; -VFunction *rl_deprep_term_function = (VFunction *)rl_deprep_terminal; +rl_compdisp_func_t *rl_completion_display_matches_hook = NULL; +rl_vintfunc_t *rl_prep_term_function = (rl_vintfunc_t *)rl_prep_terminal; +rl_voidfunc_t *rl_deprep_term_function = (rl_voidfunc_t *)rl_deprep_terminal; KEYMAP_ENTRY_ARRAY emacs_meta_keymap; unsigned long rl_readline_state = RL_STATE_NONE; int _rl_complete_mark_directories; rl_icppfunc_t *rl_directory_completion_hook; int rl_completion_suppress_append; int rl_sort_completion_matches; int _rl_completion_prefix_display_length; int _rl_echoing_p; int history_max_entries; char *rl_display_prompt; int rl_erase_empty_line; /* * The current prompt string. */ char *rl_prompt = NULL; char *rl_prompt_saved = NULL; /* * This is set to character indicating type of completion being done by * rl_complete_internal(); this is available for application completion * functions. */ int rl_completion_type = 0; /* * If more than this number of items results from query for possible * completions, we ask user if they are sure to really display the list. */ int rl_completion_query_items = 100; /* * List of characters which are word break characters, but should be left * in the parsed text when it is passed to the completion function. * Shell uses this to help determine what kind of completing to do. */ const char *rl_special_prefixes = NULL; /* * This is the character appended to the completed words if at the end of * the line. Default is ' ' (a space). */ int rl_completion_append_character = ' '; /* stuff below is used internally by libedit for readline emulation */ static History *h = NULL; static EditLine *e = NULL; static rl_command_func_t *map[256]; static jmp_buf topbuf; /* internal functions */ static unsigned char _el_rl_complete(EditLine *, int); static unsigned char _el_rl_tstp(EditLine *, int); static char *_get_prompt(EditLine *); static int _getc_function(EditLine *, wchar_t *); static int _history_expand_command(const char *, size_t, size_t, char **); static char *_rl_compat_sub(const char *, const char *, const char *, int); static int _rl_event_read_char(EditLine *, wchar_t *); static void _rl_update_pos(void); static HIST_ENTRY rl_he; /* ARGSUSED */ static char * _get_prompt(EditLine *el __attribute__((__unused__))) { rl_already_prompted = 1; return rl_prompt; } /* * read one key from user defined input function */ static int /*ARGSUSED*/ _getc_function(EditLine *el __attribute__((__unused__)), wchar_t *c) { int i; i = (*rl_getc_function)(rl_instream); if (i == -1) return 0; *c = (wchar_t)i; return 1; } static void _resize_fun(EditLine *el, void *a) { const LineInfo *li; const char **ap = a; li = el_line(el); *ap = li->buffer; } static const char * _default_history_file(void) { struct passwd *p; static char *path; size_t len; if (path) return path; if ((p = getpwuid(getuid())) == NULL) return NULL; len = strlen(p->pw_dir) + sizeof("/.history"); if ((path = el_malloc(len)) == NULL) return NULL; (void)snprintf(path, len, "%s/.history", p->pw_dir); return path; } /* * READLINE compatibility stuff */ /* * Set the prompt */ int rl_set_prompt(const char *prompt) { char *p; if (!prompt) prompt = ""; if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0) return 0; if (rl_prompt) el_free(rl_prompt); rl_prompt = strdup(prompt); if (rl_prompt == NULL) return -1; while ((p = strchr(rl_prompt, RL_PROMPT_END_IGNORE)) != NULL) { /* Remove adjacent end/start markers to avoid double-escapes. */ if (p[1] == RL_PROMPT_START_IGNORE) { memmove(p, p + 2, 1 + strlen(p + 2)); } else { *p = RL_PROMPT_START_IGNORE; } } return 0; } void rl_save_prompt(void) { rl_prompt_saved = strdup(rl_prompt); } void rl_restore_prompt(void) { if (!rl_prompt_saved) return; rl_prompt = rl_prompt_saved; rl_prompt_saved = NULL; } /* * initialize rl compat stuff */ int rl_initialize(void) { HistEvent ev; int editmode = 1; struct termios t; if (e != NULL) el_end(e); if (h != NULL) history_end(h); RL_UNSETSTATE(RL_STATE_DONE); if (!rl_instream) rl_instream = stdin; if (!rl_outstream) rl_outstream = stdout; /* * See if we don't really want to run the editor */ if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0) editmode = 0; e = el_init_internal(rl_readline_name, rl_instream, rl_outstream, stderr, fileno(rl_instream), fileno(rl_outstream), fileno(stderr), NO_RESET); if (!editmode) el_set(e, EL_EDITMODE, 0); h = history_init(); if (!e || !h) return -1; history(h, &ev, H_SETSIZE, INT_MAX); /* unlimited */ history_length = 0; max_input_history = INT_MAX; el_set(e, EL_HIST, history, h); /* Setup resize function */ el_set(e, EL_RESIZE, _resize_fun, &rl_line_buffer); /* setup getc function if valid */ if (rl_getc_function) el_set(e, EL_GETCFN, _getc_function); /* for proper prompt printing in readline() */ if (rl_set_prompt("") == -1) { history_end(h); el_end(e); return -1; } el_set(e, EL_PROMPT_ESC, _get_prompt, RL_PROMPT_START_IGNORE); el_set(e, EL_SIGNAL, rl_catch_signals); /* set default mode to "emacs"-style and read setting afterwards */ /* so this can be overridden */ el_set(e, EL_EDITOR, "emacs"); if (rl_terminal_name != NULL) el_set(e, EL_TERMINAL, rl_terminal_name); else el_get(e, EL_TERMINAL, &rl_terminal_name); /* * Word completion - this has to go AFTER rebinding keys * to emacs-style. */ el_set(e, EL_ADDFN, "rl_complete", "ReadLine compatible completion function", _el_rl_complete); el_set(e, EL_BIND, "^I", "rl_complete", NULL); /* * Send TSTP when ^Z is pressed. */ el_set(e, EL_ADDFN, "rl_tstp", "ReadLine compatible suspend function", _el_rl_tstp); el_set(e, EL_BIND, "^Z", "rl_tstp", NULL); /* * Set some readline compatible key-bindings. */ el_set(e, EL_BIND, "^R", "em-inc-search-prev", NULL); /* * Allow the use of Home/End keys. */ el_set(e, EL_BIND, "\\e[1~", "ed-move-to-beg", NULL); el_set(e, EL_BIND, "\\e[4~", "ed-move-to-end", NULL); el_set(e, EL_BIND, "\\e[7~", "ed-move-to-beg", NULL); el_set(e, EL_BIND, "\\e[8~", "ed-move-to-end", NULL); el_set(e, EL_BIND, "\\e[H", "ed-move-to-beg", NULL); el_set(e, EL_BIND, "\\e[F", "ed-move-to-end", NULL); /* * Allow the use of the Delete/Insert keys. */ el_set(e, EL_BIND, "\\e[3~", "ed-delete-next-char", NULL); - el_set(e, EL_BIND, "\\e[2~", "ed-quoted-insert", NULL); + el_set(e, EL_BIND, "\\e[2~", "em-toggle-overwrite", NULL); /* * Ctrl-left-arrow and Ctrl-right-arrow for word moving. */ el_set(e, EL_BIND, "\\e[1;5C", "em-next-word", NULL); el_set(e, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL); el_set(e, EL_BIND, "\\e[5C", "em-next-word", NULL); el_set(e, EL_BIND, "\\e[5D", "ed-prev-word", NULL); el_set(e, EL_BIND, "\\e\\e[C", "em-next-word", NULL); el_set(e, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL); /* read settings from configuration file */ el_source(e, NULL); /* * Unfortunately, some applications really do use rl_point * and rl_line_buffer directly. */ _resize_fun(e, &rl_line_buffer); _rl_update_pos(); tty_end(e, TCSADRAIN); return 0; } /* * read one line from input stream and return it, chomping * trailing newline (if there is any) */ char * readline(const char *p) { HistEvent ev; const char * volatile prompt = p; int count; const char *ret; char *buf; static int used_event_hook; if (e == NULL || h == NULL) rl_initialize(); if (rl_startup_hook) { (*rl_startup_hook)(); } tty_init(e); rl_done = 0; (void)setjmp(topbuf); buf = NULL; /* update prompt accordingly to what has been passed */ if (rl_set_prompt(prompt) == -1) goto out; if (rl_pre_input_hook) (*rl_pre_input_hook)(); if (rl_event_hook && !(e->el_flags & NO_TTY)) { el_set(e, EL_GETCFN, _rl_event_read_char); used_event_hook = 1; } if (!rl_event_hook && used_event_hook) { el_set(e, EL_GETCFN, EL_BUILTIN_GETCFN); used_event_hook = 0; } rl_already_prompted = 0; /* get one line from input stream */ ret = el_gets(e, &count); if (ret && count > 0) { int lastidx; buf = strdup(ret); if (buf == NULL) goto out; lastidx = count - 1; if (buf[lastidx] == '\n') buf[lastidx] = '\0'; } else buf = NULL; history(h, &ev, H_GETSIZE); history_length = ev.num; out: tty_end(e, TCSADRAIN); return buf; } /* * history functions */ /* * is normally called before application starts to use * history expansion functions */ void using_history(void) { if (h == NULL || e == NULL) rl_initialize(); history_offset = history_length; } /* * substitute ``what'' with ``with'', returning resulting string; if * globally == 1, substitutes all occurrences of what, otherwise only the * first one */ static char * _rl_compat_sub(const char *str, const char *what, const char *with, int globally) { const char *s; char *r, *result; size_t len, with_len, what_len; len = strlen(str); with_len = strlen(with); what_len = strlen(what); /* calculate length we need for result */ s = str; while (*s) { if (*s == *what && !strncmp(s, what, what_len)) { len += with_len - what_len; if (!globally) break; s += what_len; } else s++; } r = result = el_calloc(len + 1, sizeof(*r)); if (result == NULL) return NULL; s = str; while (*s) { if (*s == *what && !strncmp(s, what, what_len)) { memcpy(r, with, with_len); r += with_len; s += what_len; if (!globally) { (void)strcpy(r, s); return result; } } else *r++ = *s++; } *r = '\0'; return result; } static char *last_search_pat; /* last !?pat[?] search pattern */ static char *last_search_match; /* last !?pat[?] that matched */ const char * get_history_event(const char *cmd, int *cindex, int qchar) { int idx, sign, sub, num, begin, ret; size_t len; char *pat; const char *rptr; HistEvent ev; idx = *cindex; if (cmd[idx++] != history_expansion_char) return NULL; /* find out which event to take */ if (cmd[idx] == history_expansion_char || cmd[idx] == '\0') { if (history(h, &ev, H_FIRST) != 0) return NULL; *cindex = cmd[idx]? (idx + 1):idx; return ev.str; } sign = 0; if (cmd[idx] == '-') { sign = 1; idx++; } if ('0' <= cmd[idx] && cmd[idx] <= '9') { HIST_ENTRY *he; num = 0; while (cmd[idx] && '0' <= cmd[idx] && cmd[idx] <= '9') { num = num * 10 + cmd[idx] - '0'; idx++; } if (sign) num = history_length - num + history_base; if (!(he = history_get(num))) return NULL; *cindex = idx; return he->line; } sub = 0; if (cmd[idx] == '?') { sub = 1; idx++; } begin = idx; while (cmd[idx]) { if (cmd[idx] == '\n') break; if (sub && cmd[idx] == '?') break; if (!sub && (cmd[idx] == ':' || cmd[idx] == ' ' || cmd[idx] == '\t' || cmd[idx] == qchar)) break; idx++; } len = (size_t)idx - (size_t)begin; if (sub && cmd[idx] == '?') idx++; if (sub && len == 0 && last_search_pat && *last_search_pat) pat = last_search_pat; else if (len == 0) return NULL; else { if ((pat = el_calloc(len + 1, sizeof(*pat))) == NULL) return NULL; (void)strlcpy(pat, cmd + begin, len + 1); } if (history(h, &ev, H_CURR) != 0) { if (pat != last_search_pat) el_free(pat); return NULL; } num = ev.num; if (sub) { if (pat != last_search_pat) { el_free(last_search_pat); last_search_pat = pat; } ret = history_search(pat, -1); } else ret = history_search_prefix(pat, -1); if (ret == -1) { /* restore to end of list on failed search */ history(h, &ev, H_FIRST); (void)fprintf(rl_outstream, "%s: Event not found\n", pat); if (pat != last_search_pat) el_free(pat); return NULL; } if (sub && len) { el_free(last_search_match); last_search_match = strdup(pat); } if (pat != last_search_pat) el_free(pat); if (history(h, &ev, H_CURR) != 0) return NULL; *cindex = idx; rptr = ev.str; /* roll back to original position */ (void)history(h, &ev, H_SET, num); return rptr; } static int getfrom(const char **cmdp, char **fromp, const char *search, int delim) { size_t size = 16; size_t len = 0; const char *cmd = *cmdp; char *what = el_realloc(*fromp, size * sizeof(*what)); if (what == NULL){ el_free(*fromp); *fromp = NULL; return 0; } for (; *cmd && *cmd != delim; cmd++) { if (*cmd == '\\' && cmd[1] == delim) cmd++; if (len - 1 >= size) { char *nwhat; nwhat = el_realloc(what, (size <<= 1) * sizeof(*nwhat)); if (nwhat == NULL) { el_free(what); el_free(*fromp); *cmdp = cmd; *fromp = NULL; return 0; } what = nwhat; } what[len++] = *cmd; } what[len] = '\0'; *fromp = what; *cmdp = cmd; if (*what == '\0') { el_free(what); if (search) { *fromp = strdup(search); if (*fromp == NULL) { return 0; } } else { *fromp = NULL; return -1; } } if (!*cmd) { el_free(what); *fromp = NULL; return -1; } cmd++; /* shift after delim */ *cmdp = cmd; if (!*cmd) { el_free(what); *fromp = NULL; return -1; } return 1; } static int getto(const char **cmdp, char **top, const char *from, int delim) { size_t size = 16; size_t len = 0; size_t from_len = strlen(from); const char *cmd = *cmdp; char *with = el_realloc(*top, size * sizeof(*with)); *top = NULL; if (with == NULL) goto out; for (; *cmd && *cmd != delim; cmd++) { if (len + from_len + 1 >= size) { char *nwith; size += from_len + 1; nwith = el_realloc(with, size * sizeof(*nwith)); if (nwith == NULL) goto out; with = nwith; } if (*cmd == '&') { /* safe */ strcpy(&with[len], from); len += from_len; continue; } if (*cmd == '\\' && (*(cmd + 1) == delim || *(cmd + 1) == '&')) cmd++; with[len++] = *cmd; } if (!*cmd) goto out; with[len] = '\0'; *top = with; *cmdp = cmd; return 1; out: el_free(with); el_free(*top); *top = NULL; *cmdp = cmd; return -1; } static void replace(char **tmp, int c) { char *aptr; if ((aptr = strrchr(*tmp, c)) == NULL) return; aptr = strdup(aptr + 1); // XXX: check el_free(*tmp); *tmp = aptr; } /* * the real function doing history expansion - takes as argument command * to do and data upon which the command should be executed * does expansion the way I've understood readline documentation * * returns 0 if data was not modified, 1 if it was and 2 if the string * should be only printed and not executed; in case of error, * returns -1 and *result points to NULL * it's the caller's responsibility to free() the string returned in *result */ static int _history_expand_command(const char *command, size_t offs, size_t cmdlen, char **result) { char *tmp, *search = NULL, *aptr, delim; const char *ptr, *cmd; static char *from = NULL, *to = NULL; int start, end, idx, has_mods = 0; int p_on = 0, g_on = 0, ev; *result = NULL; aptr = NULL; ptr = NULL; /* First get event specifier */ idx = 0; if (strchr(":^*$", command[offs + 1])) { char str[4]; /* * "!:" is shorthand for "!!:". * "!^", "!*" and "!$" are shorthand for * "!!:^", "!!:*" and "!!:$" respectively. */ str[0] = str[1] = '!'; str[2] = '0'; ptr = get_history_event(str, &idx, 0); idx = (command[offs + 1] == ':')? 1:0; has_mods = 1; } else { if (command[offs + 1] == '#') { /* use command so far */ if ((aptr = el_calloc(offs + 1, sizeof(*aptr))) == NULL) return -1; (void)strlcpy(aptr, command, offs + 1); idx = 1; } else { int qchar; qchar = (offs > 0 && command[offs - 1] == '"') ? '"' : '\0'; ptr = get_history_event(command + offs, &idx, qchar); } has_mods = command[offs + (size_t)idx] == ':'; } if (ptr == NULL && aptr == NULL) return -1; if (!has_mods) { *result = strdup(aptr ? aptr : ptr); if (aptr) el_free(aptr); if (*result == NULL) return -1; return 1; } cmd = command + offs + idx + 1; /* Now parse any word designators */ if (*cmd == '%') /* last word matched by ?pat? */ tmp = strdup(last_search_match ? last_search_match : ""); else if (strchr("^*$-0123456789", *cmd)) { start = end = -1; if (*cmd == '^') start = end = 1, cmd++; else if (*cmd == '$') start = -1, cmd++; else if (*cmd == '*') start = 1, cmd++; else if (*cmd == '-' || isdigit((unsigned char) *cmd)) { start = 0; while (*cmd && '0' <= *cmd && *cmd <= '9') start = start * 10 + *cmd++ - '0'; if (*cmd == '-') { if (isdigit((unsigned char) cmd[1])) { cmd++; end = 0; while (*cmd && '0' <= *cmd && *cmd <= '9') end = end * 10 + *cmd++ - '0'; } else if (cmd[1] == '$') { cmd += 2; end = -1; } else { cmd++; end = -2; } } else if (*cmd == '*') end = -1, cmd++; else end = start; } tmp = history_arg_extract(start, end, aptr? aptr:ptr); if (tmp == NULL) { (void)fprintf(rl_outstream, "%s: Bad word specifier", command + offs + idx); if (aptr) el_free(aptr); return -1; } } else tmp = strdup(aptr? aptr:ptr); if (aptr) el_free(aptr); if (*cmd == '\0' || ((size_t)(cmd - (command + offs)) >= cmdlen)) { *result = tmp; return 1; } for (; *cmd; cmd++) { switch (*cmd) { case ':': continue; case 'h': /* remove trailing path */ if ((aptr = strrchr(tmp, '/')) != NULL) *aptr = '\0'; continue; case 't': /* remove leading path */ replace(&tmp, '/'); continue; case 'r': /* remove trailing suffix */ if ((aptr = strrchr(tmp, '.')) != NULL) *aptr = '\0'; continue; case 'e': /* remove all but suffix */ replace(&tmp, '.'); continue; case 'p': /* print only */ p_on = 1; continue; case 'g': g_on = 2; continue; case '&': if (from == NULL || to == NULL) continue; /*FALLTHROUGH*/ case 's': ev = -1; delim = *++cmd; if (delim == '\0' || *++cmd == '\0') goto out; if ((ev = getfrom(&cmd, &from, search, delim)) != 1) goto out; if ((ev = getto(&cmd, &to, from, delim)) != 1) goto out; aptr = _rl_compat_sub(tmp, from, to, g_on); if (aptr) { el_free(tmp); tmp = aptr; } g_on = 0; cmd--; continue; } } *result = tmp; return p_on ? 2 : 1; out: el_free(tmp); return ev; } /* * csh-style history expansion */ int history_expand(char *str, char **output) { int ret = 0; size_t idx, i, size; char *tmp, *result; if (h == NULL || e == NULL) rl_initialize(); if (history_expansion_char == 0) { *output = strdup(str); return 0; } *output = NULL; if (str[0] == history_subst_char) { /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */ *output = el_calloc(strlen(str) + 4 + 1, sizeof(**output)); if (*output == NULL) return 0; (*output)[0] = (*output)[1] = history_expansion_char; (*output)[2] = ':'; (*output)[3] = 's'; (void)strcpy((*output) + 4, str); str = *output; } else { *output = strdup(str); if (*output == NULL) return 0; } #define ADD_STRING(what, len, fr) \ { \ if (idx + len + 1 > size) { \ char *nresult = el_realloc(result, \ (size += len + 1) * sizeof(*nresult)); \ if (nresult == NULL) { \ el_free(*output); \ el_free(fr); \ return 0; \ } \ result = nresult; \ } \ (void)strlcpy(&result[idx], what, len + 1); \ idx += len; \ } result = NULL; size = idx = 0; tmp = NULL; for (i = 0; str[i];) { int qchar, loop_again; size_t len, start, j; qchar = 0; loop_again = 1; start = j = i; loop: for (; str[j]; j++) { if (str[j] == '\\' && str[j + 1] == history_expansion_char) { len = strlen(&str[j + 1]) + 1; memmove(&str[j], &str[j + 1], len); continue; } if (!loop_again) { if (isspace((unsigned char) str[j]) || str[j] == qchar) break; } if (str[j] == history_expansion_char && !strchr(history_no_expand_chars, str[j + 1]) && (!history_inhibit_expansion_function || (*history_inhibit_expansion_function)(str, (int)j) == 0)) break; } if (str[j] && loop_again) { i = j; qchar = (j > 0 && str[j - 1] == '"' )? '"':0; j++; if (str[j] == history_expansion_char) j++; loop_again = 0; goto loop; } len = i - start; ADD_STRING(&str[start], len, NULL); if (str[i] == '\0' || str[i] != history_expansion_char) { len = j - i; ADD_STRING(&str[i], len, NULL); if (start == 0) ret = 0; else ret = 1; break; } ret = _history_expand_command (str, i, (j - i), &tmp); if (ret > 0 && tmp) { len = strlen(tmp); ADD_STRING(tmp, len, tmp); } if (tmp) { el_free(tmp); tmp = NULL; } i = j; } /* ret is 2 for "print only" option */ if (ret == 2) { add_history(result); #ifdef GDB_411_HACK /* gdb 4.11 has been shipped with readline, where */ /* history_expand() returned -1 when the line */ /* should not be executed; in readline 2.1+ */ /* it should return 2 in such a case */ ret = -1; #endif } el_free(*output); *output = result; return ret; } /* * Return a string consisting of arguments of "str" from "start" to "end". */ char * history_arg_extract(int start, int end, const char *str) { size_t i, len, max; char **arr, *result = NULL; arr = history_tokenize(str); if (!arr) return NULL; if (arr && *arr == NULL) goto out; for (max = 0; arr[max]; max++) continue; max--; if (start == '$') start = (int)max; if (end == '$') end = (int)max; if (end < 0) end = (int)max + end + 1; if (start < 0) start = end; if (start < 0 || end < 0 || (size_t)start > max || (size_t)end > max || start > end) goto out; for (i = (size_t)start, len = 0; i <= (size_t)end; i++) len += strlen(arr[i]) + 1; len++; result = el_calloc(len, sizeof(*result)); if (result == NULL) goto out; for (i = (size_t)start, len = 0; i <= (size_t)end; i++) { (void)strcpy(result + len, arr[i]); len += strlen(arr[i]); if (i < (size_t)end) result[len++] = ' '; } result[len] = '\0'; out: for (i = 0; arr[i]; i++) el_free(arr[i]); el_free(arr); return result; } /* * Parse the string into individual tokens, * similar to how shell would do it. */ char ** history_tokenize(const char *str) { int size = 1, idx = 0, i, start; size_t len; char **result = NULL, *temp, delim = '\0'; for (i = 0; str[i];) { while (isspace((unsigned char) str[i])) i++; start = i; for (; str[i];) { if (str[i] == '\\') { if (str[i+1] != '\0') i++; } else if (str[i] == delim) delim = '\0'; else if (!delim && (isspace((unsigned char) str[i]) || strchr("()<>;&|$", str[i]))) break; else if (!delim && strchr("'`\"", str[i])) delim = str[i]; if (str[i]) i++; } if (idx + 2 >= size) { char **nresult; size <<= 1; nresult = el_realloc(result, (size_t)size * sizeof(*nresult)); if (nresult == NULL) { el_free(result); return NULL; } result = nresult; } len = (size_t)i - (size_t)start; temp = el_calloc(len + 1, sizeof(*temp)); if (temp == NULL) { for (i = 0; i < idx; i++) el_free(result[i]); el_free(result); return NULL; } (void)strlcpy(temp, &str[start], len + 1); result[idx++] = temp; result[idx] = NULL; if (str[i]) i++; } return result; } /* * limit size of history record to ``max'' events */ void stifle_history(int max) { HistEvent ev; HIST_ENTRY *he; if (h == NULL || e == NULL) rl_initialize(); if (history(h, &ev, H_SETSIZE, max) == 0) { max_input_history = max; if (history_length > max) history_base = history_length - max; while (history_length > max) { he = remove_history(0); el_free(he->data); el_free((void *)(unsigned long)he->line); el_free(he); } } } /* * "unlimit" size of history - set the limit to maximum allowed int value */ int unstifle_history(void) { HistEvent ev; int omax; history(h, &ev, H_SETSIZE, INT_MAX); omax = max_input_history; max_input_history = INT_MAX; return omax; /* some value _must_ be returned */ } int history_is_stifled(void) { /* cannot return true answer */ return max_input_history != INT_MAX; } static const char _history_tmp_template[] = "/tmp/.historyXXXXXX"; int history_truncate_file (const char *filename, int nlines) { int ret = 0; FILE *fp, *tp; char template[sizeof(_history_tmp_template)]; char buf[4096]; int fd; char *cp; off_t off; int count = 0; ssize_t left = 0; if (filename == NULL && (filename = _default_history_file()) == NULL) return errno; if ((fp = fopen(filename, "r+")) == NULL) return errno; strcpy(template, _history_tmp_template); if ((fd = mkstemp(template)) == -1) { ret = errno; goto out1; } if ((tp = fdopen(fd, "r+")) == NULL) { close(fd); ret = errno; goto out2; } for(;;) { if (fread(buf, sizeof(buf), (size_t)1, fp) != 1) { if (ferror(fp)) { ret = errno; break; } if (fseeko(fp, (off_t)sizeof(buf) * count, SEEK_SET) == (off_t)-1) { ret = errno; break; } left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), fp); if (ferror(fp)) { ret = errno; break; } if (left == 0) { count--; left = sizeof(buf); } else if (fwrite(buf, (size_t)left, (size_t)1, tp) != 1) { ret = errno; break; } fflush(tp); break; } if (fwrite(buf, sizeof(buf), (size_t)1, tp) != 1) { ret = errno; break; } count++; } if (ret) goto out3; cp = buf + left - 1; if(*cp != '\n') cp++; for(;;) { while (--cp >= buf) { if (*cp == '\n') { if (--nlines == 0) { if (++cp >= buf + sizeof(buf)) { count++; cp = buf; } break; } } } if (nlines <= 0 || count == 0) break; count--; if (fseeko(tp, (off_t)sizeof(buf) * count, SEEK_SET) < 0) { ret = errno; break; } if (fread(buf, sizeof(buf), (size_t)1, tp) != 1) { if (ferror(tp)) { ret = errno; break; } ret = EAGAIN; break; } cp = buf + sizeof(buf); } if (ret || nlines > 0) goto out3; if (fseeko(fp, (off_t)0, SEEK_SET) == (off_t)-1) { ret = errno; goto out3; } if (fseeko(tp, (off_t)sizeof(buf) * count + (cp - buf), SEEK_SET) == (off_t)-1) { ret = errno; goto out3; } for(;;) { if ((left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), tp)) == 0) { if (ferror(fp)) ret = errno; break; } if (fwrite(buf, (size_t)left, (size_t)1, fp) != 1) { ret = errno; break; } } fflush(fp); if((off = ftello(fp)) > 0) (void)ftruncate(fileno(fp), off); out3: fclose(tp); out2: unlink(template); out1: fclose(fp); return ret; } /* * read history from a file given */ int read_history(const char *filename) { HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); if (filename == NULL && (filename = _default_history_file()) == NULL) return errno; errno = 0; if (history(h, &ev, H_LOAD, filename) == -1) return errno ? errno : EINVAL; if (history(h, &ev, H_GETSIZE) == 0) history_length = ev.num; if (history_length < 0) return EINVAL; return 0; } /* * write history to a file given */ int write_history(const char *filename) { HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); if (filename == NULL && (filename = _default_history_file()) == NULL) return errno; return history(h, &ev, H_SAVE, filename) == -1 ? (errno ? errno : EINVAL) : 0; } int append_history(int n, const char *filename) { HistEvent ev; FILE *fp; if (h == NULL || e == NULL) rl_initialize(); if (filename == NULL && (filename = _default_history_file()) == NULL) return errno; if ((fp = fopen(filename, "a")) == NULL) return errno; if (history(h, &ev, H_NSAVE_FP, (size_t)n, fp) == -1) { int serrno = errno ? errno : EINVAL; fclose(fp); return serrno; } fclose(fp); return 0; } /* * returns history ``num''th event * * returned pointer points to static variable */ HIST_ENTRY * history_get(int num) { static HIST_ENTRY she; HistEvent ev; int curr_num; if (h == NULL || e == NULL) rl_initialize(); if (num < history_base) return NULL; /* save current position */ if (history(h, &ev, H_CURR) != 0) return NULL; curr_num = ev.num; /* * use H_DELDATA to set to nth history (without delete) by passing * (void **)-1 -- as in history_set_pos */ if (history(h, &ev, H_DELDATA, num - history_base, (void **)-1) != 0) goto out; /* get current entry */ if (history(h, &ev, H_CURR) != 0) goto out; if (history(h, &ev, H_NEXT_EVDATA, ev.num, &she.data) != 0) goto out; she.line = ev.str; /* restore pointer to where it was */ (void)history(h, &ev, H_SET, curr_num); return &she; out: /* restore pointer to where it was */ (void)history(h, &ev, H_SET, curr_num); return NULL; } /* * add the line to history table */ int add_history(const char *line) { HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); if (history(h, &ev, H_ENTER, line) == -1) return 0; (void)history(h, &ev, H_GETSIZE); if (ev.num == history_length) history_base++; else { history_offset++; history_length = ev.num; } return 0; } /* * remove the specified entry from the history list and return it. */ HIST_ENTRY * remove_history(int num) { HIST_ENTRY *he; HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); if ((he = el_malloc(sizeof(*he))) == NULL) return NULL; if (history(h, &ev, H_DELDATA, num, &he->data) != 0) { el_free(he); return NULL; } he->line = ev.str; if (history(h, &ev, H_GETSIZE) == 0) history_length = ev.num; return he; } /* * replace the line and data of the num-th entry */ HIST_ENTRY * replace_history_entry(int num, const char *line, histdata_t data) { HIST_ENTRY *he; HistEvent ev; int curr_num; if (h == NULL || e == NULL) rl_initialize(); /* save current position */ if (history(h, &ev, H_CURR) != 0) return NULL; curr_num = ev.num; /* start from the oldest */ if (history(h, &ev, H_LAST) != 0) return NULL; /* error */ if ((he = el_malloc(sizeof(*he))) == NULL) return NULL; /* look forwards for event matching specified offset */ if (history(h, &ev, H_NEXT_EVDATA, num, &he->data)) goto out; he->line = ev.str; if (he->line == NULL) goto out; if (history(h, &ev, H_REPLACE, line, data)) goto out; /* restore pointer to where it was */ if (history(h, &ev, H_SET, curr_num)) goto out; return he; out: el_free(he); return NULL; } /* * clear the history list - delete all entries */ void clear_history(void) { HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); (void)history(h, &ev, H_CLEAR); history_offset = history_length = 0; } /* * returns offset of the current history event */ int where_history(void) { return history_offset; } static HIST_ENTRY **_history_listp; static HIST_ENTRY *_history_list; HIST_ENTRY ** history_list(void) { HistEvent ev; HIST_ENTRY **nlp, *nl; int i; if (history(h, &ev, H_LAST) != 0) return NULL; if ((nlp = el_realloc(_history_listp, ((size_t)history_length + 1) * sizeof(*nlp))) == NULL) return NULL; _history_listp = nlp; if ((nl = el_realloc(_history_list, (size_t)history_length * sizeof(*nl))) == NULL) return NULL; _history_list = nl; i = 0; do { _history_listp[i] = &_history_list[i]; _history_list[i].line = ev.str; _history_list[i].data = NULL; if (i++ == history_length) abort(); } while (history(h, &ev, H_PREV) == 0); _history_listp[i] = NULL; return _history_listp; } /* * returns current history event or NULL if there is no such event */ HIST_ENTRY * current_history(void) { HistEvent ev; if (history(h, &ev, H_PREV_EVENT, history_offset + 1) != 0) return NULL; rl_he.line = ev.str; rl_he.data = NULL; return &rl_he; } /* * returns total number of bytes history events' data are using */ int history_total_bytes(void) { HistEvent ev; int curr_num; size_t size; if (history(h, &ev, H_CURR) != 0) return -1; curr_num = ev.num; (void)history(h, &ev, H_FIRST); size = 0; do size += strlen(ev.str) * sizeof(*ev.str); while (history(h, &ev, H_NEXT) == 0); /* get to the same position as before */ history(h, &ev, H_PREV_EVENT, curr_num); return (int)size; } /* * sets the position in the history list to ``pos'' */ int history_set_pos(int pos) { if (pos >= history_length || pos < 0) return 0; history_offset = pos; return 1; } /* * returns previous event in history and shifts pointer accordingly * Note that readline and editline define directions in opposite ways. */ HIST_ENTRY * previous_history(void) { HistEvent ev; if (history_offset == 0) return NULL; if (history(h, &ev, H_LAST) != 0) return NULL; history_offset--; return current_history(); } /* * returns next event in history and shifts pointer accordingly */ HIST_ENTRY * next_history(void) { HistEvent ev; if (history_offset >= history_length) return NULL; if (history(h, &ev, H_LAST) != 0) return NULL; history_offset++; return current_history(); } /* * searches for first history event containing the str */ int history_search(const char *str, int direction) { HistEvent ev; const char *strp; int curr_num; if (history(h, &ev, H_CURR) != 0) return -1; curr_num = ev.num; for (;;) { if ((strp = strstr(ev.str, str)) != NULL) return (int)(strp - ev.str); if (history(h, &ev, direction < 0 ? H_NEXT:H_PREV) != 0) break; } (void)history(h, &ev, H_SET, curr_num); return -1; } /* * searches for first history event beginning with str */ int history_search_prefix(const char *str, int direction) { HistEvent ev; return (history(h, &ev, direction < 0 ? H_PREV_STR : H_NEXT_STR, str)); } /* * search for event in history containing str, starting at offset * abs(pos); continue backward, if pos<0, forward otherwise */ /* ARGSUSED */ int history_search_pos(const char *str, int direction __attribute__((__unused__)), int pos) { HistEvent ev; int curr_num, off; off = (pos > 0) ? pos : -pos; pos = (pos > 0) ? 1 : -1; if (history(h, &ev, H_CURR) != 0) return -1; curr_num = ev.num; if (!history_set_pos(off) || history(h, &ev, H_CURR) != 0) return -1; for (;;) { if (strstr(ev.str, str)) return off; if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0) break; } /* set "current" pointer back to previous state */ (void)history(h, &ev, pos < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); return -1; } /********************************/ /* completion functions */ char * tilde_expand(char *name) { return fn_tilde_expand(name); } char * filename_completion_function(const char *name, int state) { return fn_filename_completion_function(name, state); } /* * a completion generator for usernames; returns _first_ username * which starts with supplied text * text contains a partial username preceded by random character * (usually '~'); state resets search from start (??? should we do that anyway) * it's the caller's responsibility to free the returned value */ char * username_completion_function(const char *text, int state) { #if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) struct passwd pwres; char pwbuf[1024]; #endif struct passwd *pass = NULL; if (text[0] == '\0') return NULL; if (*text == '~') text++; if (state == 0) setpwent(); while ( #if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) getpwent_r(&pwres, pwbuf, sizeof(pwbuf), &pass) == 0 && pass != NULL #else (pass = getpwent()) != NULL #endif && text[0] == pass->pw_name[0] && strcmp(text, pass->pw_name) == 0) continue; if (pass == NULL) { endpwent(); return NULL; } return strdup(pass->pw_name); } /* * el-compatible wrapper to send TSTP on ^Z */ /* ARGSUSED */ static unsigned char _el_rl_tstp(EditLine *el __attribute__((__unused__)), int ch __attribute__((__unused__))) { (void)kill(0, SIGTSTP); return CC_NORM; } static const char * /*ARGSUSED*/ _rl_completion_append_character_function(const char *dummy __attribute__((__unused__))) { static char buf[2]; buf[0] = (char)rl_completion_append_character; buf[1] = '\0'; return buf; } /* * Display list of strings in columnar format on readline's output stream. * 'matches' is list of strings, 'len' is number of strings in 'matches', * 'max' is maximum length of string in 'matches'. */ void rl_display_match_list(char **matches, int len, int max) { fn_display_match_list(e, matches, (size_t)len, (size_t)max, _rl_completion_append_character_function); } /* * complete word at current point */ /* ARGSUSED */ int rl_complete(int ignore __attribute__((__unused__)), int invoking_key) { static ct_buffer_t wbreak_conv, sprefix_conv; const char *breakchars; if (h == NULL || e == NULL) rl_initialize(); if (rl_inhibit_completion) { char arr[2]; arr[0] = (char)invoking_key; arr[1] = '\0'; el_insertstr(e, arr); return CC_REFRESH; } if (rl_completion_word_break_hook != NULL) breakchars = (*rl_completion_word_break_hook)(); else breakchars = rl_basic_word_break_characters; _rl_update_pos(); /* Just look at how many global variables modify this operation! */ - return fn_complete(e, + return fn_complete2(e, (rl_compentry_func_t *)rl_completion_entry_function, rl_attempted_completion_function, ct_decode_string(rl_basic_word_break_characters, &wbreak_conv), ct_decode_string(breakchars, &sprefix_conv), _rl_completion_append_character_function, (size_t)rl_completion_query_items, &rl_completion_type, &rl_attempted_completion_over, - &rl_point, &rl_end); + &rl_point, &rl_end, 0); } /* ARGSUSED */ static unsigned char _el_rl_complete(EditLine *el __attribute__((__unused__)), int ch) { return (unsigned char)rl_complete(0, ch); } /* * misc other functions */ /* * bind key c to readline-type function func */ int rl_bind_key(int c, rl_command_func_t *func) { int retval = -1; if (h == NULL || e == NULL) rl_initialize(); if (func == rl_insert) { /* XXX notice there is no range checking of ``c'' */ e->el_map.key[c] = ED_INSERT; retval = 0; } return retval; } /* * read one key from input - handles chars pushed back * to input stream also */ int rl_read_key(void) { char fooarr[2 * sizeof(int)]; if (e == NULL || h == NULL) rl_initialize(); return el_getc(e, fooarr); } /* * reset the terminal */ /* ARGSUSED */ int rl_reset_terminal(const char *p __attribute__((__unused__))) { if (h == NULL || e == NULL) rl_initialize(); el_reset(e); return 0; } /* * insert character ``c'' back into input stream, ``count'' times */ int rl_insert(int count, int c) { char arr[2]; if (h == NULL || e == NULL) rl_initialize(); /* XXX - int -> char conversion can lose on multichars */ arr[0] = (char)c; arr[1] = '\0'; for (; count > 0; count--) el_push(e, arr); return 0; } int rl_insert_text(const char *text) { if (!text || *text == 0) return 0; if (h == NULL || e == NULL) rl_initialize(); if (el_insertstr(e, text) < 0) return 0; return (int)strlen(text); } /*ARGSUSED*/ int rl_newline(int count __attribute__((__unused__)), int c __attribute__((__unused__))) { /* * Readline-4.0 appears to ignore the args. */ return rl_insert(1, '\n'); } /*ARGSUSED*/ static unsigned char rl_bind_wrapper(EditLine *el __attribute__((__unused__)), unsigned char c) { if (map[c] == NULL) return CC_ERROR; _rl_update_pos(); (*map[c])(1, c); /* If rl_done was set by the above call, deal with it here */ if (rl_done) return CC_EOF; return CC_NORM; } int rl_add_defun(const char *name, rl_command_func_t *fun, int c) { char dest[8]; if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0) return -1; map[(unsigned char)c] = fun; el_set(e, EL_ADDFN, name, name, rl_bind_wrapper); vis(dest, c, VIS_WHITE|VIS_NOSLASH, 0); el_set(e, EL_BIND, dest, name, NULL); return 0; } void rl_callback_read_char(void) { int count = 0, done = 0; const char *buf = el_gets(e, &count); char *wbuf; el_set(e, EL_UNBUFFERED, 1); if (buf == NULL || count-- <= 0) return; if (count == 0 && buf[0] == e->el_tty.t_c[TS_IO][C_EOF]) done = 1; if (buf[count] == '\n' || buf[count] == '\r') done = 2; if (done && rl_linefunc != NULL) { el_set(e, EL_UNBUFFERED, 0); if (done == 2) { if ((wbuf = strdup(buf)) != NULL) wbuf[count] = '\0'; RL_SETSTATE(RL_STATE_DONE); } else wbuf = NULL; (*(void (*)(const char *))rl_linefunc)(wbuf); } _rl_update_pos(); } void rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *linefunc) { if (e == NULL) { rl_initialize(); } (void)rl_set_prompt(prompt); rl_linefunc = linefunc; el_set(e, EL_UNBUFFERED, 1); } void rl_callback_handler_remove(void) { el_set(e, EL_UNBUFFERED, 0); rl_linefunc = NULL; } void rl_redisplay(void) { char a[2]; a[0] = (char)e->el_tty.t_c[TS_IO][C_REPRINT]; a[1] = '\0'; el_push(e, a); rl_forced_update_display(); } int rl_get_previous_history(int count, int key) { char a[2]; a[0] = (char)key; a[1] = '\0'; while (count--) el_push(e, a); return 0; } void /*ARGSUSED*/ rl_prep_terminal(int meta_flag __attribute__((__unused__))) { el_set(e, EL_PREP_TERM, 1); } void rl_deprep_terminal(void) { el_set(e, EL_PREP_TERM, 0); } int rl_read_init_file(const char *s) { return el_source(e, s); } int rl_parse_and_bind(const char *line) { const char **argv; int argc; Tokenizer *tok; tok = tok_init(NULL); tok_str(tok, line, &argc, &argv); argc = el_parse(e, argc, argv); tok_end(tok); return argc ? 1 : 0; } int rl_variable_bind(const char *var, const char *value) { /* * The proper return value is undocument, but this is what the * readline source seems to do. */ return el_set(e, EL_BIND, "", var, value, NULL) == -1 ? 1 : 0; } int rl_stuff_char(int c) { char buf[2]; buf[0] = (char)c; buf[1] = '\0'; el_insertstr(e, buf); return 1; } static int _rl_event_read_char(EditLine *el, wchar_t *wc) { char ch; int n; ssize_t num_read = 0; ch = '\0'; *wc = L'\0'; while (rl_event_hook) { (*rl_event_hook)(); #if defined(FIONREAD) if (ioctl(el->el_infd, FIONREAD, &n) < 0) return -1; if (n) num_read = read(el->el_infd, &ch, (size_t)1); else num_read = 0; #elif defined(F_SETFL) && defined(O_NDELAY) if ((n = fcntl(el->el_infd, F_GETFL, 0)) < 0) return -1; if (fcntl(el->el_infd, F_SETFL, n|O_NDELAY) < 0) return -1; num_read = read(el->el_infd, &ch, 1); if (fcntl(el->el_infd, F_SETFL, n)) return -1; #else /* not non-blocking, but what you gonna do? */ num_read = read(el->el_infd, &ch, 1); return -1; #endif if (num_read < 0 && errno == EAGAIN) continue; if (num_read == 0) continue; break; } if (!rl_event_hook) el_set(el, EL_GETCFN, EL_BUILTIN_GETCFN); *wc = (wchar_t)ch; return (int)num_read; } static void _rl_update_pos(void) { const LineInfo *li = el_line(e); rl_point = (int)(li->cursor - li->buffer); rl_end = (int)(li->lastchar - li->buffer); rl_line_buffer[rl_end] = '\0'; } char * rl_copy_text(int from, int to) { const LineInfo *li; size_t len; char * out; if (h == NULL || e == NULL) rl_initialize(); li = el_line(e); if (from > to) return NULL; if (li->buffer + from > li->lastchar) from = (int)(li->lastchar - li->buffer); if (li->buffer + to > li->lastchar) to = (int)(li->lastchar - li->buffer); len = (size_t)(to - from); out = el_malloc((size_t)len + 1); if (out == NULL) return NULL; (void)strlcpy(out, li->buffer + from , len); return out; } void rl_replace_line(const char * text, int clear_undo __attribute__((__unused__))) { if (!text || *text == 0) return; if (h == NULL || e == NULL) rl_initialize(); el_replacestr(e, text); } int rl_delete_text(int start, int end) { if (h == NULL || e == NULL) rl_initialize(); return el_deletestr1(e, start, end); } void rl_get_screen_size(int *rows, int *cols) { if (rows) el_get(e, EL_GETTC, "li", rows); if (cols) el_get(e, EL_GETTC, "co", cols); } #define MAX_MESSAGE 160 void rl_message(const char *format, ...) { char msg[MAX_MESSAGE]; va_list args; va_start(args, format); vsnprintf(msg, sizeof(msg), format, args); va_end(args); rl_set_prompt(msg); rl_forced_update_display(); } void rl_set_screen_size(int rows, int cols) { char buf[64]; (void)snprintf(buf, sizeof(buf), "%d", rows); el_set(e, EL_SETTC, "li", buf, NULL); (void)snprintf(buf, sizeof(buf), "%d", cols); el_set(e, EL_SETTC, "co", buf, NULL); } char ** rl_completion_matches(const char *str, rl_compentry_func_t *fun) { size_t len, max, i, j, min; char **list, *match, *a, *b; len = 1; max = 10; if ((list = el_calloc(max, sizeof(*list))) == NULL) return NULL; while ((match = (*fun)(str, (int)(len - 1))) != NULL) { list[len++] = match; if (len == max) { char **nl; max += 10; if ((nl = el_realloc(list, max * sizeof(*nl))) == NULL) goto out; list = nl; } } if (len == 1) goto out; list[len] = NULL; if (len == 2) { if ((list[0] = strdup(list[1])) == NULL) goto out; return list; } qsort(&list[1], len - 1, sizeof(*list), (int (*)(const void *, const void *)) strcmp); min = SIZE_MAX; for (i = 1, a = list[i]; i < len - 1; i++, a = b) { b = list[i + 1]; for (j = 0; a[j] && a[j] == b[j]; j++) continue; if (min > j) min = j; } if (min == 0 && *str) { if ((list[0] = strdup(str)) == NULL) goto out; } else { if ((list[0] = el_calloc(min + 1, sizeof(*list[0]))) == NULL) goto out; (void)memcpy(list[0], list[1], min); list[0][min] = '\0'; } return list; out: el_free(list); return NULL; } char * rl_filename_completion_function (const char *text, int state) { return fn_filename_completion_function(text, state); } void rl_forced_update_display(void) { el_set(e, EL_REFRESH); } int _rl_abort_internal(void) { el_beep(e); longjmp(topbuf, 1); /*NOTREACHED*/ } int _rl_qsort_string_compare(char **s1, char **s2) { return strcoll(*s1, *s2); } HISTORY_STATE * history_get_history_state(void) { HISTORY_STATE *hs; if ((hs = el_malloc(sizeof(*hs))) == NULL) return NULL; hs->length = history_length; return hs; } int /*ARGSUSED*/ rl_kill_text(int from __attribute__((__unused__)), int to __attribute__((__unused__))) { return 0; } Keymap rl_make_bare_keymap(void) { return NULL; } Keymap rl_get_keymap(void) { return NULL; } void /*ARGSUSED*/ rl_set_keymap(Keymap k __attribute__((__unused__))) { } int /*ARGSUSED*/ rl_generic_bind(int type __attribute__((__unused__)), const char * keyseq __attribute__((__unused__)), const char * data __attribute__((__unused__)), Keymap k __attribute__((__unused__))) { return 0; } int /*ARGSUSED*/ rl_bind_key_in_map(int key __attribute__((__unused__)), rl_command_func_t *fun __attribute__((__unused__)), Keymap k __attribute__((__unused__))) { return 0; } int rl_set_key(const char *keyseq __attribute__((__unused__)), rl_command_func_t *function __attribute__((__unused__)), Keymap k __attribute__((__unused__))) { return 0; } /* unsupported, but needed by python */ void rl_cleanup_after_signal(void) { } int rl_on_new_line(void) { return 0; } void rl_free_line_state(void) { } int /*ARGSUSED*/ rl_set_keyboard_input_timeout(int u __attribute__((__unused__))) { return 0; } void rl_resize_terminal(void) { el_resize(e); } void rl_reset_after_signal(void) { if (rl_prep_term_function) - (*rl_prep_term_function)(); + (*rl_prep_term_function)(1); } void rl_echo_signal_char(int sig) { int c = tty_get_signal_character(e, sig); if (c == -1) return; re_putc(e, c, 0); } int rl_crlf(void) { re_putc(e, '\n', 0); return 0; } int rl_ding(void) { re_putc(e, '\a', 0); return 0; } int rl_abort(int count, int key) { return count && key ? 0 : 0; } int rl_set_keymap_name(const char *name, Keymap k) { return name && k ? 0 : 0; } histdata_t free_history_entry(HIST_ENTRY *he) { return he ? NULL : NULL; } void _rl_erase_entire_line(void) { } diff --git a/contrib/libedit/readline/readline.h b/contrib/libedit/readline/readline.h index 2bd0b7e80ab6..03e2d2eb8e1e 100644 --- a/contrib/libedit/readline/readline.h +++ b/contrib/libedit/readline/readline.h @@ -1,267 +1,277 @@ -/* $NetBSD: readline.h,v 1.53 2022/02/19 17:45:02 christos Exp $ */ +/* $NetBSD: readline.h,v 1.55 2023/04/25 17:51:32 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _READLINE_H_ #define _READLINE_H_ #include #include /* list of readline stuff supported by editline library's readline wrapper */ /* typedefs */ -typedef int Function(const char *, int); -typedef char *CPFunction(const char *, int); -typedef void VFunction(void); +typedef int rl_linebuf_func_t(const char *, int); +typedef void rl_voidfunc_t(void); +typedef void rl_vintfunc_t(int); typedef void rl_vcpfunc_t(char *); typedef char **rl_completion_func_t(const char *, int, int); typedef char *rl_compentry_func_t(const char *, int); +typedef void rl_compdisp_func_t(char **, int, int); typedef int rl_command_func_t(int, int); typedef int rl_hook_func_t(void); typedef int rl_icppfunc_t(char **); /* only supports length */ typedef struct { int length; } HISTORY_STATE; typedef void *histdata_t; typedef struct _hist_entry { const char *line; histdata_t data; } HIST_ENTRY; typedef struct _keymap_entry { char type; #define ISFUNC 0 #define ISKMAP 1 #define ISMACR 2 - Function *function; + rl_linebuf_func_t *function; } KEYMAP_ENTRY; #define KEYMAP_SIZE 256 typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[KEYMAP_SIZE]; typedef KEYMAP_ENTRY *Keymap; #define control_character_threshold 0x20 #define control_character_bit 0x40 #ifndef CTRL #include #if !defined(__sun) && !defined(__hpux) && !defined(_AIX) #include #endif #ifndef CTRL #define CTRL(c) ((c) & 037) #endif #endif #ifndef UNCTRL #define UNCTRL(c) (((c) - 'a' + 'A')|control_character_bit) #endif #define RUBOUT 0x7f #define ABORT_CHAR CTRL('G') #define RL_READLINE_VERSION 0x0402 #define RL_PROMPT_START_IGNORE '\1' #define RL_PROMPT_END_IGNORE '\2' #define RL_STATE_NONE 0x000000 #define RL_STATE_DONE 0x000001 #define RL_SETSTATE(x) (rl_readline_state |= ((unsigned long) x)) #define RL_UNSETSTATE(x) (rl_readline_state &= ~((unsigned long) x)) #define RL_ISSTATE(x) (rl_readline_state & ((unsigned long) x)) /* global variables used by readline enabled applications */ #ifdef __cplusplus extern "C" { #endif extern const char *rl_library_version; extern int rl_readline_version; extern const char *rl_readline_name; extern FILE *rl_instream; extern FILE *rl_outstream; extern char *rl_line_buffer; -extern int rl_point, rl_end; -extern int history_base, history_length; -extern int max_input_history; +extern int rl_point, rl_end; extern const char *rl_basic_quote_characters; extern const char *rl_basic_word_break_characters; extern char *rl_completer_word_break_characters; extern const char *rl_completer_quote_characters; extern rl_compentry_func_t *rl_completion_entry_function; extern char *(*rl_completion_word_break_hook)(void); extern rl_completion_func_t *rl_attempted_completion_function; extern int rl_attempted_completion_over; extern int rl_completion_type; extern int rl_completion_query_items; extern const char *rl_special_prefixes; extern int rl_completion_append_character; extern int rl_inhibit_completion; -extern rl_hook_func_t *rl_pre_input_hook; -extern rl_hook_func_t *rl_startup_hook; +extern rl_hook_func_t *rl_pre_input_hook; +extern rl_hook_func_t *rl_startup_hook; extern char *rl_terminal_name; extern int rl_already_prompted; extern char *rl_prompt; extern int rl_done; +extern rl_vcpfunc_t *rl_linefunc; +extern rl_hook_func_t *rl_startup1_hook; +extern char *rl_prompt_saved; +extern int history_base, history_length; +extern int history_offset; +extern char history_expansion_char; +extern char history_subst_char; +extern char *history_no_expand_chars; +extern rl_linebuf_func_t *history_inhibit_expansion_function; +extern int max_input_history; + /* * The following is not implemented */ extern unsigned long rl_readline_state; extern int rl_catch_signals; extern int rl_catch_sigwinch; extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap; extern int rl_filename_completion_desired; extern int rl_ignore_completion_duplicates; extern int (*rl_getc_function)(FILE *); -extern VFunction *rl_redisplay_function; -extern VFunction *rl_completion_display_matches_hook; -extern VFunction *rl_prep_term_function; -extern VFunction *rl_deprep_term_function; +extern rl_voidfunc_t *rl_redisplay_function; +extern rl_compdisp_func_t *rl_completion_display_matches_hook; +extern rl_vintfunc_t *rl_prep_term_function; +extern rl_voidfunc_t *rl_deprep_term_function; extern rl_hook_func_t *rl_event_hook; extern int readline_echoing_p; extern int _rl_print_completions_horizontally; extern int _rl_complete_mark_directories; extern rl_icppfunc_t *rl_directory_completion_hook; extern int rl_completion_suppress_append; extern int rl_sort_completion_matches; extern int _rl_completion_prefix_display_length; extern int _rl_echoing_p; extern int history_max_entries; extern char *rl_display_prompt; extern int rl_erase_empty_line; /* supported functions */ char *readline(const char *); int rl_initialize(void); void using_history(void); int add_history(const char *); void clear_history(void); int append_history(int, const char *); void stifle_history(int); int unstifle_history(void); int history_is_stifled(void); int where_history(void); HIST_ENTRY *current_history(void); HIST_ENTRY *history_get(int); HIST_ENTRY *remove_history(int); HIST_ENTRY *replace_history_entry(int, const char *, histdata_t); int history_total_bytes(void); int history_set_pos(int); HIST_ENTRY *previous_history(void); HIST_ENTRY *next_history(void); HIST_ENTRY **history_list(void); int history_search(const char *, int); int history_search_prefix(const char *, int); int history_search_pos(const char *, int, int); int read_history(const char *); int write_history(const char *); int history_truncate_file(const char *, int); int history_expand(char *, char **); char **history_tokenize(const char *); const char *get_history_event(const char *, int *, int); char *history_arg_extract(int, int, const char *); char *tilde_expand(char *); char *filename_completion_function(const char *, int); char *username_completion_function(const char *, int); int rl_complete(int, int); int rl_read_key(void); char **completion_matches(/* const */ char *, rl_compentry_func_t *); void rl_display_match_list(char **, int, int); int rl_insert(int, int); int rl_insert_text(const char *); int rl_reset_terminal(const char *); void rl_resize_terminal(void); int rl_bind_key(int, rl_command_func_t *); int rl_newline(int, int); void rl_callback_read_char(void); void rl_callback_handler_install(const char *, rl_vcpfunc_t *); void rl_callback_handler_remove(void); void rl_redisplay(void); int rl_get_previous_history(int, int); void rl_prep_terminal(int); void rl_deprep_terminal(void); int rl_read_init_file(const char *); int rl_parse_and_bind(const char *); int rl_variable_bind(const char *, const char *); int rl_stuff_char(int); int rl_add_defun(const char *, rl_command_func_t *, int); HISTORY_STATE *history_get_history_state(void); void rl_get_screen_size(int *, int *); void rl_set_screen_size(int, int); char *rl_filename_completion_function(const char *, int); int _rl_abort_internal(void); int _rl_qsort_string_compare(char **, char **); char **rl_completion_matches(const char *, rl_compentry_func_t *); void rl_forced_update_display(void); int rl_set_prompt(const char *); int rl_on_new_line(void); void rl_reset_after_signal(void); void rl_echo_signal_char(int); int rl_crlf(void); int rl_ding(void); char *rl_copy_text(int, int); void rl_replace_line(const char *, int); int rl_delete_text(int, int); void rl_message(const char *format, ...) __attribute__((__format__(__printf__, 1, 2))); void rl_save_prompt(void); void rl_restore_prompt(void); /* * The following are not implemented */ int rl_kill_text(int, int); Keymap rl_get_keymap(void); void rl_set_keymap(Keymap); Keymap rl_make_bare_keymap(void); int rl_generic_bind(int, const char *, const char *, Keymap); int rl_bind_key_in_map(int, rl_command_func_t *, Keymap); int rl_set_key(const char *, rl_command_func_t *, Keymap); void rl_cleanup_after_signal(void); void rl_free_line_state(void); int rl_set_keyboard_input_timeout(int); int rl_abort(int, int); int rl_set_keymap_name(const char *, Keymap); histdata_t free_history_entry(HIST_ENTRY *); void _rl_erase_entire_line(void); #ifdef __cplusplus } #endif #endif /* _READLINE_H_ */ diff --git a/contrib/libedit/refresh.c b/contrib/libedit/refresh.c index 26a49fe751c6..ae62f88f0bea 100644 --- a/contrib/libedit/refresh.c +++ b/contrib/libedit/refresh.c @@ -1,1228 +1,1232 @@ -/* $NetBSD: refresh.c,v 1.58 2021/09/09 20:24:07 christos Exp $ */ +/* $NetBSD: refresh.c,v 1.60 2024/12/05 22:21:53 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: refresh.c,v 1.58 2021/09/09 20:24:07 christos Exp $"); +__RCSID("$NetBSD: refresh.c,v 1.60 2024/12/05 22:21:53 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * refresh.c: Lower level screen refreshing functions */ #include #include #include #include #include "el.h" static void re_nextline(EditLine *); static void re_addc(EditLine *, wint_t); static void re_update_line(EditLine *, wchar_t *, wchar_t *, int); static void re_insert (EditLine *, wchar_t *, int, int, wchar_t *, int); static void re_delete(EditLine *, wchar_t *, int, int, int); static void re_fastputc(EditLine *, wint_t); static void re_clear_eol(EditLine *, int, int, int); static void re__strncopy(wchar_t *, wchar_t *, size_t); static void re__copy_and_pad(wchar_t *, const wchar_t *, size_t); #ifdef DEBUG_REFRESH static void re_printstr(EditLine *, const char *, wchar_t *, wchar_t *); #define __F el->el_errfile #define ELRE_ASSERT(a, b, c) do \ if (/*CONSTCOND*/ a) { \ (void) fprintf b; \ c; \ } \ while (/*CONSTCOND*/0) #define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;) /* re_printstr(): * Print a string on the debugging pty */ static void re_printstr(EditLine *el, const char *str, wchar_t *f, wchar_t *t) { ELRE_DEBUG(1, (__F, "%s:\"", str)); while (f < t) ELRE_DEBUG(1, (__F, "%c", *f++ & 0177)); ELRE_DEBUG(1, (__F, "\"\r\n")); } #else #define ELRE_ASSERT(a, b, c) #define ELRE_DEBUG(a, b) #endif /* re_nextline(): * Move to the next line or scroll */ static void re_nextline(EditLine *el) { el->el_refresh.r_cursor.h = 0; /* reset it. */ /* * If we would overflow (input is longer than terminal size), * emulate scroll by dropping first line and shuffling the rest. * We do this via pointer shuffling - it's safe in this case * and we avoid memcpy(). */ if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v) { int i, lins = el->el_terminal.t_size.v; wint_t *firstline = el->el_vdisplay[0]; for(i = 1; i < lins; i++) el->el_vdisplay[i - 1] = el->el_vdisplay[i]; firstline[0] = '\0'; /* empty the string */ el->el_vdisplay[i - 1] = firstline; } else el->el_refresh.r_cursor.v++; ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_terminal.t_size.v, (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n", el->el_refresh.r_cursor.v, el->el_terminal.t_size.v), abort()); } /* re_addc(): * Draw c, expanding tabs, control chars etc. */ static void re_addc(EditLine *el, wint_t c) { switch (ct_chr_class(c)) { case CHTYPE_TAB: /* expand the tab */ for (;;) { re_putc(el, ' ', 1); if ((el->el_refresh.r_cursor.h & 07) == 0) break; /* go until tab stop */ } break; case CHTYPE_NL: { int oldv = el->el_refresh.r_cursor.v; re_putc(el, '\0', 0); /* assure end of line */ if (oldv == el->el_refresh.r_cursor.v) /* XXX */ re_nextline(el); break; } case CHTYPE_PRINT: re_putc(el, c, 1); break; default: { wchar_t visbuf[VISUAL_WIDTH_MAX]; ssize_t i, n = ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c); for (i = 0; n-- > 0; ++i) re_putc(el, visbuf[i], 1); break; } } } /* re_putliteral(): * Place the literal string given */ libedit_private void re_putliteral(EditLine *el, const wchar_t *begin, const wchar_t *end) { coord_t *cur = &el->el_refresh.r_cursor; wint_t c; int sizeh = el->el_terminal.t_size.h; int i, w; c = literal_add(el, begin, end, &w); - if (c == 0 || w <= 0) + if (c == 0 || w < 0) return; el->el_vdisplay[cur->v][cur->h] = c; i = w; if (i > sizeh - cur->h) /* avoid overflow */ i = sizeh - cur->h; while (--i > 0) el->el_vdisplay[cur->v][cur->h + i] = MB_FILL_CHAR; - cur->h += w; + cur->h += w ? w : 1; if (cur->h >= sizeh) { /* assure end of line */ el->el_vdisplay[cur->v][sizeh] = '\0'; re_nextline(el); } } /* re_putc(): * Draw the character given */ libedit_private void re_putc(EditLine *el, wint_t c, int shift) { coord_t *cur = &el->el_refresh.r_cursor; int i, w = wcwidth(c); int sizeh = el->el_terminal.t_size.h; ELRE_DEBUG(1, (__F, "printing %5x '%lc'\r\n", c, c)); if (w == -1) w = 0; while (shift && (cur->h + w > sizeh)) re_putc(el, ' ', 1); el->el_vdisplay[cur->v][cur->h] = c; /* assumes !shift is only used for single-column chars */ i = w; while (--i > 0) el->el_vdisplay[cur->v][cur->h + i] = MB_FILL_CHAR; if (!shift) return; - cur->h += w; /* advance to next place */ + cur->h += w ? w : 1; /* advance to next place */ if (cur->h >= sizeh) { /* assure end of line */ el->el_vdisplay[cur->v][sizeh] = '\0'; re_nextline(el); } } /* re_refresh(): * draws the new virtual screen image from the current input * line, then goes line-by-line changing the real image to the new * virtual image. The routine to re-draw a line can be replaced * easily in hopes of a smarter one being placed there. */ libedit_private void re_refresh(EditLine *el) { int i, rhdiff; wchar_t *cp, *st; coord_t cur; #ifdef notyet size_t termsz; #endif ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%ls:\r\n", el->el_line.buffer)); literal_clear(el); /* reset the Drawing cursor */ el->el_refresh.r_cursor.h = 0; el->el_refresh.r_cursor.v = 0; terminal_move_to_char(el, 0); /* temporarily draw rprompt to calculate its size */ prompt_print(el, EL_RPROMPT); /* reset the Drawing cursor */ el->el_refresh.r_cursor.h = 0; el->el_refresh.r_cursor.v = 0; if (el->el_line.cursor >= el->el_line.lastchar) { if (el->el_map.current == el->el_map.alt && el->el_line.lastchar != el->el_line.buffer) el->el_line.cursor = el->el_line.lastchar - 1; else el->el_line.cursor = el->el_line.lastchar; } cur.h = -1; /* set flag in case I'm not set */ cur.v = 0; prompt_print(el, EL_PROMPT); /* draw the current input buffer */ #if notyet termsz = el->el_terminal.t_size.h * el->el_terminal.t_size.v; if (el->el_line.lastchar - el->el_line.buffer > termsz) { /* * If line is longer than terminal, process only part * of line which would influence display. */ size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz; st = el->el_line.lastchar - rem - (termsz - (((rem / el->el_terminal.t_size.v) - 1) * el->el_terminal.t_size.v)); } else #endif st = el->el_line.buffer; for (cp = st; cp < el->el_line.lastchar; cp++) { if (cp == el->el_line.cursor) { int w = wcwidth(*cp); /* save for later */ cur.h = el->el_refresh.r_cursor.h; cur.v = el->el_refresh.r_cursor.v; /* handle being at a linebroken doublewidth char */ if (w > 1 && el->el_refresh.r_cursor.h + w > el->el_terminal.t_size.h) { cur.h = 0; cur.v++; } } re_addc(el, *cp); } if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */ cur.h = el->el_refresh.r_cursor.h; cur.v = el->el_refresh.r_cursor.v; } rhdiff = el->el_terminal.t_size.h - el->el_refresh.r_cursor.h - el->el_rprompt.p_pos.h; if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v && !el->el_refresh.r_cursor.v && rhdiff > 1) { /* * have a right-hand side prompt that will fit * on the end of the first line with at least * one character gap to the input buffer. */ while (--rhdiff > 0) /* pad out with spaces */ re_putc(el, ' ', 1); prompt_print(el, EL_RPROMPT); } else { el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */ el->el_rprompt.p_pos.v = 0; } re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */ el->el_refresh.r_newcv = el->el_refresh.r_cursor.v; ELRE_DEBUG(1, (__F, "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n", el->el_terminal.t_size.h, el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0], &el->el_scratch))); ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv)); for (i = 0; i <= el->el_refresh.r_newcv; i++) { /* NOTE THAT re_update_line MAY CHANGE el_display[i] */ re_update_line(el, (wchar_t *)el->el_display[i], (wchar_t *)el->el_vdisplay[i], i); /* * Copy the new line to be the current one, and pad out with * spaces to the full width of the terminal so that if we try * moving the cursor by writing the character that is at the * end of the screen line, it won't be a NUL or some old * leftover stuff. */ re__copy_and_pad((wchar_t *)el->el_display[i], (wchar_t *)el->el_vdisplay[i], (size_t) el->el_terminal.t_size.h); } ELRE_DEBUG(1, (__F, "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n", el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i)); if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv) for (; i <= el->el_refresh.r_oldcv; i++) { terminal_move_to_line(el, i); terminal_move_to_char(el, 0); /* This wcslen should be safe even with MB_FILL_CHARs */ terminal_clear_EOL(el, (int) wcslen((const wchar_t *)el->el_display[i])); #ifdef DEBUG_REFRESH terminal_overwrite(el, L"C\b", 2); #endif /* DEBUG_REFRESH */ el->el_display[i][0] = '\0'; } el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */ ELRE_DEBUG(1, (__F, "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n", el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v, cur.h, cur.v)); terminal_move_to_line(el, cur.v); /* go to where the cursor is */ terminal_move_to_char(el, cur.h); } /* re_goto_bottom(): * used to go to last used screen line */ libedit_private void re_goto_bottom(EditLine *el) { terminal_move_to_line(el, el->el_refresh.r_oldcv); terminal__putc(el, '\n'); re_clear_display(el); terminal__flush(el); } /* re_insert(): * insert num characters of s into d (in front of the character) * at dat, maximum length of d is dlen */ static void /*ARGSUSED*/ re_insert(EditLine *el __attribute__((__unused__)), wchar_t *d, int dat, int dlen, wchar_t *s, int num) { wchar_t *a, *b; if (num <= 0) return; if (num > dlen - dat) num = dlen - dat; ELRE_DEBUG(1, (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, ct_encode_string(d, &el->el_scratch))); ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s, &el->el_scratch))); /* open up the space for num chars */ if (num > 0) { b = d + dlen - 1; a = b - num; while (a >= &d[dat]) *b-- = *a--; d[dlen] = '\0'; /* just in case */ } ELRE_DEBUG(1, (__F, "re_insert() after insert: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, ct_encode_string(d, &el->el_scratch))); ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s, &el->el_scratch))); /* copy the characters */ for (a = d + dat; (a < d + dlen) && (num > 0); num--) *a++ = *s++; #ifdef notyet /* ct_encode_string() uses a static buffer, so we can't conveniently * encode both d & s here */ ELRE_DEBUG(1, (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n", num, dat, dlen, d, s)); ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); #endif } /* re_delete(): * delete num characters d at dat, maximum length of d is dlen */ static void /*ARGSUSED*/ re_delete(EditLine *el __attribute__((__unused__)), wchar_t *d, int dat, int dlen, int num) { wchar_t *a, *b; if (num <= 0) return; if (dat + num >= dlen) { d[dat] = '\0'; return; } ELRE_DEBUG(1, (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, ct_encode_string(d, &el->el_scratch))); /* open up the space for num chars */ if (num > 0) { b = d + dat; a = b + num; while (a < &d[dlen]) *b++ = *a++; d[dlen] = '\0'; /* just in case */ } ELRE_DEBUG(1, (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, ct_encode_string(d, &el->el_scratch))); } /* re__strncopy(): * Like strncpy without padding. */ static void re__strncopy(wchar_t *a, wchar_t *b, size_t n) { while (n-- && *b) *a++ = *b++; } /* re_clear_eol(): * Find the number of characters we need to clear till the end of line * in order to make sure that we have cleared the previous contents of * the line. fx and sx is the number of characters inserted or deleted * in the first or second diff, diff is the difference between the * number of characters between the new and old line. */ static void re_clear_eol(EditLine *el, int fx, int sx, int diff) { ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n", sx, fx, diff)); if (fx < 0) fx = -fx; if (sx < 0) sx = -sx; if (fx > diff) diff = fx; if (sx > diff) diff = sx; ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff)); terminal_clear_EOL(el, diff); } /***************************************************************** re_update_line() is based on finding the middle difference of each line on the screen; vis: /old first difference /beginning of line | /old last same /old EOL v v v v old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as new: eddie> Oh, my little buggy says to me, as lurgid as ^ ^ ^ ^ \beginning of line | \new last same \new end of line \new first difference all are character pointers for the sake of speed. Special cases for no differences, as well as for end of line additions must be handled. **************************************************************** */ /* Minimum at which doing an insert it "worth it". This should be about * half the "cost" of going into insert mode, inserting a character, and * going back out. This should really be calculated from the termcap * data... For the moment, a good number for ANSI terminals. */ #define MIN_END_KEEP 4 static void re_update_line(EditLine *el, wchar_t *old, wchar_t *new, int i) { wchar_t *o, *n, *p, c; wchar_t *ofd, *ols, *oe, *nfd, *nls, *ne; wchar_t *osb, *ose, *nsb, *nse; int fx, sx; size_t len; /* * find first diff */ for (o = old, n = new; *o && (*o == *n); o++, n++) continue; ofd = o; nfd = n; /* * Find the end of both old and new */ while (*o) o++; /* * Remove any trailing blanks off of the end, being careful not to * back up past the beginning. */ while (ofd < o) { if (o[-1] != ' ') break; o--; } oe = o; *oe = '\0'; while (*n) n++; /* remove blanks from end of new */ while (nfd < n) { if (n[-1] != ' ') break; n--; } ne = n; *ne = '\0'; /* * if no diff, continue to next line of redraw */ if (*ofd == '\0' && *nfd == '\0') { ELRE_DEBUG(1, (__F, "no difference.\r\n")); return; } /* * find last same pointer */ while ((o > ofd) && (n > nfd) && (*--o == *--n)) continue; ols = ++o; nls = ++n; /* * find same beginning and same end */ osb = ols; nsb = nls; ose = ols; nse = nls; /* * case 1: insert: scan from nfd to nls looking for *ofd */ if (*ofd) { for (c = *ofd, n = nfd; n < nls; n++) { if (c == *n) { for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++) continue; /* * if the new match is longer and it's worth * keeping, then we take it */ if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) { nsb = n; nse = p; osb = ofd; ose = o; } } } } /* * case 2: delete: scan from ofd to ols looking for *nfd */ if (*nfd) { for (c = *nfd, o = ofd; o < ols; o++) { if (c == *o) { for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++) continue; /* * if the new match is longer and it's worth * keeping, then we take it */ if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) { nsb = nfd; nse = n; osb = o; ose = p; } } } } /* * Pragmatics I: If old trailing whitespace or not enough characters to * save to be worth it, then don't save the last same info. */ if ((oe - ols) < MIN_END_KEEP) { ols = oe; nls = ne; } /* * Pragmatics II: if the terminal isn't smart enough, make the data * dumber so the smart update doesn't try anything fancy */ /* * fx is the number of characters we need to insert/delete: in the * beginning to bring the two same begins together */ fx = (int)((nsb - nfd) - (osb - ofd)); /* * sx is the number of characters we need to insert/delete: in the * end to bring the two same last parts together */ sx = (int)((nls - nse) - (ols - ose)); if (!EL_CAN_INSERT) { if (fx > 0) { osb = ols; ose = ols; nsb = nls; nse = nls; } if (sx > 0) { ols = oe; nls = ne; } if ((ols - ofd) < (nls - nfd)) { ols = oe; nls = ne; } } if (!EL_CAN_DELETE) { if (fx < 0) { osb = ols; ose = ols; nsb = nls; nse = nls; } if (sx < 0) { ols = oe; nls = ne; } if ((ols - ofd) > (nls - nfd)) { ols = oe; nls = ne; } } /* * Pragmatics III: make sure the middle shifted pointers are correct if * they don't point to anything (we may have moved ols or nls). */ /* if the change isn't worth it, don't bother */ /* was: if (osb == ose) */ if ((ose - osb) < MIN_END_KEEP) { osb = ols; ose = ols; nsb = nls; nse = nls; } /* * Now that we are done with pragmatics we recompute fx, sx */ fx = (int)((nsb - nfd) - (osb - ofd)); sx = (int)((nls - nse) - (ols - ose)); ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx)); ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n", ofd - old, osb - old, ose - old, ols - old, oe - old)); ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n", nfd - new, nsb - new, nse - new, nls - new, ne - new)); ELRE_DEBUG(1, (__F, "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n")); ELRE_DEBUG(1, (__F, "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n")); #ifdef DEBUG_REFRESH re_printstr(el, "old- oe", old, oe); re_printstr(el, "new- ne", new, ne); re_printstr(el, "old-ofd", old, ofd); re_printstr(el, "new-nfd", new, nfd); re_printstr(el, "ofd-osb", ofd, osb); re_printstr(el, "nfd-nsb", nfd, nsb); re_printstr(el, "osb-ose", osb, ose); re_printstr(el, "nsb-nse", nsb, nse); re_printstr(el, "ose-ols", ose, ols); re_printstr(el, "nse-nls", nse, nls); re_printstr(el, "ols- oe", ols, oe); re_printstr(el, "nls- ne", nls, ne); #endif /* DEBUG_REFRESH */ /* * el_cursor.v to this line i MUST be in this routine so that if we * don't have to change the line, we don't move to it. el_cursor.h to * first diff char */ terminal_move_to_line(el, i); /* * at this point we have something like this: * * /old /ofd /osb /ose /ols /oe * v.....................v v..................v v........v * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as * ^.....................^ ^..................^ ^........^ * \new \nfd \nsb \nse \nls \ne * * fx is the difference in length between the chars between nfd and * nsb, and the chars between ofd and osb, and is thus the number of * characters to delete if < 0 (new is shorter than old, as above), * or insert (new is longer than short). * * sx is the same for the second differences. */ /* * if we have a net insert on the first difference, AND inserting the * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful * character (which is ne if nls != ne, otherwise is nse) off the edge * of the screen (el->el_terminal.t_size.h) else we do the deletes first * so that we keep everything we need to. */ /* * if the last same is the same like the end, there is no last same * part, otherwise we want to keep the last same part set p to the * last useful old character */ p = (ols != oe) ? oe : ose; /* * if (There is a diffence in the beginning) && (we need to insert * characters) && (the number of characters to insert is less than * the term width) * We need to do an insert! * else if (we need to delete characters) * We need to delete characters! * else * No insert or delete */ if ((nsb != nfd) && fx > 0 && ((p - old) + fx <= el->el_terminal.t_size.h)) { ELRE_DEBUG(1, (__F, "first diff insert at %td...\r\n", nfd - new)); /* * Move to the first char to insert, where the first diff is. */ terminal_move_to_char(el, (int)(nfd - new)); /* * Check if we have stuff to keep at end */ if (nsb != ne) { ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); /* * insert fx chars of new starting at nfd */ if (fx > 0) { ELRE_DEBUG(!EL_CAN_INSERT, (__F, "ERROR: cannot insert in early first diff\n")); terminal_insertwrite(el, nfd, fx); re_insert(el, old, (int)(ofd - old), el->el_terminal.t_size.h, nfd, fx); } /* * write (nsb-nfd) - fx chars of new starting at * (nfd + fx) */ len = (size_t) ((nsb - nfd) - fx); terminal_overwrite(el, (nfd + fx), len); re__strncopy(ofd + fx, nfd + fx, len); } else { ELRE_DEBUG(1, (__F, "without anything to save\r\n")); len = (size_t)(nsb - nfd); terminal_overwrite(el, nfd, len); re__strncopy(ofd, nfd, len); /* * Done */ return; } } else if (fx < 0) { ELRE_DEBUG(1, (__F, "first diff delete at %td...\r\n", ofd - old)); /* * move to the first char to delete where the first diff is */ terminal_move_to_char(el, (int)(ofd - old)); /* * Check if we have stuff to save */ if (osb != oe) { ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); /* * fx is less than zero *always* here but we check * for code symmetry */ if (fx < 0) { ELRE_DEBUG(!EL_CAN_DELETE, (__F, "ERROR: cannot delete in first diff\n")); terminal_deletechars(el, -fx); re_delete(el, old, (int)(ofd - old), el->el_terminal.t_size.h, -fx); } /* * write (nsb-nfd) chars of new starting at nfd */ len = (size_t) (nsb - nfd); terminal_overwrite(el, nfd, len); re__strncopy(ofd, nfd, len); } else { ELRE_DEBUG(1, (__F, "but with nothing left to save\r\n")); /* * write (nsb-nfd) chars of new starting at nfd */ terminal_overwrite(el, nfd, (size_t)(nsb - nfd)); re_clear_eol(el, fx, sx, (int)((oe - old) - (ne - new))); /* * Done */ return; } } else fx = 0; if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) { ELRE_DEBUG(1, (__F, "second diff delete at %td...\r\n", (ose - old) + fx)); /* * Check if we have stuff to delete */ /* * fx is the number of characters inserted (+) or deleted (-) */ terminal_move_to_char(el, (int)((ose - old) + fx)); /* * Check if we have stuff to save */ if (ols != oe) { ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); /* * Again a duplicate test. */ if (sx < 0) { ELRE_DEBUG(!EL_CAN_DELETE, (__F, "ERROR: cannot delete in second diff\n")); terminal_deletechars(el, -sx); } /* * write (nls-nse) chars of new starting at nse */ terminal_overwrite(el, nse, (size_t)(nls - nse)); } else { ELRE_DEBUG(1, (__F, "but with nothing left to save\r\n")); terminal_overwrite(el, nse, (size_t)(nls - nse)); re_clear_eol(el, fx, sx, (int)((oe - old) - (ne - new))); } } /* * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... */ if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n", nfd - new)); terminal_move_to_char(el, (int)(nfd - new)); /* * Check if we have stuff to keep at the end */ if (nsb != ne) { ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); /* * We have to recalculate fx here because we set it * to zero above as a flag saying that we hadn't done * an early first insert. */ fx = (int)((nsb - nfd) - (osb - ofd)); if (fx > 0) { /* * insert fx chars of new starting at nfd */ ELRE_DEBUG(!EL_CAN_INSERT, (__F, "ERROR: cannot insert in late first diff\n")); terminal_insertwrite(el, nfd, fx); re_insert(el, old, (int)(ofd - old), el->el_terminal.t_size.h, nfd, fx); } /* * write (nsb-nfd) - fx chars of new starting at * (nfd + fx) */ len = (size_t) ((nsb - nfd) - fx); terminal_overwrite(el, (nfd + fx), len); re__strncopy(ofd + fx, nfd + fx, len); } else { ELRE_DEBUG(1, (__F, "without anything to save\r\n")); len = (size_t) (nsb - nfd); terminal_overwrite(el, nfd, len); re__strncopy(ofd, nfd, len); } } /* * line is now NEW up to nse */ if (sx >= 0) { ELRE_DEBUG(1, (__F, "second diff insert at %d...\r\n", (int)(nse - new))); terminal_move_to_char(el, (int)(nse - new)); if (ols != oe) { ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); if (sx > 0) { /* insert sx chars of new starting at nse */ ELRE_DEBUG(!EL_CAN_INSERT, (__F, "ERROR: cannot insert in second diff\n")); terminal_insertwrite(el, nse, sx); } /* * write (nls-nse) - sx chars of new starting at * (nse + sx) */ terminal_overwrite(el, (nse + sx), (size_t)((nls - nse) - sx)); } else { ELRE_DEBUG(1, (__F, "without anything to save\r\n")); terminal_overwrite(el, nse, (size_t)(nls - nse)); /* * No need to do a clear-to-end here because we were * doing a second insert, so we will have over * written all of the old string. */ } } ELRE_DEBUG(1, (__F, "done.\r\n")); } /* re__copy_and_pad(): * Copy string and pad with spaces */ static void re__copy_and_pad(wchar_t *dst, const wchar_t *src, size_t width) { size_t i; for (i = 0; i < width; i++) { if (*src == '\0') break; *dst++ = *src++; } for (; i < width; i++) *dst++ = ' '; *dst = '\0'; } /* re_refresh_cursor(): * Move to the new cursor position */ libedit_private void re_refresh_cursor(EditLine *el) { wchar_t *cp; int h, v, th, w; if (el->el_line.cursor >= el->el_line.lastchar) { if (el->el_map.current == el->el_map.alt && el->el_line.lastchar != el->el_line.buffer) el->el_line.cursor = el->el_line.lastchar - 1; else el->el_line.cursor = el->el_line.lastchar; } /* first we must find where the cursor is... */ h = el->el_prompt.p_pos.h; v = el->el_prompt.p_pos.v; th = el->el_terminal.t_size.h; /* optimize for speed */ /* do input buffer to el->el_line.cursor */ for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { switch (ct_chr_class(*cp)) { case CHTYPE_NL: /* handle newline in data part too */ h = 0; v++; break; case CHTYPE_TAB: /* if a tab, to next tab stop */ while (++h & 07) continue; break; default: w = wcwidth(*cp); if (w > 1 && h + w > th) { /* won't fit on line */ h = 0; v++; } h += ct_visual_width(*cp); break; } if (h >= th) { /* check, extra long tabs picked up here also */ h -= th; v++; } } /* if we have a next character, and it's a doublewidth one, we need to * check whether we need to linebreak for it to fit */ if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1) if (h + w > th) { h = 0; v++; } /* now go there */ terminal_move_to_line(el, v); terminal_move_to_char(el, h); terminal__flush(el); } /* re_fastputc(): * Add a character fast. */ static void re_fastputc(EditLine *el, wint_t c) { wint_t *lastline; int w; w = wcwidth(c); while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h) re_fastputc(el, ' '); terminal__putc(el, c); el->el_display[el->el_cursor.v][el->el_cursor.h++] = c; while (--w > 0) el->el_display[el->el_cursor.v][el->el_cursor.h++] = MB_FILL_CHAR; if (el->el_cursor.h >= el->el_terminal.t_size.h) { /* if we must overflow */ el->el_cursor.h = 0; /* * If we would overflow (input is longer than terminal size), * emulate scroll by dropping first line and shuffling the rest. * We do this via pointer shuffling - it's safe in this case * and we avoid memcpy(). */ if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) { int i, lins = el->el_terminal.t_size.v; lastline = el->el_display[0]; for(i = 1; i < lins; i++) el->el_display[i - 1] = el->el_display[i]; el->el_display[i - 1] = lastline; } else { el->el_cursor.v++; lastline = el->el_display[++el->el_refresh.r_oldcv]; } re__copy_and_pad((wchar_t *)lastline, L"", (size_t)el->el_terminal.t_size.h); if (EL_HAS_AUTO_MARGINS) { if (EL_HAS_MAGIC_MARGINS) { terminal__putc(el, ' '); terminal__putc(el, '\b'); } } else { terminal__putc(el, '\r'); terminal__putc(el, '\n'); } } } /* re_fastaddc(): * we added just one char, handle it fast. * Assumes that screen cursor == real cursor */ libedit_private void re_fastaddc(EditLine *el) { wchar_t c; int rhdiff; + if (el->el_line.cursor == el->el_line.buffer) { + re_refresh(el); + return; + } c = el->el_line.cursor[-1]; if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { re_refresh(el); /* too hard to handle */ return; } rhdiff = el->el_terminal.t_size.h - el->el_cursor.h - el->el_rprompt.p_pos.h; if (el->el_rprompt.p_pos.h && rhdiff < 3) { re_refresh(el); /* clear out rprompt if less than 1 char gap */ return; } /* else (only do at end of line, no TAB) */ switch (ct_chr_class(c)) { case CHTYPE_TAB: /* already handled, should never happen here */ break; case CHTYPE_NL: case CHTYPE_PRINT: re_fastputc(el, c); break; case CHTYPE_ASCIICTL: case CHTYPE_NONPRINT: { wchar_t visbuf[VISUAL_WIDTH_MAX]; ssize_t i, n = ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c); for (i = 0; n-- > 0; ++i) re_fastputc(el, visbuf[i]); break; } } terminal__flush(el); } /* re_clear_display(): * clear the screen buffers so that new new prompt starts fresh. */ libedit_private void re_clear_display(EditLine *el) { int i; el->el_cursor.v = 0; el->el_cursor.h = 0; for (i = 0; i < el->el_terminal.t_size.v; i++) el->el_display[i][0] = '\0'; el->el_refresh.r_oldcv = 0; } /* re_clear_lines(): * Make sure all lines are *really* blank */ libedit_private void re_clear_lines(EditLine *el) { if (EL_CAN_CEOL) { int i; for (i = el->el_refresh.r_oldcv; i >= 0; i--) { /* for each line on the screen */ terminal_move_to_line(el, i); terminal_move_to_char(el, 0); terminal_clear_EOL(el, el->el_terminal.t_size.h); } } else { terminal_move_to_line(el, el->el_refresh.r_oldcv); /* go to last line */ terminal__putc(el, '\r'); /* go to BOL */ terminal__putc(el, '\n'); /* go to new line */ } } diff --git a/contrib/libedit/search.c b/contrib/libedit/search.c index e909d1475725..585a73876841 100644 --- a/contrib/libedit/search.c +++ b/contrib/libedit/search.c @@ -1,641 +1,644 @@ -/* $NetBSD: search.c,v 1.51 2020/03/30 06:56:38 ryo Exp $ */ +/* $NetBSD: search.c,v 1.52 2024/06/30 16:26:30 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: search.c,v 1.51 2020/03/30 06:56:38 ryo Exp $"); +__RCSID("$NetBSD: search.c,v 1.52 2024/06/30 16:26:30 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * search.c: History and character search functions */ #include #include #if defined(REGEX) #include #elif defined(REGEXP) #include #endif #include "el.h" #include "common.h" #include "fcns.h" /* * Adjust cursor in vi mode to include the character under it */ #define EL_CURSOR(el) \ ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \ ((el)->el_map.current == (el)->el_map.alt))) /* search_init(): * Initialize the search stuff */ libedit_private int search_init(EditLine *el) { el->el_search.patbuf = el_calloc(EL_BUFSIZ, sizeof(*el->el_search.patbuf)); if (el->el_search.patbuf == NULL) return -1; el->el_search.patbuf[0] = L'\0'; el->el_search.patlen = 0; el->el_search.patdir = -1; el->el_search.chacha = L'\0'; el->el_search.chadir = CHAR_FWD; el->el_search.chatflg = 0; return 0; } /* search_end(): * Initialize the search stuff */ libedit_private void search_end(EditLine *el) { el_free(el->el_search.patbuf); el->el_search.patbuf = NULL; } #ifdef REGEXP /* regerror(): * Handle regular expression errors */ void /*ARGSUSED*/ regerror(const char *msg) { } #endif /* el_match(): * Return if string matches pattern */ libedit_private int el_match(const wchar_t *str, const wchar_t *pat) { static ct_buffer_t conv; #if defined (REGEX) regex_t re; int rv; #elif defined (REGEXP) regexp *rp; int rv; #else extern char *re_comp(const char *); extern int re_exec(const char *); #endif if (wcsstr(str, pat) != 0) return 1; #if defined(REGEX) if (regcomp(&re, ct_encode_string(pat, &conv), 0) == 0) { rv = regexec(&re, ct_encode_string(str, &conv), (size_t)0, NULL, 0) == 0; regfree(&re); } else { rv = 0; } return rv; #elif defined(REGEXP) if ((re = regcomp(ct_encode_string(pat, &conv))) != NULL) { rv = regexec(re, ct_encode_string(str, &conv)); el_free(re); } else { rv = 0; } return rv; #else if (re_comp(ct_encode_string(pat, &conv)) != NULL) return 0; else return re_exec(ct_encode_string(str, &conv)) == 1; #endif } /* c_hmatch(): * return True if the pattern matches the prefix */ libedit_private int c_hmatch(EditLine *el, const wchar_t *str) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "match `%ls' with `%ls'\n", el->el_search.patbuf, str); #endif /* SDEBUG */ return el_match(str, el->el_search.patbuf); } /* c_setpat(): * Set the history seatch pattern */ libedit_private void c_setpat(EditLine *el) { if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY && el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) { el->el_search.patlen = (size_t)(EL_CURSOR(el) - el->el_line.buffer); if (el->el_search.patlen >= EL_BUFSIZ) el->el_search.patlen = EL_BUFSIZ - 1; (void) wcsncpy(el->el_search.patbuf, el->el_line.buffer, el->el_search.patlen); el->el_search.patbuf[el->el_search.patlen] = '\0'; } #ifdef SDEBUG (void) fprintf(el->el_errfile, "\neventno = %d\n", el->el_history.eventno); (void) fprintf(el->el_errfile, "patlen = %ld\n", el->el_search.patlen); (void) fprintf(el->el_errfile, "patbuf = \"%ls\"\n", el->el_search.patbuf); (void) fprintf(el->el_errfile, "cursor %ld lastchar %ld\n", EL_CURSOR(el) - el->el_line.buffer, el->el_line.lastchar - el->el_line.buffer); #endif } /* ce_inc_search(): * Emacs incremental search */ libedit_private el_action_t ce_inc_search(EditLine *el, int dir) { static const wchar_t STRfwd[] = L"fwd", STRbck[] = L"bck"; static wchar_t pchar = L':'; /* ':' = normal, '?' = failed */ static wchar_t endcmd[2] = {'\0', '\0'}; wchar_t *ocursor = el->el_line.cursor, oldpchar = pchar, ch; const wchar_t *cp; el_action_t ret = CC_NORM; int ohisteventno = el->el_history.eventno; size_t oldpatlen = el->el_search.patlen; int newdir = dir; int done, redo; if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(*el->el_line.lastchar) + 2 + el->el_search.patlen >= el->el_line.limit) return CC_ERROR; for (;;) { if (el->el_search.patlen == 0) { /* first round */ pchar = ':'; #ifdef ANCHOR #define LEN 2 el->el_search.patbuf[el->el_search.patlen++] = '.'; el->el_search.patbuf[el->el_search.patlen++] = '*'; #else #define LEN 0 #endif } done = redo = 0; *el->el_line.lastchar++ = '\n'; for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd; *cp; *el->el_line.lastchar++ = *cp++) continue; *el->el_line.lastchar++ = pchar; for (cp = &el->el_search.patbuf[LEN]; cp < &el->el_search.patbuf[el->el_search.patlen]; *el->el_line.lastchar++ = *cp++) continue; *el->el_line.lastchar = '\0'; re_refresh(el); if (el_wgetc(el, &ch) != 1) return ed_end_of_file(el, 0); switch (el->el_map.current[(unsigned char) ch]) { case ED_INSERT: case ED_DIGIT: if (el->el_search.patlen >= EL_BUFSIZ - LEN) terminal_beep(el); else { el->el_search.patbuf[el->el_search.patlen++] = ch; *el->el_line.lastchar++ = ch; *el->el_line.lastchar = '\0'; re_refresh(el); } break; case EM_INC_SEARCH_NEXT: newdir = ED_SEARCH_NEXT_HISTORY; redo++; break; case EM_INC_SEARCH_PREV: newdir = ED_SEARCH_PREV_HISTORY; redo++; break; case EM_DELETE_PREV_CHAR: case ED_DELETE_PREV_CHAR: if (el->el_search.patlen > LEN) done++; else terminal_beep(el); break; default: switch (ch) { case 0007: /* ^G: Abort */ ret = CC_ERROR; done++; break; case 0027: /* ^W: Append word */ /* No can do if globbing characters in pattern */ for (cp = &el->el_search.patbuf[LEN];; cp++) if (cp >= &el->el_search.patbuf[ el->el_search.patlen]) { + if (el->el_line.cursor == + el->el_line.buffer) + break; el->el_line.cursor += el->el_search.patlen - LEN - 1; cp = c__next_word(el->el_line.cursor, el->el_line.lastchar, 1, ce__isword); while (el->el_line.cursor < cp && *el->el_line.cursor != '\n') { if (el->el_search.patlen >= EL_BUFSIZ - LEN) { terminal_beep(el); break; } el->el_search.patbuf[el->el_search.patlen++] = *el->el_line.cursor; *el->el_line.lastchar++ = *el->el_line.cursor++; } el->el_line.cursor = ocursor; *el->el_line.lastchar = '\0'; re_refresh(el); break; } else if (isglob(*cp)) { terminal_beep(el); break; } break; default: /* Terminate and execute cmd */ endcmd[0] = ch; el_wpush(el, endcmd); /* FALLTHROUGH */ case 0033: /* ESC: Terminate */ ret = CC_REFRESH; done++; break; } break; } while (el->el_line.lastchar > el->el_line.buffer && *el->el_line.lastchar != '\n') *el->el_line.lastchar-- = '\0'; *el->el_line.lastchar = '\0'; if (!done) { /* Can't search if unmatched '[' */ for (cp = &el->el_search.patbuf[el->el_search.patlen-1], ch = L']'; cp >= &el->el_search.patbuf[LEN]; cp--) if (*cp == '[' || *cp == ']') { ch = *cp; break; } if (el->el_search.patlen > LEN && ch != L'[') { if (redo && newdir == dir) { if (pchar == '?') { /* wrap around */ el->el_history.eventno = newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff; if (hist_get(el) == CC_ERROR) /* el->el_history.event * no was fixed by * first call */ (void) hist_get(el); el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ? el->el_line.lastchar : el->el_line.buffer; } else el->el_line.cursor += newdir == ED_SEARCH_PREV_HISTORY ? -1 : 1; } #ifdef ANCHOR el->el_search.patbuf[el->el_search.patlen++] = '.'; el->el_search.patbuf[el->el_search.patlen++] = '*'; #endif el->el_search.patbuf[el->el_search.patlen] = '\0'; if (el->el_line.cursor < el->el_line.buffer || el->el_line.cursor > el->el_line.lastchar || (ret = ce_search_line(el, newdir)) == CC_ERROR) { /* avoid c_setpat */ el->el_state.lastcmd = (el_action_t) newdir; ret = (el_action_t) (newdir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) : ed_search_next_history(el, 0)); if (ret != CC_ERROR) { el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ? el->el_line.lastchar : el->el_line.buffer; (void) ce_search_line(el, newdir); } } el->el_search.patlen -= LEN; el->el_search.patbuf[el->el_search.patlen] = '\0'; if (ret == CC_ERROR) { terminal_beep(el); if (el->el_history.eventno != ohisteventno) { el->el_history.eventno = ohisteventno; if (hist_get(el) == CC_ERROR) return CC_ERROR; } el->el_line.cursor = ocursor; pchar = '?'; } else { pchar = ':'; } } ret = ce_inc_search(el, newdir); if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') /* * break abort of failed search at last * non-failed */ ret = CC_NORM; } if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) { /* restore on normal return or error exit */ pchar = oldpchar; el->el_search.patlen = oldpatlen; if (el->el_history.eventno != ohisteventno) { el->el_history.eventno = ohisteventno; if (hist_get(el) == CC_ERROR) return CC_ERROR; } el->el_line.cursor = ocursor; if (ret == CC_ERROR) re_refresh(el); } if (done || ret != CC_NORM) return ret; } } /* cv_search(): * Vi search. */ libedit_private el_action_t cv_search(EditLine *el, int dir) { wchar_t ch; wchar_t tmpbuf[EL_BUFSIZ]; ssize_t tmplen; #ifdef ANCHOR tmpbuf[0] = '.'; tmpbuf[1] = '*'; #endif tmplen = LEN; el->el_search.patdir = dir; tmplen = c_gets(el, &tmpbuf[LEN], dir == ED_SEARCH_PREV_HISTORY ? L"\n/" : L"\n?" ); if (tmplen == -1) return CC_REFRESH; tmplen += LEN; ch = tmpbuf[tmplen]; tmpbuf[tmplen] = '\0'; if (tmplen == LEN) { /* * Use the old pattern, but wild-card it. */ if (el->el_search.patlen == 0) { re_refresh(el); return CC_ERROR; } #ifdef ANCHOR if (el->el_search.patbuf[0] != '.' && el->el_search.patbuf[0] != '*') { (void) wcsncpy(tmpbuf, el->el_search.patbuf, sizeof(tmpbuf) / sizeof(*tmpbuf) - 1); el->el_search.patbuf[0] = '.'; el->el_search.patbuf[1] = '*'; (void) wcsncpy(&el->el_search.patbuf[2], tmpbuf, EL_BUFSIZ - 3); el->el_search.patlen++; el->el_search.patbuf[el->el_search.patlen++] = '.'; el->el_search.patbuf[el->el_search.patlen++] = '*'; el->el_search.patbuf[el->el_search.patlen] = '\0'; } #endif } else { #ifdef ANCHOR tmpbuf[tmplen++] = '.'; tmpbuf[tmplen++] = '*'; #endif tmpbuf[tmplen] = '\0'; (void) wcsncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1); el->el_search.patlen = (size_t)tmplen; } el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */ el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer; if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) : ed_search_next_history(el, 0)) == CC_ERROR) { re_refresh(el); return CC_ERROR; } if (ch == 0033) { re_refresh(el); return ed_newline(el, 0); } return CC_REFRESH; } /* ce_search_line(): * Look for a pattern inside a line */ libedit_private el_action_t ce_search_line(EditLine *el, int dir) { wchar_t *cp = el->el_line.cursor; wchar_t *pattern = el->el_search.patbuf; wchar_t oc, *ocp; #ifdef ANCHOR ocp = &pattern[1]; oc = *ocp; *ocp = '^'; #else ocp = pattern; oc = *ocp; #endif if (dir == ED_SEARCH_PREV_HISTORY) { for (; cp >= el->el_line.buffer; cp--) { if (el_match(cp, ocp)) { *ocp = oc; el->el_line.cursor = cp; return CC_NORM; } } *ocp = oc; return CC_ERROR; } else { for (; *cp != '\0' && cp < el->el_line.limit; cp++) { if (el_match(cp, ocp)) { *ocp = oc; el->el_line.cursor = cp; return CC_NORM; } } *ocp = oc; return CC_ERROR; } } /* cv_repeat_srch(): * Vi repeat search */ libedit_private el_action_t cv_repeat_srch(EditLine *el, wint_t c) { #ifdef SDEBUG static ct_buffer_t conv; (void) fprintf(el->el_errfile, "dir %d patlen %ld patbuf %s\n", c, el->el_search.patlen, ct_encode_string(el->el_search.patbuf, &conv)); #endif el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */ el->el_line.lastchar = el->el_line.buffer; switch (c) { case ED_SEARCH_NEXT_HISTORY: return ed_search_next_history(el, 0); case ED_SEARCH_PREV_HISTORY: return ed_search_prev_history(el, 0); default: return CC_ERROR; } } /* cv_csearch(): * Vi character search */ libedit_private el_action_t cv_csearch(EditLine *el, int direction, wint_t ch, int count, int tflag) { wchar_t *cp; if (ch == 0) return CC_ERROR; if (ch == (wint_t)-1) { wchar_t c; if (el_wgetc(el, &c) != 1) return ed_end_of_file(el, 0); ch = c; } /* Save for ';' and ',' commands */ el->el_search.chacha = ch; el->el_search.chadir = direction; el->el_search.chatflg = (char)tflag; cp = el->el_line.cursor; while (count--) { if ((wint_t)*cp == ch) cp += direction; for (;;cp += direction) { if (cp >= el->el_line.lastchar) return CC_ERROR; if (cp < el->el_line.buffer) return CC_ERROR; if ((wint_t)*cp == ch) break; } } if (tflag) cp -= direction; el->el_line.cursor = cp; if (el->el_chared.c_vcmd.action != NOP) { if (direction > 0) el->el_line.cursor++; cv_delfini(el); return CC_REFRESH; } return CC_CURSOR; } diff --git a/contrib/libedit/sig.c b/contrib/libedit/sig.c index 7e7486980253..d2b77e77af1a 100644 --- a/contrib/libedit/sig.c +++ b/contrib/libedit/sig.c @@ -1,203 +1,203 @@ -/* $NetBSD: sig.c,v 1.27 2023/02/03 19:47:38 christos Exp $ */ +/* $NetBSD: sig.c,v 1.28 2024/12/18 15:38:52 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)sig.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: sig.c,v 1.27 2023/02/03 19:47:38 christos Exp $"); +__RCSID("$NetBSD: sig.c,v 1.28 2024/12/18 15:38:52 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * sig.c: Signal handling stuff. * our policy is to trap all signals, set a good state * and pass the ball to our caller. */ #include #include #include "el.h" #include "common.h" static EditLine *sel = NULL; static const int sighdl[] = { #define _DO(a) (a), ALLSIGS #undef _DO - 1 }; static void sig_handler(int); /* sig_handler(): * This is the handler called for all signals * XXX: we cannot pass any data so we just store the old editline * state in a private variable */ static void sig_handler(int signo) { int i, save_errno; sigset_t nset, oset; save_errno = errno; (void) sigemptyset(&nset); (void) sigaddset(&nset, signo); (void) sigprocmask(SIG_BLOCK, &nset, &oset); sel->el_signal->sig_no = signo; switch (signo) { case SIGCONT: tty_rawmode(sel); if (ed_redisplay(sel, 0) == CC_REFRESH) re_refresh(sel); terminal__flush(sel); break; case SIGWINCH: el_resize(sel); break; default: tty_cookedmode(sel); break; } for (i = 0; sighdl[i] != -1; i++) if (signo == sighdl[i]) break; (void) sigaction(signo, &sel->el_signal->sig_action[i], NULL); sel->el_signal->sig_action[i].sa_handler = SIG_ERR; sel->el_signal->sig_action[i].sa_flags = 0; sigemptyset(&sel->el_signal->sig_action[i].sa_mask); (void) sigprocmask(SIG_SETMASK, &oset, NULL); (void) kill(0, signo); errno = save_errno; } /* sig_init(): * Initialize all signal stuff */ libedit_private int sig_init(EditLine *el) { size_t i; sigset_t *nset, oset; el->el_signal = el_malloc(sizeof(*el->el_signal)); if (el->el_signal == NULL) return -1; nset = &el->el_signal->sig_set; (void) sigemptyset(nset); #define _DO(a) (void) sigaddset(nset, a); ALLSIGS #undef _DO (void) sigprocmask(SIG_BLOCK, nset, &oset); for (i = 0; sighdl[i] != -1; i++) { el->el_signal->sig_action[i].sa_handler = SIG_ERR; el->el_signal->sig_action[i].sa_flags = 0; sigemptyset(&el->el_signal->sig_action[i].sa_mask); } (void) sigprocmask(SIG_SETMASK, &oset, NULL); return 0; } /* sig_end(): * Clear all signal stuff */ libedit_private void sig_end(EditLine *el) { el_free(el->el_signal); el->el_signal = NULL; } /* sig_set(): * set all the signal handlers */ libedit_private void sig_set(EditLine *el) { size_t i; sigset_t oset; struct sigaction osa, nsa; nsa.sa_handler = sig_handler; - nsa.sa_flags = 0; + nsa.sa_flags = SA_ONSTACK; sigemptyset(&nsa.sa_mask); sel = el; (void) sigprocmask(SIG_BLOCK, &el->el_signal->sig_set, &oset); for (i = 0; sighdl[i] != -1; i++) { /* This could happen if we get interrupted */ if (sigaction(sighdl[i], &nsa, &osa) != -1 && osa.sa_handler != sig_handler) el->el_signal->sig_action[i] = osa; } (void) sigprocmask(SIG_SETMASK, &oset, NULL); } /* sig_clr(): * clear all the signal handlers */ libedit_private void sig_clr(EditLine *el) { size_t i; sigset_t oset; (void) sigprocmask(SIG_BLOCK, &el->el_signal->sig_set, &oset); for (i = 0; sighdl[i] != -1; i++) if (el->el_signal->sig_action[i].sa_handler != SIG_ERR) (void)sigaction(sighdl[i], &el->el_signal->sig_action[i], NULL); (void)sigprocmask(SIG_SETMASK, &oset, NULL); } diff --git a/contrib/libedit/sys.h b/contrib/libedit/sys.h index 5395531a298f..67bfcb535939 100644 --- a/contrib/libedit/sys.h +++ b/contrib/libedit/sys.h @@ -1,75 +1,75 @@ -/* $NetBSD: sys.h,v 1.28 2023/02/04 14:34:28 christos Exp $ */ +/* $NetBSD: sys.h,v 1.29 2023/04/25 17:51:32 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)sys.h 8.1 (Berkeley) 6/4/93 */ /* * sys.h: Put all the stupid compiler and system dependencies here... */ #ifndef _h_sys #define _h_sys #ifdef HAVE_SYS_CDEFS_H #include #endif -#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8) +#if !defined(__attribute__) && !defined(__lint__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8) # define __attribute__(A) #endif #ifndef __BEGIN_DECLS # ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } # else # define __BEGIN_DECLS # define __END_DECLS # endif #endif /* If your compiler does not support this, define it to be empty. */ #define libedit_private __attribute__((__visibility__("hidden"))) #ifndef __arraycount # define __arraycount(a) (sizeof(a) / sizeof(*(a))) #endif #ifndef __RCSID #define __RCSID(x) #endif #define REGEX /* Use POSIX.2 regular expression functions */ #undef REGEXP /* Use UNIX V8 regular expression functions */ #endif /* _h_sys */ diff --git a/contrib/libedit/tokenizer.c b/contrib/libedit/tokenizer.c index 18532240dbb9..fe98fab6b68a 100644 --- a/contrib/libedit/tokenizer.c +++ b/contrib/libedit/tokenizer.c @@ -1,466 +1,466 @@ -/* $NetBSD: tokenizer.c,v 1.28 2016/04/11 18:56:31 christos Exp $ */ +/* $NetBSD: tokenizer.c,v 1.29 2023/05/30 11:53:40 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)tokenizer.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: tokenizer.c,v 1.28 2016/04/11 18:56:31 christos Exp $"); +__RCSID("$NetBSD: tokenizer.c,v 1.29 2023/05/30 11:53:40 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* We build this file twice, once as NARROW, once as WIDE. */ /* * tokenize.c: Bourne shell like tokenizer */ #include #include #include "histedit.h" typedef enum { Q_none, Q_single, Q_double, Q_one, Q_doubleone } quote_t; #define TOK_KEEP 1 #define TOK_EAT 2 #define WINCR 20 #define AINCR 10 #define IFS STR("\t \n") #define tok_malloc(a) malloc(a) #define tok_free(a) free(a) #define tok_realloc(a, b) realloc(a, b) #ifdef NARROWCHAR #define Char char #define FUN(prefix, rest) prefix ## _ ## rest #define TYPE(type) type #define STR(x) x #define Strchr(s, c) strchr(s, c) #define tok_strdup(s) strdup(s) #else #define Char wchar_t #define FUN(prefix, rest) prefix ## _w ## rest #define TYPE(type) type ## W #define STR(x) L ## x #define Strchr(s, c) wcschr(s, c) #define tok_strdup(s) wcsdup(s) #endif struct TYPE(tokenizer) { Char *ifs; /* In field separator */ size_t argc, amax; /* Current and maximum number of args */ - Char **argv; /* Argument list */ + const Char **argv; /* Argument list */ Char *wptr, *wmax; /* Space and limit on the word buffer */ Char *wstart; /* Beginning of next word */ Char *wspace; /* Space of word buffer */ quote_t quote; /* Quoting state */ int flags; /* flags; */ }; static void FUN(tok,finish)(TYPE(Tokenizer) *); /* FUN(tok,finish)(): * Finish a word in the tokenizer. */ static void FUN(tok,finish)(TYPE(Tokenizer) *tok) { *tok->wptr = '\0'; if ((tok->flags & TOK_KEEP) || tok->wptr != tok->wstart) { tok->argv[tok->argc++] = tok->wstart; tok->argv[tok->argc] = NULL; tok->wstart = ++tok->wptr; } tok->flags &= ~TOK_KEEP; } /* FUN(tok,init)(): * Initialize the tokenizer */ TYPE(Tokenizer) * FUN(tok,init)(const Char *ifs) { TYPE(Tokenizer) *tok = tok_malloc(sizeof(*tok)); if (tok == NULL) return NULL; tok->ifs = tok_strdup(ifs ? ifs : IFS); if (tok->ifs == NULL) { tok_free(tok); return NULL; } tok->argc = 0; tok->amax = AINCR; tok->argv = tok_malloc(sizeof(*tok->argv) * tok->amax); if (tok->argv == NULL) { tok_free(tok->ifs); tok_free(tok); return NULL; } tok->argv[0] = NULL; tok->wspace = tok_malloc(WINCR * sizeof(*tok->wspace)); if (tok->wspace == NULL) { tok_free(tok->argv); tok_free(tok->ifs); tok_free(tok); return NULL; } tok->wmax = tok->wspace + WINCR; tok->wstart = tok->wspace; tok->wptr = tok->wspace; tok->flags = 0; tok->quote = Q_none; return tok; } /* FUN(tok,reset)(): * Reset the tokenizer */ void FUN(tok,reset)(TYPE(Tokenizer) *tok) { tok->argc = 0; tok->wstart = tok->wspace; tok->wptr = tok->wspace; tok->flags = 0; tok->quote = Q_none; } /* FUN(tok,end)(): * Clean up */ void FUN(tok,end)(TYPE(Tokenizer) *tok) { tok_free(tok->ifs); tok_free(tok->wspace); tok_free(tok->argv); tok_free(tok); } /* FUN(tok,line)(): * Bourne shell (sh(1)) like tokenizing * Arguments: * tok current tokenizer state (setup with FUN(tok,init)()) * line line to parse * Returns: * -1 Internal error * 3 Quoted return * 2 Unmatched double quote * 1 Unmatched single quote * 0 Ok * Modifies (if return value is 0): * argc number of arguments * argv argument array * cursorc if !NULL, argv element containing cursor * cursorv if !NULL, offset in argv[cursorc] of cursor */ int FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line, int *argc, const Char ***argv, int *cursorc, int *cursoro) { const Char *ptr; int cc, co; cc = co = -1; ptr = line->buffer; for (ptr = line->buffer; ;ptr++) { if (ptr >= line->lastchar) ptr = STR(""); if (ptr == line->cursor) { cc = (int)tok->argc; co = (int)(tok->wptr - tok->wstart); } switch (*ptr) { case '\'': tok->flags |= TOK_KEEP; tok->flags &= ~TOK_EAT; switch (tok->quote) { case Q_none: tok->quote = Q_single; /* Enter single quote * mode */ break; case Q_single: /* Exit single quote mode */ tok->quote = Q_none; break; case Q_one: /* Quote this ' */ tok->quote = Q_none; *tok->wptr++ = *ptr; break; case Q_double: /* Stay in double quote mode */ *tok->wptr++ = *ptr; break; case Q_doubleone: /* Quote this ' */ tok->quote = Q_double; *tok->wptr++ = *ptr; break; default: return -1; } break; case '"': tok->flags &= ~TOK_EAT; tok->flags |= TOK_KEEP; switch (tok->quote) { case Q_none: /* Enter double quote mode */ tok->quote = Q_double; break; case Q_double: /* Exit double quote mode */ tok->quote = Q_none; break; case Q_one: /* Quote this " */ tok->quote = Q_none; *tok->wptr++ = *ptr; break; case Q_single: /* Stay in single quote mode */ *tok->wptr++ = *ptr; break; case Q_doubleone: /* Quote this " */ tok->quote = Q_double; *tok->wptr++ = *ptr; break; default: return -1; } break; case '\\': tok->flags |= TOK_KEEP; tok->flags &= ~TOK_EAT; switch (tok->quote) { case Q_none: /* Quote next character */ tok->quote = Q_one; break; case Q_double: /* Quote next character */ tok->quote = Q_doubleone; break; case Q_one: /* Quote this, restore state */ *tok->wptr++ = *ptr; tok->quote = Q_none; break; case Q_single: /* Stay in single quote mode */ *tok->wptr++ = *ptr; break; case Q_doubleone: /* Quote this \ */ tok->quote = Q_double; *tok->wptr++ = *ptr; break; default: return -1; } break; case '\n': tok->flags &= ~TOK_EAT; switch (tok->quote) { case Q_none: goto tok_line_outok; case Q_single: case Q_double: *tok->wptr++ = *ptr; /* Add the return */ break; case Q_doubleone: /* Back to double, eat the '\n' */ tok->flags |= TOK_EAT; tok->quote = Q_double; break; case Q_one: /* No quote, more eat the '\n' */ tok->flags |= TOK_EAT; tok->quote = Q_none; break; default: return 0; } break; case '\0': switch (tok->quote) { case Q_none: /* Finish word and return */ if (tok->flags & TOK_EAT) { tok->flags &= ~TOK_EAT; return 3; } goto tok_line_outok; case Q_single: return 1; case Q_double: return 2; case Q_doubleone: tok->quote = Q_double; *tok->wptr++ = *ptr; break; case Q_one: tok->quote = Q_none; *tok->wptr++ = *ptr; break; default: return -1; } break; default: tok->flags &= ~TOK_EAT; switch (tok->quote) { case Q_none: if (Strchr(tok->ifs, *ptr) != NULL) FUN(tok,finish)(tok); else *tok->wptr++ = *ptr; break; case Q_single: case Q_double: *tok->wptr++ = *ptr; break; case Q_doubleone: *tok->wptr++ = '\\'; tok->quote = Q_double; *tok->wptr++ = *ptr; break; case Q_one: tok->quote = Q_none; *tok->wptr++ = *ptr; break; default: return -1; } break; } if (tok->wptr >= tok->wmax - 4) { size_t size = (size_t)(tok->wmax - tok->wspace + WINCR); Char *s = tok_realloc(tok->wspace, size * sizeof(*s)); if (s == NULL) return -1; if (s != tok->wspace) { size_t i; for (i = 0; i < tok->argc; i++) { tok->argv[i] = (tok->argv[i] - tok->wspace) + s; } tok->wptr = (tok->wptr - tok->wspace) + s; tok->wstart = (tok->wstart - tok->wspace) + s; tok->wspace = s; } tok->wmax = s + size; } if (tok->argc >= tok->amax - 4) { - Char **p; + const Char **p; tok->amax += AINCR; p = tok_realloc(tok->argv, tok->amax * sizeof(*p)); if (p == NULL) { tok->amax -= AINCR; return -1; } tok->argv = p; } } tok_line_outok: if (cc == -1 && co == -1) { cc = (int)tok->argc; co = (int)(tok->wptr - tok->wstart); } if (cursorc != NULL) *cursorc = cc; if (cursoro != NULL) *cursoro = co; FUN(tok,finish)(tok); - *argv = (const Char **)tok->argv; + *argv = tok->argv; *argc = (int)tok->argc; return 0; } /* FUN(tok,str)(): * Simpler version of tok_line, taking a NUL terminated line * and splitting into words, ignoring cursor state. */ int FUN(tok,str)(TYPE(Tokenizer) *tok, const Char *line, int *argc, const Char ***argv) { TYPE(LineInfo) li; memset(&li, 0, sizeof(li)); li.buffer = line; li.cursor = li.lastchar = Strchr(line, '\0'); return FUN(tok,line)(tok, &li, argc, argv, NULL, NULL); }