diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-03-03 20:54:47 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-03-03 20:54:47 +0300 |
| commit | d137cc06981fac53b2dbc408d65bae10384535af (patch) | |
| tree | 8e825da49192c3239f9ed72e03b3d5a8a606c641 | |
| parent | 807f52653f51895129d7db9d5dc50d2334044165 (diff) | |
| download | Project-Tick-d137cc06981fac53b2dbc408d65bae10384535af.tar.gz Project-Tick-d137cc06981fac53b2dbc408d65bae10384535af.zip | |
sh: enable in-tree history and editline on Linux
Build bin/sh with interactive history support instead of NO_HISTORY.
- vendor libedit and vis sources into the standalone GNUmakefile build
- re-enable fc/history tests and remove no-history negative cases
- replace BSD-only libedit assumptions for Linux/musl
- local issetugid mapping
- local sys/cdefs fallback
- Linux wchar_t and dirent fixes
- vis/libedit portability fixes
- add a local ANSI termcap backend so interactive editing does not depend
on host ncurses/termcap ABIs
- keep unsupported verify semantics as explicit runtime errors
- update README to document the Linux-native history/editline strategy
This keeps the shell self-contained, reproducible, and runnable on both
glibc and musl while preserving FreeBSD sh history semantics where Linux
has a real equivalent.
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
| -rw-r--r-- | GNUmakefile | 72 | ||||
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | cdefs.h | 10 | ||||
| -rw-r--r-- | compat.c | 20 | ||||
| -rw-r--r-- | compat.h | 2 | ||||
| -rw-r--r-- | histedit.c | 20 | ||||
| -rw-r--r-- | namespace.h | 3 | ||||
| -rw-r--r-- | sys/cdefs.h | 10 | ||||
| -rw-r--r-- | termcap.c | 311 | ||||
| -rw-r--r-- | termcap.h | 11 | ||||
| -rw-r--r-- | tests/linux/bind-no-history.2 | 1 | ||||
| -rw-r--r-- | tests/linux/bind-no-history.2.stderr | 1 | ||||
| -rw-r--r-- | tests/linux/fc-no-history.2 | 1 | ||||
| -rw-r--r-- | tests/linux/fc-no-history.2.stderr | 1 | ||||
| -rw-r--r-- | tests/test.sh | 3 |
15 files changed, 452 insertions, 22 deletions
diff --git a/GNUmakefile b/GNUmakefile index 14814b9624..871b18c5ef 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,13 +6,15 @@ export LANG := C CC ?= cc AWK ?= awk -CPPFLAGS += -D_GNU_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 -DNO_HISTORY \ +CPPFLAGS += -D_GNU_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 \ -I"$(CURDIR)" -I"$(CURDIR)/build/gen" -I"$(CURDIR)/../kill" \ -I"$(CURDIR)/../../usr.bin/printf" -I"$(CURDIR)/../../bin/test" CFLAGS ?= -O2 CFLAGS += -g -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare LDFLAGS ?= LDLIBS ?= +EDITLINE_LIBS ?= +EDITLINE_CPPFLAGS ?= OBJDIR := $(CURDIR)/build/obj TOOLDIR := $(CURDIR)/build/tools @@ -20,11 +22,17 @@ GENDIR := $(CURDIR)/build/gen OUTDIR := $(CURDIR)/out TARGET := $(OUTDIR)/sh TEST_RUNNER := $(TOOLDIR)/run-default-sigpipe +LIBEDITDIR := $(CURDIR)/../../contrib/libedit +LIBEDIT_GENDIR := $(GENDIR)/libedit +VISDIR := $(CURDIR)/../../contrib/libc-vis KILLDIR := $(CURDIR)/../kill TESTDIR := $(CURDIR)/../../bin/test PRINTFDIR := $(CURDIR)/../../usr.bin/printf +CPPFLAGS += -I"$(LIBEDIT_GENDIR)" -I"$(LIBEDITDIR)" +LIBEDIT_CPPFLAGS := $(CPPFLAGS) $(EDITLINE_CPPFLAGS) -include "$(CURDIR)/compat.h" -I"$(VISDIR)" + LOCAL_SRCS := \ alias.c \ arith_yacc.c \ @@ -50,14 +58,46 @@ LOCAL_SRCS := \ redir.c \ show.c \ signames.c \ + termcap.c \ trap.c \ var.c BUILTIN_SRCS := bltin/echo.c EXTERNAL_SRCS := $(KILLDIR)/kill.c $(TESTDIR)/test.c $(PRINTFDIR)/printf.c GEN_SRCS := $(GENDIR)/builtins.c $(GENDIR)/nodes.c $(GENDIR)/syntax.c GEN_HDRS := $(GENDIR)/builtins.h $(GENDIR)/nodes.h $(GENDIR)/syntax.h $(GENDIR)/token.h +LIBEDIT_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 \ + refresh.c \ + search.c \ + sig.c \ + terminal.c \ + tokenizer.c \ + tokenizern.c \ + tty.c \ + vi.c +LIBEDIT_GEN_HDRS := $(LIBEDIT_GENDIR)/vi.h $(LIBEDIT_GENDIR)/emacs.h \ + $(LIBEDIT_GENDIR)/common.h $(LIBEDIT_GENDIR)/fcns.h \ + $(LIBEDIT_GENDIR)/func.h $(LIBEDIT_GENDIR)/help.h +VIS_SRCS := vis.c unvis.c SRCS := $(LOCAL_SRCS) $(BUILTIN_SRCS) $(EXTERNAL_SRCS) $(GEN_SRCS) OBJS := $(addprefix $(OBJDIR)/,$(notdir $(SRCS:.c=.o))) +LIBEDIT_OBJS := $(addprefix $(OBJDIR)/libedit-,$(LIBEDIT_SRCS:.c=.o)) +VIS_OBJS := $(addprefix $(OBJDIR)/vis-,$(VIS_SRCS:.c=.o)) vpath %.c $(CURDIR) $(CURDIR)/bltin $(KILLDIR) $(TESTDIR) $(PRINTFDIR) $(GENDIR) @@ -66,10 +106,10 @@ vpath %.c $(CURDIR) $(CURDIR)/bltin $(KILLDIR) $(TESTDIR) $(PRINTFDIR) $(GENDIR) all: $(TARGET) dirs: - @mkdir -p "$(OBJDIR)" "$(TOOLDIR)" "$(GENDIR)" "$(OUTDIR)" + @mkdir -p "$(OBJDIR)" "$(TOOLDIR)" "$(GENDIR)" "$(OUTDIR)" "$(LIBEDIT_GENDIR)" -$(TARGET): $(OBJS) | dirs - $(CC) $(LDFLAGS) -o "$@" $(OBJS) $(LDLIBS) +$(TARGET): $(OBJS) $(LIBEDIT_OBJS) $(VIS_OBJS) | dirs + $(CC) $(LDFLAGS) -o "$@" $(OBJS) $(LIBEDIT_OBJS) $(VIS_OBJS) $(LDLIBS) $(EDITLINE_LIBS) $(OBJDIR)/%.o: %.c $(GEN_HDRS) | dirs $(CC) $(CPPFLAGS) $(CFLAGS) -DSHELL -c "$<" -o "$@" @@ -83,6 +123,12 @@ $(OBJDIR)/mode.o: $(CURDIR)/mode.c $(CURDIR)/mode.h | dirs $(OBJDIR)/signames.o: $(CURDIR)/signames.c $(CURDIR)/signames.h | dirs $(CC) $(CPPFLAGS) $(CFLAGS) -c "$<" -o "$@" +$(OBJDIR)/libedit-%.o: $(LIBEDITDIR)/%.c $(LIBEDIT_GEN_HDRS) $(LIBEDITDIR)/histedit.h $(LIBEDITDIR)/filecomplete.h | dirs + $(CC) $(LIBEDIT_CPPFLAGS) $(CFLAGS) -c "$<" -o "$@" + +$(OBJDIR)/vis-%.o: $(VISDIR)/%.c $(CURDIR)/namespace.h $(VISDIR)/vis.h | dirs + $(CC) $(LIBEDIT_CPPFLAGS) $(CFLAGS) -c "$<" -o "$@" + $(TOOLDIR)/mknodes: $(CURDIR)/mknodes.c | dirs $(CC) $(CPPFLAGS) $(CFLAGS) "$<" -o "$@" @@ -104,6 +150,24 @@ $(GENDIR)/syntax.c $(GENDIR)/syntax.h: $(TOOLDIR)/mksyntax $(CURDIR)/parser.h | $(GENDIR)/token.h: $(CURDIR)/mktokens | dirs cd "$(GENDIR)" && sh "$(CURDIR)/mktokens" +$(LIBEDIT_GENDIR)/vi.h: $(LIBEDITDIR)/vi.c $(LIBEDITDIR)/makelist | dirs + cd "$(LIBEDIT_GENDIR)" && sh "$(LIBEDITDIR)/makelist" -h "$(LIBEDITDIR)/vi.c" > vi.h + +$(LIBEDIT_GENDIR)/emacs.h: $(LIBEDITDIR)/emacs.c $(LIBEDITDIR)/makelist | dirs + cd "$(LIBEDIT_GENDIR)" && sh "$(LIBEDITDIR)/makelist" -h "$(LIBEDITDIR)/emacs.c" > emacs.h + +$(LIBEDIT_GENDIR)/common.h: $(LIBEDITDIR)/common.c $(LIBEDITDIR)/makelist | dirs + cd "$(LIBEDIT_GENDIR)" && sh "$(LIBEDITDIR)/makelist" -h "$(LIBEDITDIR)/common.c" > common.h + +$(LIBEDIT_GENDIR)/fcns.h: $(LIBEDIT_GENDIR)/vi.h $(LIBEDIT_GENDIR)/emacs.h $(LIBEDIT_GENDIR)/common.h $(LIBEDITDIR)/makelist | dirs + cd "$(LIBEDIT_GENDIR)" && sh "$(LIBEDITDIR)/makelist" -fh vi.h emacs.h common.h > fcns.h + +$(LIBEDIT_GENDIR)/func.h: $(LIBEDIT_GENDIR)/vi.h $(LIBEDIT_GENDIR)/emacs.h $(LIBEDIT_GENDIR)/common.h $(LIBEDITDIR)/makelist | dirs + cd "$(LIBEDIT_GENDIR)" && sh "$(LIBEDITDIR)/makelist" -fc vi.h emacs.h common.h > func.h + +$(LIBEDIT_GENDIR)/help.h: $(LIBEDITDIR)/vi.c $(LIBEDITDIR)/emacs.c $(LIBEDITDIR)/common.c $(LIBEDITDIR)/makelist | dirs + cd "$(LIBEDIT_GENDIR)" && sh "$(LIBEDITDIR)/makelist" -bh "$(LIBEDITDIR)/vi.c" "$(LIBEDITDIR)/emacs.c" "$(LIBEDITDIR)/common.c" > help.h + test: $(TARGET) $(TEST_RUNNER) SH_BIN="$(TARGET)" SH_RUNNER="$(TEST_RUNNER)" sh "$(CURDIR)/tests/test.sh" @@ -1,6 +1,6 @@ # sh -Standalone Linux-native port of FreeBSD `sh` for Project Tick BSD/Linux Distribution. +Standalone Linux-native port of FreeBSD `sh` for Project Tick BSD/Linux Distribution, with in-tree `libedit`, `vis`, and a local ANSI/termcap backend for history and line editing. ## Build @@ -26,6 +26,8 @@ gmake -f GNUmakefile test CC=musl-gcc - The FreeBSD `bsd.prog.mk` build is replaced with a standalone `GNUmakefile` that regenerates `builtins.c`, `builtins.h`, `nodes.c`, `nodes.h`, `syntax.c`, `syntax.h`, and `token.h`. - No shared BSD compatibility shim is introduced. FreeBSD-only assumptions are either rewritten directly for Linux or rejected with explicit runtime errors. +- History and editline support are built from the repository's `contrib/libedit` sources rather than depending on a host `libedit` package. +- Terminal capability handling is provided by a local Linux-oriented ANSI/termcap implementation, avoiding host `ncurses`/`tinfo` ABI dependencies and keeping musl builds runnable. - `wait3(2)` is replaced with Linux `wait4(-1, ...)` for job accounting and child collection. - `CLOCK_UPTIME`-based `read -t` timing is mapped to `CLOCK_MONOTONIC`, preserving monotonic timeout behavior on Linux. - BSD `sys_signame` / `sys_nsig` usage is replaced with a local Linux signal table that covers standard signals plus `SIGRTMIN`/`SIGRTMAX`. @@ -37,6 +39,8 @@ gmake -f GNUmakefile test CC=musl-gcc ## Supported / Unsupported Linux Semantics - Supported: POSIX shell scripts, pipelines, expansions, traps, job control, `wait`, `kill %job`, `command -p`, symbolic `umask`, and interactive prompting. +- Supported: interactive history, `fc`, and editline bindings through vendored `libedit`. +- Supported with Linux-native mapping: interactive editing targets ANSI/xterm-style terminals through the local termcap backend instead of relying on external `ncurses`/`termcap` libraries. - Supported with Linux-native mapping: `read -t` uses monotonic time, job waiting uses `wait4(2)`, and shell self-reexec falls back through `/proc/self/exe`. - Unsupported: `set -o verify` / `set +o verify` enabling is rejected with an explicit error because Linux has no `O_VERIFY` equivalent. -- Unsupported in this standalone build: line editing and history support are disabled (`NO_HISTORY`). `fc` and `bind` remain present but fail with explicit runtime errors instead of pretending to work. +- Unsupported: non-ANSI terminal databases are not consulted. Exotic `TERM` entries outside the built-in ANSI capability set may lose advanced editing behavior even though non-interactive shell semantics remain supported. @@ -5,6 +5,16 @@ #define __dead2 __attribute__((__noreturn__)) #endif +#ifndef __BEGIN_DECLS +#ifdef __cplusplus +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif +#endif + #ifndef __printflike #define __printflike(fmtarg, firstvararg) \ __attribute__((__format__(__printf__, fmtarg, firstvararg))) @@ -1,7 +1,27 @@ +#include <errno.h> #include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#if defined(__linux__) +#include <sys/auxv.h> +#endif #include "compat.h" +int +sh_issetugid(void) +{ +#if defined(__linux__) && defined(AT_SECURE) + errno = 0; + if (getauxval(AT_SECURE) != 0) + return (1); + if (errno == 0) + return (0); +#endif + return (getuid() != geteuid() || getgid() != getegid()); +} + size_t sh_strlcpy(char *dst, const char *src, size_t dstsize) { @@ -56,8 +56,10 @@ ((a)->tv_sec cmp (b)->tv_sec)) #endif +int sh_issetugid(void); size_t sh_strlcpy(char *dst, const char *src, size_t dstsize); +#define issetugid sh_issetugid #define strlcpy sh_strlcpy #endif diff --git a/histedit.c b/histedit.c index d41f4b2e02..900e488208 100644 --- a/histedit.c +++ b/histedit.c @@ -71,11 +71,12 @@ int displayhist; static int savehist; static FILE *el_in, *el_out; static bool in_command_completion; +static int completion_compare_curpos; static char *fc_replace(const char *, char *, char *); static int not_fcnumber(const char *); static int str_to_event(const char *, int); -static int comparator(const void *, const void *, void *); +static int comparator(const void *, const void *); static char **sh_matches(const char *, int, int); static const char *append_char_function(const char *); static unsigned char sh_complete(EditLine *, int); @@ -582,9 +583,9 @@ bindcmd(int argc, char **argv) * characters that we already know to compare equal (common prefix). */ static int -comparator(const void *a, const void *b, void *thunk) +comparator(const void *a, const void *b) { - size_t curpos = (intptr_t)thunk; + size_t curpos = (size_t)completion_compare_curpos; return (strcmp(*(char *const *)a + curpos, *(char *const *)b + curpos)); @@ -665,7 +666,8 @@ static char for (const unsigned char *bp = builtincmd; *bp != 0; bp += 2 + bp[0]) { if (curpos > bp[0] || memcmp(bp + 2, text, curpos) != 0) continue; - rmatches = add_match(matches, ++i, &size, strndup(bp + 2, bp[0])); + rmatches = add_match(matches, ++i, &size, + strndup((const char *)bp + 2, bp[0])); if (rmatches == NULL) goto out; matches = rmatches; @@ -694,11 +696,11 @@ out: free(matches); return (NULL); } - uniq = 1; - if (i > 1) { - qsort_s(matches + 1, i, sizeof(matches[0]), comparator, - (void *)(intptr_t)curpos); - for (size_t k = 2; k <= i; k++) { + uniq = 1; + if (i > 1) { + completion_compare_curpos = (int)curpos; + qsort(matches + 1, i, sizeof(matches[0]), comparator); + for (size_t k = 2; k <= i; k++) { const char *l = matches[uniq] + curpos; const char *r = matches[k] + curpos; size_t common = 0; diff --git a/namespace.h b/namespace.h new file mode 100644 index 0000000000..0ed042f265 --- /dev/null +++ b/namespace.h @@ -0,0 +1,3 @@ +#ifndef SH_NAMESPACE_H +#define SH_NAMESPACE_H +#endif diff --git a/sys/cdefs.h b/sys/cdefs.h new file mode 100644 index 0000000000..8671995871 --- /dev/null +++ b/sys/cdefs.h @@ -0,0 +1,10 @@ +#ifndef SH_SYS_CDEFS_H +#define SH_SYS_CDEFS_H + +#if defined(__GLIBC__) +#include_next <sys/cdefs.h> +#else +#include "../cdefs.h" +#endif + +#endif diff --git a/termcap.c b/termcap.c new file mode 100644 index 0000000000..ae2f0c8ca8 --- /dev/null +++ b/termcap.c @@ -0,0 +1,311 @@ +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include "termcap.h" + +struct capability { + const char *name; + const char *value; +}; + +struct flag_capability { + const char *name; + int value; +}; + +struct num_capability { + const char *name; + int value; +}; + +static const struct capability ansi_caps[] = { + { "al", "\033[L" }, + { "bl", "\a" }, + { "cd", "\033[J" }, + { "ce", "\033[K" }, + { "ch", "\033[%i%dG" }, + { "cl", "\033[H\033[2J" }, + { "dc", "\033[P" }, + { "dl", "\033[M" }, + { "dm", "" }, + { "ed", "" }, + { "ei", "" }, + { "fs", "" }, + { "ho", "\033[H" }, + { "ic", "\033[@" }, + { "im", "" }, + { "ip", "" }, + { "kd", "\033[B" }, + { "kl", "\033[D" }, + { "kr", "\033[C" }, + { "ku", "\033[A" }, + { "md", "\033[1m" }, + { "me", "\033[0m" }, + { "nd", "\033[C" }, + { "se", "\033[0m" }, + { "so", "\033[7m" }, + { "ts", "" }, + { "up", "\033[A" }, + { "us", "\033[4m" }, + { "ue", "\033[0m" }, + { "vb", "\a" }, + { "DC", "\033[%dP" }, + { "DO", "\033[%dB" }, + { "IC", "\033[%d@" }, + { "LE", "\033[%dD" }, + { "RI", "\033[%dC" }, + { "UP", "\033[%dA" }, + { "kh", "\033[H" }, + { "@7", "\033[F" }, + { "kD", "\033[3~" }, + { NULL, NULL } +}; + +static const struct flag_capability flag_caps[] = { + { "am", 1 }, + { "km", 1 }, + { "pt", 1 }, + { "xt", 0 }, + { "xn", 0 }, + { "MT", 0 }, + { NULL, 0 } +}; + +static char tgoto_buf[64]; +static int term_columns = 80; +static int term_lines = 24; + +static const char * +find_capability(const char *id) +{ + size_t i; + + for (i = 0; ansi_caps[i].name != NULL; i++) { + if (strcmp(ansi_caps[i].name, id) == 0) + return ansi_caps[i].value; + } + return NULL; +} + +static void +update_terminal_size(void) +{ + struct winsize ws; + const char *env; + char *end; + long value; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) { + if (ws.ws_col > 0) + term_columns = ws.ws_col; + if (ws.ws_row > 0) + term_lines = ws.ws_row; + } + + env = getenv("COLUMNS"); + if (env != NULL && *env != '\0') { + errno = 0; + value = strtol(env, &end, 10); + if (errno == 0 && *end == '\0' && value > 0 && value <= INT_MAX) + term_columns = (int)value; + } + + env = getenv("LINES"); + if (env != NULL && *env != '\0') { + errno = 0; + value = strtol(env, &end, 10); + if (errno == 0 && *end == '\0' && value > 0 && value <= INT_MAX) + term_lines = (int)value; + } +} + +int +tgetent(char *bp, const char *name) +{ + (void)bp; + (void)name; + update_terminal_size(); + return 1; +} + +int +tgetflag(const char *id) +{ + size_t i; + + for (i = 0; flag_caps[i].name != NULL; i++) { + if (strcmp(flag_caps[i].name, id) == 0) + return flag_caps[i].value; + } + return 0; +} + +int +tgetnum(const char *id) +{ + if (strcmp(id, "co") == 0) + return term_columns; + if (strcmp(id, "li") == 0) + return term_lines; + return -1; +} + +char * +tgetstr(const char *id, char **area) +{ + const char *value; + size_t len; + char *dst; + + value = find_capability(id); + if (value == NULL) + return NULL; + if (area == NULL) + return (char *)value; + + len = strlen(value) + 1; + dst = *area; + memcpy(dst, value, len); + *area += len; + return dst; +} + +int +tputs(const char *str, int affcnt, int (*putc_fn)(int)) +{ + const unsigned char *p; + + (void)affcnt; + if (str == NULL || putc_fn == NULL) + return -1; + for (p = (const unsigned char *)str; *p != '\0'; p++) { + if (putc_fn(*p) == EOF) + return -1; + } + return 0; +} + +static int +next_param(const int params[2], int *index) +{ + int value; + + if (*index >= 2) + return 0; + value = params[*index]; + (*index)++; + if (value < 0) + value = -value; + return value; +} + +char * +tgoto(const char *cap, int col, int row) +{ + const unsigned char *src; + char *dst; + int params[2]; + int param_index; + + if (cap == NULL) + return NULL; + + params[0] = col; + params[1] = row; + param_index = 0; + dst = tgoto_buf; + src = (const unsigned char *)cap; + + while (*src != '\0' && (size_t)(dst - tgoto_buf) < sizeof(tgoto_buf) - 1) { + if (*src != '%') { + *dst++ = (char)*src++; + continue; + } + + src++; + switch (*src) { + case '%': + *dst++ = '%'; + src++; + break; + case 'd': { + int value; + int written; + + value = next_param(params, ¶m_index); + written = snprintf(dst, + sizeof(tgoto_buf) - (size_t)(dst - tgoto_buf), + "%d", value); + if (written < 0) + return NULL; + dst += written; + src++; + break; + } + case '2': + case '3': { + int width; + int value; + int written; + + width = *src - '0'; + value = next_param(params, ¶m_index); + written = snprintf(dst, + sizeof(tgoto_buf) - (size_t)(dst - tgoto_buf), + "%0*d", width, value); + if (written < 0) + return NULL; + dst += written; + src++; + break; + } + case '.': { + int value; + + value = next_param(params, ¶m_index); + if (value == 0) + value = ' '; + *dst++ = (char)value; + src++; + break; + } + case '+': { + int value; + + src++; + value = next_param(params, ¶m_index); + *dst++ = (char)(value + *src++); + break; + } + case 'i': + params[0]++; + params[1]++; + src++; + break; + case 'r': { + int tmp; + + tmp = params[0]; + params[0] = params[1]; + params[1] = tmp; + src++; + break; + } + default: + *dst++ = '%'; + if (*src != '\0') + *dst++ = (char)*src++; + break; + } + } + + *dst = '\0'; + return tgoto_buf; +} diff --git a/termcap.h b/termcap.h new file mode 100644 index 0000000000..17f98b4b9c --- /dev/null +++ b/termcap.h @@ -0,0 +1,11 @@ +#ifndef SH_TERMCAP_H +#define SH_TERMCAP_H + +int tgetent(char *bp, const char *name); +int tgetflag(const char *id); +int tgetnum(const char *id); +char *tgetstr(const char *id, char **area); +int tputs(const char *str, int affcnt, int (*putc_fn)(int)); +char *tgoto(const char *cap, int col, int row); + +#endif diff --git a/tests/linux/bind-no-history.2 b/tests/linux/bind-no-history.2 deleted file mode 100644 index 4eb66e263c..0000000000 --- a/tests/linux/bind-no-history.2 +++ /dev/null @@ -1 +0,0 @@ -bind '^I' self-insert diff --git a/tests/linux/bind-no-history.2.stderr b/tests/linux/bind-no-history.2.stderr deleted file mode 100644 index 009b41f04f..0000000000 --- a/tests/linux/bind-no-history.2.stderr +++ /dev/null @@ -1 +0,0 @@ -bind: not compiled with line editing support diff --git a/tests/linux/fc-no-history.2 b/tests/linux/fc-no-history.2 deleted file mode 100644 index 6bfcc02b19..0000000000 --- a/tests/linux/fc-no-history.2 +++ /dev/null @@ -1 +0,0 @@ -fc -l diff --git a/tests/linux/fc-no-history.2.stderr b/tests/linux/fc-no-history.2.stderr deleted file mode 100644 index 8b2fe95717..0000000000 --- a/tests/linux/fc-no-history.2.stderr +++ /dev/null @@ -1 +0,0 @@ -fc: not compiled with history support diff --git a/tests/test.sh b/tests/test.sh index c0acb5ae46..2ba944951c 100644 --- a/tests/test.sh +++ b/tests/test.sh @@ -12,9 +12,6 @@ trap 'rm -rf "$WORKDIR"' EXIT INT TERM SKIP_CASES=' parameters/mail1.0 parameters/mail2.0 -builtins/fc1.0 -builtins/fc2.0 -builtins/fc3.0 ' fail() { |
