diff options
| author | Michele Bini <rev@alienbuntu.local> | 2019-04-18 22:12:22 (GMT) |
|---|---|---|
| committer | Michele Bini <rev@alienbuntu.local> | 2019-04-18 22:13:11 (GMT) |
| commit | 3567ae383ea8d19f054ba138c8250446cc4a8bb1 (patch) | |
| tree | d7d23be597c2ffb7176fbaa511050a067947a62f | |
| parent | da7ba579a7d9a95457f87bc5389a53b12d6fc9cb (diff) | |
.
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | Makefile.save | 2 | ||||
| -rw-r--r-- | bboxfix.c | 7 | ||||
| -rw-r--r-- | bboxfix.h | 63 | ||||
| -rw-r--r-- | full_write.c | 42 | ||||
| -rw-r--r-- | keycodes.h | 49 | ||||
| -rw-r--r-- | libbb.h | 1 | ||||
| -rw-r--r-- | nanobox.c | 255 | ||||
| -rw-r--r-- | platform.h | 587 | ||||
| -rw-r--r-- | read.c | 72 | ||||
| -rw-r--r-- | read_key.c | 293 | ||||
| -rw-r--r-- | safe_poll.c | 34 | ||||
| -rw-r--r-- | safe_write.c | 29 | ||||
| -rw-r--r-- | verror_msg.c | 182 | ||||
| -rw-r--r-- | xatonum.h | 185 |
16 files changed, 1734 insertions, 70 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3814265 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +nanobox diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a9419d8 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +CFLAGS = -DENABLE_FEATURE_VERBOSE=0 +nanobox: full_write.c read.c read_key.c safe_poll.c safe_write.c verror_msg.c bboxfix.c nanobox.c diff --git a/Makefile.save b/Makefile.save new file mode 100644 index 0000000..a469b28 --- /dev/null +++ b/Makefile.save @@ -0,0 +1,2 @@ +nanobox: nanobox.c full_write.c read.c read_key.c safe_poll.c +verror_msg.c diff --git a/bboxfix.c b/bboxfix.c new file mode 100644 index 0000000..355e934 --- /dev/null +++ b/bboxfix.c @@ -0,0 +1,7 @@ +#include "bboxfix.h" + +const char *applet_name = "nanobox"; + +void xfunc_die() { + exit(EXIT_FAILURE); +} diff --git a/bboxfix.h b/bboxfix.h new file mode 100644 index 0000000..b78d1b0 --- /dev/null +++ b/bboxfix.h @@ -0,0 +1,63 @@ +#define _GNU_SOURCE +#include <setjmp.h> +#include <termios.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <ctype.h> +#include <stddef.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#if 1 +#include "platform.h" +#define FALSE 0 +#define TRUE 1 +#define smallint int +// #define KEYCODE_BUFFER_SIZE 16 +struct globals *ptr_to_globals; +#define SET_PTR_TO_GLOBALS(x) ptr_to_globals = (x) +#define xzalloc malloc +#define xstrndup strndup +#define xstrdup strdup +#define xmalloc malloc +#define xrealloc realloc +#define xfree free +#define bb_putchar putchar +#define bb_perror_msg perror +// #define bb_error_msg_and_die(...) exit((fprintf(stderr, __VA_ARGS__)&0)|(EXIT_FAILURE)) +#define ssize_t size_t +#define FAST_FUNC +#define NOINLINE +#include "keycodes.h" +#define TERMIOS_RAW_CRNL (1 << 1) +#define TERMIOS_CLEAR_ISIG (1 << 0) +#define TERMIOS_RAW_INPUT (1 << 2) +#define ARRAY_SIZE(x) ((unsigned)((sizeof(x)) / (sizeof(x[0])))) +#define LOGMODE_STDIO 0 +extern const char *applet_name; +#if 1 +int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *height); +int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout); +int FAST_FUNC set_termios_to_raw(int fd, struct termios *oldterm, int flags); +int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp); +int safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout_ms) FAST_FUNC; +void FAST_FUNC bb_error_msg_and_die(const char *s, ...); +ssize_t FAST_FUNC full_read(int fd, void *buf, size_t len); +ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len); +static int wh_helper(int value, int def_val, const char *env_name, int *err); +ssize_t FAST_FUNC safe_write(int fd, const void *buf, size_t count); +ssize_t FAST_FUNC safe_read(int fd, void *buf, size_t count); +void xfunc_die(); +#endif +#endif + +#define ENABLE_FEATURE_EDITING_ASK_TERMINAL 1 +#define ENABLE_FEATURE_VI_ASK_TERMINAL 1 +#define ENABLE_FEATURE_LESS_ASK_TERMINAL 1 diff --git a/full_write.c b/full_write.c new file mode 100644 index 0000000..777fbd9 --- /dev/null +++ b/full_write.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" + +/* + * Write all of the supplied buffer out to a file. + * This does multiple writes as necessary. + * Returns the amount written, or -1 on an error. + */ +ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len) +{ + ssize_t cc; + ssize_t total; + + total = 0; + + while (len) { + cc = safe_write(fd, buf, len); + + if (cc < 0) { + if (total) { + /* we already wrote some! */ + /* user can do another write to know the error code */ + return total; + } + return cc; /* write() returns -1 on failure. */ + } + + total += cc; + buf = ((const char *)buf) + cc; + len -= cc; + } + + return total; +} diff --git a/keycodes.h b/keycodes.h new file mode 100644 index 0000000..4541734 --- /dev/null +++ b/keycodes.h @@ -0,0 +1,49 @@ +enum { + KEYCODE_UP = -2, + KEYCODE_DOWN = -3, + KEYCODE_RIGHT = -4, + KEYCODE_LEFT = -5, + KEYCODE_HOME = -6, + KEYCODE_END = -7, + KEYCODE_INSERT = -8, + KEYCODE_DELETE = -9, + KEYCODE_PAGEUP = -10, + KEYCODE_PAGEDOWN = -11, + KEYCODE_BACKSPACE = -12, /* Used only if Alt/Ctrl/Shifted */ + KEYCODE_D = -13, /* Used only if Alted */ +#if 0 + KEYCODE_FUN1 = , + KEYCODE_FUN2 = , + KEYCODE_FUN3 = , + KEYCODE_FUN4 = , + KEYCODE_FUN5 = , + KEYCODE_FUN6 = , + KEYCODE_FUN7 = , + KEYCODE_FUN8 = , + KEYCODE_FUN9 = , + KEYCODE_FUN10 = , + KEYCODE_FUN11 = , + KEYCODE_FUN12 = , +#endif + /* ^^^^^ Be sure that last defined value is small enough. + * Current read_key() code allows going up to -32 (0xfff..fffe0). + * This gives three upper bits in LSB to play with: + * KEYCODE_foo values are 0xfff..fffXX, lowest XX bits are: scavvvvv, + * s=0 if SHIFT, c=0 if CTRL, a=0 if ALT, + * vvvvv bits are the same for same key regardless of "shift bits". + */ + //KEYCODE_SHIFT_... = KEYCODE_... & ~0x80, + KEYCODE_CTRL_RIGHT = KEYCODE_RIGHT & ~0x40, + KEYCODE_CTRL_LEFT = KEYCODE_LEFT & ~0x40, + KEYCODE_ALT_RIGHT = KEYCODE_RIGHT & ~0x20, + KEYCODE_ALT_LEFT = KEYCODE_LEFT & ~0x20, + KEYCODE_ALT_BACKSPACE = KEYCODE_BACKSPACE & ~0x20, + KEYCODE_ALT_D = KEYCODE_D & ~0x20, + + KEYCODE_CURSOR_POS = -0x100, /* 0xfff..fff00 */ + /* How long is the longest ESC sequence we know? + * We want it big enough to be able to contain + * cursor position sequence "ESC [ 9999 ; 9999 R" + */ + KEYCODE_BUFFER_SIZE = 16 +}; @@ -0,0 +1 @@ +#include "bboxfix.h" @@ -17,12 +17,14 @@ * An "ex" line oriented mode- maybe using "cmdedit" */ +#define ENABLE_FEATURE_NANO 1 //config:config NANO //config: bool "nano" //config: default y //config: help //config: 'nano' is a text editor. This is a clone attempt. //config: +#define CONFIG_FEATURE_NANO_MAX_LEN 4096 //config:config FEATURE_NANO_MAX_LEN //config: int "Maximum screen width" //config: range 256 16384 @@ -32,6 +34,7 @@ //config: Contrary to what you may think, this is not eating much. //config: Make it smaller than 4k only if you are very limited on memory. //config: +#define ENABLE_FEATURE_NANO_8BIT 1 //config:config FEATURE_NANO_8BIT //config: bool "Allow to display 8-bit chars (otherwise shows dots)" //config: default n @@ -42,6 +45,7 @@ //config: If your terminal combines several 8-bit bytes into one character //config: (as in Unicode mode), this will not work properly. //config: +#define ENABLE_FEATURE_NANO_UTF8 1 //config:config FEATURE_NANO_UTF8 //config: bool "Allow editing utf-8 files" //config: default y @@ -49,6 +53,7 @@ //config: help //config: Allow input of utf8 characters. (EXPERIMENTAL) //config: +#define ENABLE_FEATURE_NANO_UTF8_MIXED 1 //config:config FEATURE_NANO_UTF8_MIXED //config: bool "Allow editing of utf-8 files containing invalid character sequences" //config: default y @@ -56,14 +61,7 @@ //config: help //config: Allow input of utf8 characters. (EXPERIMENTAL) //config: -//config:config FEATURE_NANO_COLON -//config: bool "Enable \":\" colon commands (no \"ex\" mode)" -//config: default y -//config: depends on NANO -//config: help -//config: Enable a limited set of colon commands. This does not -//config: provide an "ex" mode. -//config: +#define ENABLE_FEATURE_NANO_YANKMARK 1 //config:config FEATURE_NANO_YANKMARK //config: bool "Enable yank/put commands and mark cmds" //config: default y @@ -71,6 +69,8 @@ //config: help //config: This will enable you to use yank and put, as well as mark. //config: +#define ENABLE_FEATURE_NANO_SEARCH 1 +#define IF_FEATURE_NANO_SEARCH(...) __VA_ARGS__ //config:config FEATURE_NANO_SEARCH //config: bool "Enable search and replace cmds" //config: default y @@ -78,6 +78,7 @@ //config: help //config: Select this if you wish to be able to do search and replace. //config: +#define ENABLE_FEATURE_NANO_REGEX_SEARCH 0 //config:config FEATURE_NANO_REGEX_SEARCH //config: bool "Enable regex in search and replace" //config: default n # Uses GNU regex, which may be unavailable. FIXME @@ -85,6 +86,7 @@ //config: help //config: Use extended regex search. //config: +#define ENABLE_FEATURE_NANO_USE_SIGNALS 1 //config:config FEATURE_NANO_USE_SIGNALS //config: bool "Catch signals" //config: default y @@ -93,6 +95,7 @@ //config: Selecting this option will make nano signal aware. This will support //config: SIGWINCH to deal with Window Changes, catch ^Z and ^C and alarms. //config: +#define ENABLE_FEATURE_NANO_READONLY 1 //config:config FEATURE_NANO_READONLY //config: bool "Enable -R option and \"view\" mode" //config: default y @@ -101,6 +104,7 @@ //config: Enable the read-only command line option, which allows the user to //config: open a file in read-only mode. //config: +#define ENABLE_FEATURE_NANO_SETOPTS 1 //config:config FEATURE_NANO_SETOPTS //config: bool "Enable settable options, ai ic showmatch" //config: default y @@ -109,11 +113,7 @@ //config: Enable the editor to set some (ai, ic, showmatch) options. //config: Consider removing this option! FIXME //config: -//config:config FEATURE_NANO_SET -//config: bool "Support :set" -//config: default y -//config: depends on NANO -//config: +#define ENABLE_FEATURE_NANO_WIN_RESIZE 1 //config:config FEATURE_NANO_WIN_RESIZE //config: bool "Handle window resize" //config: default y @@ -121,6 +121,8 @@ //config: help //config: Behave nicely with terminals that get resized. //config: +#define ENABLE_FEATURE_NANO_ASK_TERMINAL 1 +#define IF_FEATURE_NANO_ASK_TERMINAL(...) __VA_ARGS__ //config:config FEATURE_NANO_ASK_TERMINAL //config: bool "Use 'tell me cursor position' ESC sequence to measure window" //config: default y @@ -132,6 +134,7 @@ //config: cursor position using "ESC [ 6 n" escape sequence, then read stdin. //config: This is not clean but helps a lot on serial lines and such. //config: +#define ENABLE_FEATURE_NANO_UNDO 1 //config:config FEATURE_NANO_UNDO //config: bool "Support undo command \"u\"" //config: default y @@ -140,6 +143,7 @@ //config: Support the 'u' command to undo insertion, deletion, and replacement //config: of text. //config: +#define ENABLE_FEATURE_NANO_UNDO_QUEUE 1 //config:config FEATURE_NANO_UNDO_QUEUE //config: bool "Enable undo operation queuing" //config: default y @@ -151,6 +155,7 @@ //config: This increases the size of the undo code and allows some undo //config: operations (especially un-typing/backspacing) to be far more useful. //config: +#define CONFIG_FEATURE_NANO_UNDO_QUEUE_MAX 256 //config:config FEATURE_NANO_UNDO_QUEUE_MAX //config: int "Maximum undo character queue size" //config: default 256 @@ -165,6 +170,7 @@ //config: Unless you want more (or less) frequent "undo points" while typing, //config: you should probably leave this unchanged. //config: +#define ENABLE_FEATURE_NANO_HIGHLIGHT 1 //config:config FEATURE_NANO_HIGHLIGHT //config: bool "Support syntax highlighting" //config: default y @@ -172,6 +178,7 @@ //config: help //config: Support syntax highlighting. //config: +#define ENABLE_FEATURE_NANO_HIGHLIGHT_LANGUAGE_C 1 //config:config FEATURE_NANO_HIGHLIGHT_LANGUAGE_C //config: bool "Support syntax highlighting for C language" //config: default y @@ -180,6 +187,56 @@ //config: Support syntax highlighting for the C language //config: +#define _GNU_SOURCE +#include <setjmp.h> +#include <termios.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <ctype.h> +#include <stddef.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#if 1 +#include "platform.h" +#define FALSE 0 +#define TRUE 1 +#define smallint int +// #define KEYCODE_BUFFER_SIZE 16 +struct globals *ptr_to_globals; +#define SET_PTR_TO_GLOBALS(x) ptr_to_globals = (x) +#define xzalloc malloc +#define xstrndup strndup +#define xstrdup strdup +#define xmalloc malloc +#define xrealloc realloc +#define xfree free +#define FAST_FUNC +int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *height); +int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout); +#include "keycodes.h" +#define NOINLINE +#define ARRAY_SIZE(x) ((unsigned)((sizeof(x)) / (sizeof(x[0])))) +int FAST_FUNC set_termios_to_raw(int fd, struct termios *oldterm, int flags); +#define TERMIOS_RAW_CRNL (1 << 1) +#define TERMIOS_CLEAR_ISIG (1 << 0) +#define TERMIOS_RAW_INPUT (1 << 2) +int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp); +int safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout_ms) FAST_FUNC; +void FAST_FUNC bb_error_msg_and_die(const char *s, ...); +#define bb_putchar putchar +ssize_t FAST_FUNC full_read(int fd, void *buf, size_t len); +ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len); +static int wh_helper(int value, int def_val, const char *env_name, int *err); +#endif + //applet:IF_NANO(APPLET(nano, BB_DIR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_NANO) += nano.o @@ -188,9 +245,6 @@ //usage: "[OPTIONS] [FILE]..." //usage:#define nano_full_usage "\n\n" //usage: "Edit FILE\n" -//usage: IF_FEATURE_NANO_COLON( -//usage: "\n -c CMD Initial command to run ($EXINIT also available)" -//usage: ) //usage: IF_FEATURE_NANO_READONLY( //usage: "\n -R Read-only" //usage: ) @@ -262,13 +316,6 @@ enum { //#define ESC_CURSOR_UP "\033[A" //#define ESC_CURSOR_DOWN "\n" -#if ENABLE_FEATURE_NANO_YANKMARK -// cmds modifying text[] -// vda: removed "aAiIs" as they switch us into insert mode -// and remembering input for replay after them makes no sense -static const char modifying_cmds[] ALIGN1 = "cCdDJoOpPrRxX<>~"; -#endif - enum { YANKONLY = FALSE, YANKDEL = TRUE, @@ -358,9 +405,6 @@ struct globals { sigjmp_buf restart; // catch_sig() #endif struct termios term_orig; // remember what the cooked mode was -#if ENABLE_FEATURE_NANO_COLON - char *initial_cmds[3]; // currently 2 entries, NULL terminated -#endif // Should be just enough to hold a key sequence, // but CRASHME mode uses it as generated command buffer too #if ENABLE_FEATURE_NANO_CRASHME @@ -561,7 +605,7 @@ static void catch_sig(int); // catch ctrl-C and alarm time-outs #if ENABLE_FEATURE_NANO_SETOPTS static void showmatching(char *); // show the matching pair () [] {} #endif -#if ENABLE_FEATURE_NANO_YANKMARK || (ENABLE_FEATURE_NANO_COLON && ENABLE_FEATURE_NANO_SEARCH) || ENABLE_FEATURE_NANO_CRASHME +#if ENABLE_FEATURE_NANO_YANKMARK || ENABLE_FEATURE_NANO_SEARCH || ENABLE_FEATURE_NANO_CRASHME // might reallocate text[]! use p += string_insert(p, ...), // and be careful to not use pointers into potentially freed text[]! # if !ENABLE_FEATURE_NANO_UNDO @@ -603,8 +647,7 @@ static void write1(const char *out) fputs(out, stdout); } -int nano_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int nano_main(int argc, char **argv) +int main(int argc, char **argv) { int c; @@ -636,14 +679,7 @@ int nano_main(int argc, char **argv) // 1- process $HOME/.exrc file (not inplemented yet) // 2- process EXINIT variable from environment // 3- process command line args -#if ENABLE_FEATURE_NANO_COLON - { - char *p = getenv("EXINIT"); - if (p && *p) - initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN); - } -#endif - while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_NANO_COLON("c:"))) != -1) { + while ((c = getopt(argc, argv, "hCRH")) != -1) { switch (c) { #if ENABLE_FEATURE_NANO_CRASHME case 'C': @@ -655,17 +691,11 @@ int nano_main(int argc, char **argv) SET_READONLY_MODE(readonly_mode); break; #endif -#if ENABLE_FEATURE_NANO_COLON - case 'c': // cmd line nano command - if (*optarg) - initial_cmds[initial_cmds[0] != NULL] = xstrndup(optarg, MAX_INPUT_LEN); - break; -#endif case 'H': show_help(); /* fall through */ default: - bb_show_usage(); + // bb_show_usage(); return 1; } } @@ -755,7 +785,7 @@ static void edit_file(char *fn) if (G.get_rowcol_error /* TODO? && no input on stdin */) { uint64_t k; write1("\033[999;999H" "\033[6n"); - fflush_all(); + fflush(NULL); k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100); if ((int32_t)k == KEYCODE_CURSOR_POS) { uint32_t rc = (k >> 32); @@ -1778,7 +1808,7 @@ static void show_help(void) } #if ENABLE_FEATURE_NANO_YANKMARK \ - || (ENABLE_FEATURE_NANO_COLON && ENABLE_FEATURE_NANO_SEARCH) \ + || ENABLE_FEATURE_NANO_SEARCH \ || ENABLE_FEATURE_NANO_CRASHME // might reallocate text[]! use p += string_insert(p, ...), // and be careful to not use pointers into potentially freed text[]! @@ -1835,23 +1865,6 @@ static char *text_yank(char *p, char *q) // copy text into a register return p; } -static void check_context(char cmd) -{ - // A context is defined to be "modifying text" - // Any modifying command establishes a new context. - - if (dot < context_start || dot > context_end) { - if (strchr(modifying_cmds, cmd) != NULL) { - // we are trying to modify text[]- make this the current context - mark[27] = mark[26]; // move cur to prev - mark[26] = dot; // move local to cur - context_start = prev_line(prev_line(dot)); - context_end = next_line(next_line(dot)); - //loiter= start_loiter= now; - } - } -} - #endif /* FEATURE_NANO_YANKMARK */ //----- Set terminal attributes -------------------------------- @@ -1864,7 +1877,7 @@ static void rawmode(void) static void cookmode(void) { - fflush_all(); + fflush(NULL); tcsetattr_stdin_TCSANOW(&term_orig); } @@ -1921,7 +1934,7 @@ static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready struct pollfd pfd[1]; if (hund != 0) - fflush_all(); + fflush(NULL); pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; @@ -1932,7 +1945,7 @@ static int get_one_char(void) // read (maybe cursor) key from stdin { int c; - fflush_all(); + fflush(NULL); // Wait for input. TIMEOUT = -1 makes read_key wait even // on nonblocking stdin. @@ -2215,7 +2228,7 @@ static void show_status_line(void) } place_cursor(crow, ccol); // put cursor back in correct place } - fflush_all(); + fflush(NULL); } //----- format the status buffer, the bottom line of screen ------ @@ -2861,9 +2874,6 @@ static void do_cmd(int c) if (dot != end) { dot = bound_dot(dot); // make sure "dot" is valid } -#if ENABLE_FEATURE_NANO_YANKMARK - check_context(c); // update the current context -#endif } /* NB! the CRASHME code is unmaintained, and doesn't currently build */ @@ -3071,3 +3081,108 @@ static void crash_test() } } #endif + +int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *height) +{ + struct winsize win; + int err; + int close_me = -1; + + if (fd == -1) { + if (isatty(STDOUT_FILENO)) + fd = STDOUT_FILENO; + else + if (isatty(STDERR_FILENO)) + fd = STDERR_FILENO; + else + if (isatty(STDIN_FILENO)) + fd = STDIN_FILENO; + else + close_me = fd = open("/dev/tty", O_RDONLY); + } + + win.ws_row = 0; + win.ws_col = 0; + /* I've seen ioctl returning 0, but row/col is (still?) 0. + * We treat that as an error too. */ + err = ioctl(fd, TIOCGWINSZ, &win) != 0 || win.ws_row == 0; + if (height) + *height = wh_helper(win.ws_row, 24, "LINES", &err); + if (width) + *width = wh_helper(win.ws_col, 80, "COLUMNS", &err); + + if (close_me >= 0) + close(close_me); + + return err; +} + +int FAST_FUNC set_termios_to_raw(int fd, struct termios *oldterm, int flags) +{ +//TODO: lineedit, microcom and less might be adapted to use this too: +// grep for "tcsetattr" + + struct termios newterm; + + tcgetattr(fd, oldterm); + newterm = *oldterm; + + /* Turn off buffered input (ICANON) + * Turn off echoing (ECHO) + * and separate echoing of newline (ECHONL, normally off anyway) + */ + newterm.c_lflag &= ~(ICANON | ECHO | ECHONL); + if (flags & TERMIOS_CLEAR_ISIG) { + /* dont recognize INT/QUIT/SUSP chars */ + newterm.c_lflag &= ~ISIG; + } + /* reads will block only if < 1 char is available */ + newterm.c_cc[VMIN] = 1; + /* no timeout (reads block forever) */ + newterm.c_cc[VTIME] = 0; + if (flags & TERMIOS_RAW_CRNL) { + /* dont convert CR to NL on input */ + newterm.c_iflag &= ~(IXON | ICRNL); + /* dont convert NL to CR on output */ + newterm.c_oflag &= ~(ONLCR); + } + if (flags & TERMIOS_RAW_INPUT) { + /* dont convert anything on input */ + newterm.c_iflag &= ~(BRKINT|INLCR|ICRNL|IXON|IXOFF|IUCLC|IXANY|IMAXBEL); + } + + return tcsetattr(fd, TCSANOW, &newterm); +} + +int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp) +{ + return tcsetattr(STDIN_FILENO, TCSANOW, tp); +} + +static int wh_helper(int value, int def_val, const char *env_name, int *err) +{ + /* Envvars override even if "value" from ioctl is valid (>0). + * Rationale: it's impossible to guess what user wants. + * For example: "man CMD | ...": should "man" format output + * to stdout's width? stdin's width? /dev/tty's width? 80 chars? + * We _cant_ know it. If "..." saves text for e.g. email, + * then it's probably 80 chars. + * If "..." is, say, "grep -v DISCARD | $PAGER", then user + * would prefer his tty's width to be used! + * + * Since we don't know, at least allow user to do this: + * "COLUMNS=80 man CMD | ..." + */ + char *s = getenv(env_name); + if (s) { + value = atoi(s); + /* If LINES/COLUMNS are set, pretend that there is + * no error getting w/h, this prevents some ugly + * cursor tricks by our callers */ + *err = 0; + } + + if (value <= 1 || value >= 30000) + value = def_val; + return value; +} diff --git a/platform.h b/platform.h new file mode 100644 index 0000000..6c7d03d --- /dev/null +++ b/platform.h @@ -0,0 +1,587 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2006, Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#ifndef BB_PLATFORM_H +#define BB_PLATFORM_H 1 + + +/* Convenience macros to test the version of gcc. */ +#undef __GNUC_PREREQ +#if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +# define __GNUC_PREREQ(maj, min) 0 +#endif + +/* __restrict is known in EGCS 1.2 and above. */ +#if !__GNUC_PREREQ(2,92) +# ifndef __restrict +# define __restrict +# endif +#endif + +#if !__GNUC_PREREQ(2,7) +# ifndef __attribute__ +# define __attribute__(x) +# endif +#endif + +#undef inline +#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L +/* it's a keyword */ +#elif __GNUC_PREREQ(2,7) +# define inline __inline__ +#else +# define inline +#endif + +#ifndef __const +# define __const const +#endif + +#define UNUSED_PARAM __attribute__ ((__unused__)) +#define NORETURN __attribute__ ((__noreturn__)) +/* "The malloc attribute is used to tell the compiler that a function + * may be treated as if any non-NULL pointer it returns cannot alias + * any other pointer valid when the function returns. This will often + * improve optimization. Standard functions with this property include + * malloc and calloc. realloc-like functions have this property as long + * as the old pointer is never referred to (including comparing it + * to the new pointer) after the function returns a non-NULL value." + */ +#define RETURNS_MALLOC __attribute__ ((malloc)) +#define PACKED __attribute__ ((__packed__)) +#define ALIGNED(m) __attribute__ ((__aligned__(m))) + +/* __NO_INLINE__: some gcc's do not honor inlining! :( */ +#if __GNUC_PREREQ(3,0) && !defined(__NO_INLINE__) +# define ALWAYS_INLINE __attribute__ ((always_inline)) inline +/* I've seen a toolchain where I needed __noinline__ instead of noinline */ +# define NOINLINE __attribute__((__noinline__)) +# if !ENABLE_WERROR +# define DEPRECATED __attribute__ ((__deprecated__)) +# define UNUSED_PARAM_RESULT __attribute__ ((warn_unused_result)) +# else +# define DEPRECATED +# define UNUSED_PARAM_RESULT +# endif +#else +# define ALWAYS_INLINE inline +# define NOINLINE +# define DEPRECATED +# define UNUSED_PARAM_RESULT +#endif + +/* used by unit test machinery to run registration functions before calling main() */ +#define INIT_FUNC __attribute__ ((constructor)) + +/* -fwhole-program makes all symbols local. The attribute externally_visible + * forces a symbol global. */ +#if __GNUC_PREREQ(4,1) +# define EXTERNALLY_VISIBLE __attribute__(( visibility("default") )) +//__attribute__ ((__externally_visible__)) +#else +# define EXTERNALLY_VISIBLE +#endif + +/* At 4.4 gcc become much more anal about this, need to use "aliased" types */ +#if __GNUC_PREREQ(4,4) +# define FIX_ALIASING __attribute__((__may_alias__)) +#else +# define FIX_ALIASING +#endif + +/* We use __extension__ in some places to suppress -pedantic warnings + * about GCC extensions. This feature didn't work properly before + * gcc 2.8. */ +#if !__GNUC_PREREQ(2,8) +# ifndef __extension__ +# define __extension__ +# endif +#endif + +/* FAST_FUNC is a qualifier which (possibly) makes function call faster + * and/or smaller by using modified ABI. It is usually only needed + * on non-static, busybox internal functions. Recent versions of gcc + * optimize statics automatically. FAST_FUNC on static is required + * only if you need to match a function pointer's type. + * FAST_FUNC may not work well with -flto so allow user to disable this. + * (-DFAST_FUNC= ) + */ +#ifndef FAST_FUNC +# if __GNUC_PREREQ(3,0) && defined(i386) +/* stdcall makes callee to pop arguments from stack, not caller */ +# define FAST_FUNC __attribute__((regparm(3),stdcall)) +/* #elif ... - add your favorite arch today! */ +# else +# define FAST_FUNC +# endif +#endif + +/* Make all declarations hidden (-fvisibility flag only affects definitions) */ +/* (don't include system headers after this until corresponding pop!) */ +#if __GNUC_PREREQ(4,1) && !defined(__CYGWIN__) +# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN _Pragma("GCC visibility push(hidden)") +# define POP_SAVED_FUNCTION_VISIBILITY _Pragma("GCC visibility pop") +#else +# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN +# define POP_SAVED_FUNCTION_VISIBILITY +#endif + +/* gcc-2.95 had no va_copy but only __va_copy. */ +#if !__GNUC_PREREQ(3,0) +# include <stdarg.h> +# if !defined va_copy && defined __va_copy +# define va_copy(d,s) __va_copy((d),(s)) +# endif +#endif + + +/* ---- Endian Detection ------------------------------------ */ + +#include <limits.h> +#if defined(__digital__) && defined(__unix__) +# include <sex.h> +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ + || defined(__APPLE__) +# include <sys/resource.h> /* rlimit */ +# include <machine/endian.h> +# define bswap_64 __bswap64 +# define bswap_32 __bswap32 +# define bswap_16 __bswap16 +#else +# include <byteswap.h> +# include <endian.h> +#endif + +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN +# define BB_BIG_ENDIAN 1 +# define BB_LITTLE_ENDIAN 0 +#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN +# define BB_BIG_ENDIAN 0 +# define BB_LITTLE_ENDIAN 1 +#elif defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN +# define BB_BIG_ENDIAN 1 +# define BB_LITTLE_ENDIAN 0 +#elif defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN +# define BB_BIG_ENDIAN 0 +# define BB_LITTLE_ENDIAN 1 +#elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN +# define BB_BIG_ENDIAN 1 +# define BB_LITTLE_ENDIAN 0 +#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN +# define BB_BIG_ENDIAN 0 +# define BB_LITTLE_ENDIAN 1 +#elif defined(__386__) +# define BB_BIG_ENDIAN 0 +# define BB_LITTLE_ENDIAN 1 +#else +# error "Can't determine endianness" +#endif + +#if ULONG_MAX > 0xffffffff +# define bb_bswap_64(x) bswap_64(x) +#endif + +/* SWAP_LEnn means "convert CPU<->little_endian by swapping bytes" */ +#if BB_BIG_ENDIAN +# define SWAP_BE16(x) (x) +# define SWAP_BE32(x) (x) +# define SWAP_BE64(x) (x) +# define SWAP_LE16(x) bswap_16(x) +# define SWAP_LE32(x) bswap_32(x) +# define SWAP_LE64(x) bb_bswap_64(x) +# define IF_BIG_ENDIAN(...) __VA_ARGS__ +# define IF_LITTLE_ENDIAN(...) +#else +# define SWAP_BE16(x) bswap_16(x) +# define SWAP_BE32(x) bswap_32(x) +# define SWAP_BE64(x) bb_bswap_64(x) +# define SWAP_LE16(x) (x) +# define SWAP_LE32(x) (x) +# define SWAP_LE64(x) (x) +# define IF_BIG_ENDIAN(...) +# define IF_LITTLE_ENDIAN(...) __VA_ARGS__ +#endif + + +/* ---- Unaligned access ------------------------------------ */ + +#include <stdint.h> +typedef int bb__aliased_int FIX_ALIASING; +typedef long bb__aliased_long FIX_ALIASING; +typedef uint16_t bb__aliased_uint16_t FIX_ALIASING; +typedef uint32_t bb__aliased_uint32_t FIX_ALIASING; +typedef uint64_t bb__aliased_uint64_t FIX_ALIASING; + +/* NB: unaligned parameter should be a pointer, aligned one - + * a lvalue. This makes it more likely to not swap them by mistake + */ +#if defined(i386) || defined(__x86_64__) || defined(__powerpc__) +# define BB_UNALIGNED_MEMACCESS_OK 1 +# define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp)) +# define move_from_unaligned_long(v, longp) ((v) = *(bb__aliased_long*)(longp)) +# define move_from_unaligned16(v, u16p) ((v) = *(bb__aliased_uint16_t*)(u16p)) +# define move_from_unaligned32(v, u32p) ((v) = *(bb__aliased_uint32_t*)(u32p)) +# define move_to_unaligned16(u16p, v) (*(bb__aliased_uint16_t*)(u16p) = (v)) +# define move_to_unaligned32(u32p, v) (*(bb__aliased_uint32_t*)(u32p) = (v)) +/* #elif ... - add your favorite arch today! */ +#else +# define BB_UNALIGNED_MEMACCESS_OK 0 +/* performs reasonably well (gcc usually inlines memcpy here) */ +# define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int))) +# define move_from_unaligned_long(v, longp) (memcpy(&(v), (longp), sizeof(long))) +# define move_from_unaligned16(v, u16p) (memcpy(&(v), (u16p), 2)) +# define move_from_unaligned32(v, u32p) (memcpy(&(v), (u32p), 4)) +# define move_to_unaligned16(u16p, v) do { \ + uint16_t __t = (v); \ + memcpy((u16p), &__t, 2); \ +} while (0) +# define move_to_unaligned32(u32p, v) do { \ + uint32_t __t = (v); \ + memcpy((u32p), &__t, 4); \ +} while (0) +#endif + + +/* ---- Size-saving "small" ints (arch-dependent) ----------- */ + +#if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__) +/* add other arches which benefit from this... */ +typedef signed char smallint; +typedef unsigned char smalluint; +#else +/* for arches where byte accesses generate larger code: */ +typedef int smallint; +typedef unsigned smalluint; +#endif + +/* ISO C Standard: 7.16 Boolean type and values <stdbool.h> */ +#if (defined __digital__ && defined __unix__) +/* old system without (proper) C99 support */ +# define bool smalluint +#else +/* modern system, so use it */ +# include <stdbool.h> +#endif + + +/*----- Kernel versioning ------------------------------------*/ + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#ifdef __UCLIBC__ +# define UCLIBC_VERSION KERNEL_VERSION(__UCLIBC_MAJOR__, __UCLIBC_MINOR__, __UCLIBC_SUBLEVEL__) +#else +# define UCLIBC_VERSION 0 +#endif + + +/* ---- Miscellaneous --------------------------------------- */ + +#if defined __GLIBC__ \ + || defined __UCLIBC__ \ + || defined __dietlibc__ \ + || defined __BIONIC__ \ + || defined _NEWLIB_VERSION +# include <features.h> +#endif + +/* Define bb_setpgrp */ +#if defined(__digital__) && defined(__unix__) +/* use legacy setpgrp(pid_t, pid_t) for now. move to platform.c */ +# define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0) +#else +# define bb_setpgrp() setpgrp() +#endif + +/* fdprintf is more readable, we used it before dprintf was standardized */ +#include <unistd.h> +#define fdprintf dprintf + +/* Useful for defeating gcc's alignment of "char message[]"-like data */ +#if !defined(__s390__) + /* on s390[x], non-word-aligned data accesses require larger code */ +# define ALIGN1 __attribute__((aligned(1))) +# define ALIGN2 __attribute__((aligned(2))) +# define ALIGN4 __attribute__((aligned(4))) +#else +/* Arches which MUST have 2 or 4 byte alignment for everything are here */ +# define ALIGN1 +# define ALIGN2 +# define ALIGN4 +#endif + +/* + * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably. + * For earlier versions there is no reliable way to check if we are building + * for a mmu-less system. + */ +#if ENABLE_NOMMU || \ + (defined __UCLIBC__ && \ + UCLIBC_VERSION > KERNEL_VERSION(0, 9, 28) && \ + !defined __ARCH_USE_MMU__) +# define BB_MMU 0 +# define USE_FOR_NOMMU(...) __VA_ARGS__ +# define USE_FOR_MMU(...) +#else +# define BB_MMU 1 +# define USE_FOR_NOMMU(...) +# define USE_FOR_MMU(...) __VA_ARGS__ +#endif + +#if defined(__digital__) && defined(__unix__) +# include <standards.h> +# include <inttypes.h> +# define PRIu32 "u" +# if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET +# define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET) +# endif +# if !defined ADJ_FREQUENCY && defined MOD_FREQUENCY +# define ADJ_FREQUENCY MOD_FREQUENCY +# endif +# if !defined ADJ_TIMECONST && defined MOD_TIMECONST +# define ADJ_TIMECONST MOD_TIMECONST +# endif +# if !defined ADJ_TICK && defined MOD_CLKB +# define ADJ_TICK MOD_CLKB +# endif +#endif + +#if defined(__CYGWIN__) +# define MAXSYMLINKS SYMLOOP_MAX +#endif + +#if defined(ANDROID) || defined(__ANDROID__) +# define BB_ADDITIONAL_PATH ":/system/sbin:/system/bin:/system/xbin" +# define SYS_ioprio_set __NR_ioprio_set +# define SYS_ioprio_get __NR_ioprio_get +#endif + + +/* ---- Who misses what? ------------------------------------ */ + +/* Assume all these functions and header files exist by default. + * Platforms where it is not true will #undef them below. + */ +#define HAVE_CLEARENV 1 +#define HAVE_FDATASYNC 1 +#define HAVE_DPRINTF 1 +#define HAVE_MEMRCHR 1 +#define HAVE_MKDTEMP 1 +#define HAVE_TTYNAME_R 1 +#define HAVE_PTSNAME_R 1 +#define HAVE_SETBIT 1 +#define HAVE_SIGHANDLER_T 1 +#define HAVE_STPCPY 1 +#define HAVE_MEMPCPY 1 +#define HAVE_STRCASESTR 1 +#define HAVE_STRCHRNUL 1 +#define HAVE_STRSEP 1 +#define HAVE_STRSIGNAL 1 +#define HAVE_STRVERSCMP 1 +#define HAVE_VASPRINTF 1 +#define HAVE_USLEEP 1 +#define HAVE_UNLOCKED_STDIO 1 +#define HAVE_UNLOCKED_LINE_OPS 1 +#define HAVE_GETLINE 1 +#define HAVE_XTABS 1 +#define HAVE_MNTENT_H 1 +#define HAVE_NET_ETHERNET_H 1 +#define HAVE_SYS_STATFS_H 1 + +#if defined(__UCLIBC__) +# if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) +# undef HAVE_STRVERSCMP +# endif +# if UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 30) +# ifndef __UCLIBC_SUSV3_LEGACY__ +# undef HAVE_USLEEP +# endif +# endif +#endif + +#if defined(__WATCOMC__) +# undef HAVE_DPRINTF +# undef HAVE_GETLINE +# undef HAVE_MEMRCHR +# undef HAVE_MKDTEMP +# undef HAVE_SETBIT +# undef HAVE_STPCPY +# undef HAVE_STRCASESTR +# undef HAVE_STRCHRNUL +# undef HAVE_STRSEP +# undef HAVE_STRSIGNAL +# undef HAVE_STRVERSCMP +# undef HAVE_VASPRINTF +# undef HAVE_UNLOCKED_STDIO +# undef HAVE_UNLOCKED_LINE_OPS +# undef HAVE_NET_ETHERNET_H +#endif + +#if defined(__CYGWIN__) +# undef HAVE_CLEARENV +# undef HAVE_FDPRINTF +# undef HAVE_MEMRCHR +# undef HAVE_PTSNAME_R +# undef HAVE_STRVERSCMP +# undef HAVE_UNLOCKED_LINE_OPS +#endif + +/* These BSD-derived OSes share many similarities */ +#if (defined __digital__ && defined __unix__) \ + || defined __APPLE__ \ + || defined __OpenBSD__ || defined __NetBSD__ +# undef HAVE_CLEARENV +# undef HAVE_FDATASYNC +# undef HAVE_GETLINE +# undef HAVE_MNTENT_H +# undef HAVE_PTSNAME_R +# undef HAVE_SYS_STATFS_H +# undef HAVE_SIGHANDLER_T +# undef HAVE_STRVERSCMP +# undef HAVE_XTABS +# undef HAVE_DPRINTF +# undef HAVE_UNLOCKED_STDIO +# undef HAVE_UNLOCKED_LINE_OPS +#endif + +#if defined(__dietlibc__) +# undef HAVE_STRCHRNUL +#endif + +#if defined(__APPLE__) +# undef HAVE_STRCHRNUL +#endif + +#if defined(__FreeBSD__) +/* users say mempcpy is not present in FreeBSD 9.x */ +# undef HAVE_MEMPCPY +# undef HAVE_CLEARENV +# undef HAVE_FDATASYNC +# undef HAVE_MNTENT_H +# undef HAVE_PTSNAME_R +# undef HAVE_SYS_STATFS_H +# undef HAVE_SIGHANDLER_T +# undef HAVE_STRVERSCMP +# undef HAVE_XTABS +# undef HAVE_UNLOCKED_LINE_OPS +# include <osreldate.h> +# if __FreeBSD_version < 1000029 +# undef HAVE_STRCHRNUL /* FreeBSD added strchrnul() between 1000028 and 1000029 */ +# endif +#endif + +#if defined(__NetBSD__) +# define HAVE_GETLINE 1 /* Recent NetBSD versions have getline() */ +#endif + +#if defined(__digital__) && defined(__unix__) +# undef HAVE_STPCPY +#endif + +#if defined(ANDROID) || defined(__ANDROID__) +# if __ANDROID_API__ < 8 + /* ANDROID < 8 has no [f]dprintf at all */ +# undef HAVE_DPRINTF +# elif __ANDROID_API__ < 21 + /* ANDROID < 21 has fdprintf */ +# define dprintf fdprintf +# else + /* ANDROID >= 21 has standard dprintf */ +# endif +# if __ANDROID_API__ < 21 +# undef HAVE_TTYNAME_R +# undef HAVE_GETLINE +# undef HAVE_STPCPY +# endif +# undef HAVE_MEMPCPY +# undef HAVE_STRCHRNUL +# undef HAVE_STRVERSCMP +# undef HAVE_UNLOCKED_LINE_OPS +# undef HAVE_NET_ETHERNET_H +#endif + +/* + * Now, define prototypes for all the functions defined in platform.c + * These must come after all the HAVE_* macros are defined (or not) + */ + +#ifndef HAVE_DPRINTF +extern int dprintf(int fd, const char *format, ...); +#endif + +#ifndef HAVE_MEMRCHR +extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC; +#endif + +#ifndef HAVE_MKDTEMP +extern char *mkdtemp(char *template) FAST_FUNC; +#endif + +#ifndef HAVE_TTYNAME_R +#define ttyname_r bb_ttyname_r +extern int ttyname_r(int fd, char *buf, size_t buflen); +#endif + +#ifndef HAVE_SETBIT +# define setbit(a, b) ((a)[(b) >> 3] |= 1 << ((b) & 7)) +# define clrbit(a, b) ((a)[(b) >> 3] &= ~(1 << ((b) & 7))) +#endif + +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t)(int); +#endif + +#ifndef HAVE_STPCPY +extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; +#endif + +#ifndef HAVE_MEMPCPY +#include <string.h> +/* In case we are wrong about !HAVE_MEMPCPY, and toolchain _does_ have + * mempcpy(), avoid colliding with it: + */ +#define mempcpy bb__mempcpy +static ALWAYS_INLINE void *mempcpy(void *dest, const void *src, size_t len) +{ + return memcpy(dest, src, len) + len; +} +#endif + +#ifndef HAVE_STRCASESTR +extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC; +#endif + +#ifndef HAVE_STRCHRNUL +extern char *strchrnul(const char *s, int c) FAST_FUNC; +#endif + +#ifndef HAVE_STRSEP +extern char *strsep(char **stringp, const char *delim) FAST_FUNC; +#endif + +#ifndef HAVE_STRSIGNAL +/* Not exactly the same: instead of "Stopped" it shows "STOP" etc */ +# define strsignal(sig) get_signame(sig) +#endif + +#ifndef HAVE_USLEEP +extern int usleep(unsigned) FAST_FUNC; +#endif + +#ifndef HAVE_VASPRINTF +extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC; +#endif + +#ifndef HAVE_GETLINE +# include <stdio.h> /* for FILE */ +# include <sys/types.h> /* size_t */ +extern ssize_t getline(char **lineptr, size_t *n, FILE *stream) FAST_FUNC; +#endif + +#endif @@ -0,0 +1,72 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" + +ssize_t FAST_FUNC safe_read(int fd, void *buf, size_t count) +{ + ssize_t n; + + do { + n = read(fd, buf, count); + } while (n < 0 && errno == EINTR); + + return n; +} + +/* + * Read all of the supplied buffer from a file. + * This does multiple reads as necessary. + * Returns the amount read, or -1 on an error. + * A short read is returned on an end of file. + */ +ssize_t FAST_FUNC full_read(int fd, void *buf, size_t len) +{ + ssize_t cc; + ssize_t total; + + total = 0; + + while (len) { + cc = safe_read(fd, buf, len); + + if (cc < 0) { + if (total) { + /* we already have some! */ + /* user can do another read to know the error code */ + return total; + } + return cc; /* read() returns -1 on failure. */ + } + if (cc == 0) + break; + buf = ((char *)buf) + cc; + total += cc; + len -= cc; + } + + return total; +} + +ssize_t FAST_FUNC read_close(int fd, void *buf, size_t size) +{ + /*int e;*/ + size = full_read(fd, buf, size); + /*e = errno;*/ + close(fd); + /*errno = e;*/ + return size; +} + +ssize_t FAST_FUNC open_read_close(const char *filename, void *buf, size_t size) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) + return fd; + return read_close(fd, buf, size); +} diff --git a/read_key.c b/read_key.c new file mode 100644 index 0000000..9517868 --- /dev/null +++ b/read_key.c @@ -0,0 +1,293 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2008 Rob Landley <rob@landley.net> + * Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com> + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include "libbb.h" + +int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout) +{ + struct pollfd pfd; + const char *seq; + int n; + + /* Known escape sequences for cursor and function keys. + * See "Xterm Control Sequences" + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * Array should be sorted from shortest to longest. + */ + static const char esccmds[] ALIGN1 = { + '\x7f' |0x80,KEYCODE_ALT_BACKSPACE, + '\b' |0x80,KEYCODE_ALT_BACKSPACE, + 'd' |0x80,KEYCODE_ALT_D , + /* lineedit mimics bash: Alt-f and Alt-b are forward/backward + * word jumps. We cheat here and make them return ALT_LEFT/RIGHT + * keycodes. This way, lineedit need no special code to handle them. + * If we'll need to distinguish them, introduce new ALT_F/B keycodes, + * and update lineedit to react to them. + */ + 'f' |0x80,KEYCODE_ALT_RIGHT, + 'b' |0x80,KEYCODE_ALT_LEFT, + 'O','A' |0x80,KEYCODE_UP , + 'O','B' |0x80,KEYCODE_DOWN , + 'O','C' |0x80,KEYCODE_RIGHT , + 'O','D' |0x80,KEYCODE_LEFT , + 'O','H' |0x80,KEYCODE_HOME , + 'O','F' |0x80,KEYCODE_END , +#if 0 + 'O','P' |0x80,KEYCODE_FUN1 , + /* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */ + /* ESC [ O 1 ; 2 P - Shift-F1 */ + /* ESC [ O 1 ; 3 P - Alt-F1 */ + /* ESC [ O 1 ; 4 P - Alt-Shift-F1 */ + /* ESC [ O 1 ; 5 P - Ctrl-F1 */ + /* ESC [ O 1 ; 6 P - Ctrl-Shift-F1 */ + 'O','Q' |0x80,KEYCODE_FUN2 , + 'O','R' |0x80,KEYCODE_FUN3 , + 'O','S' |0x80,KEYCODE_FUN4 , +#endif + '[','A' |0x80,KEYCODE_UP , + '[','B' |0x80,KEYCODE_DOWN , + '[','C' |0x80,KEYCODE_RIGHT , + '[','D' |0x80,KEYCODE_LEFT , + /* ESC [ 1 ; 2 x, where x = A/B/C/D: Shift-<arrow> */ + /* ESC [ 1 ; 3 x, where x = A/B/C/D: Alt-<arrow> - implemented below */ + /* ESC [ 1 ; 4 x, where x = A/B/C/D: Alt-Shift-<arrow> */ + /* ESC [ 1 ; 5 x, where x = A/B/C/D: Ctrl-<arrow> - implemented below */ + /* ESC [ 1 ; 6 x, where x = A/B/C/D: Ctrl-Shift-<arrow> */ + /* ESC [ 1 ; 7 x, where x = A/B/C/D: Ctrl-Alt-<arrow> */ + /* ESC [ 1 ; 8 x, where x = A/B/C/D: Ctrl-Alt-Shift-<arrow> */ + '[','H' |0x80,KEYCODE_HOME , /* xterm */ + '[','F' |0x80,KEYCODE_END , /* xterm */ + /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home (End similarly?) */ + /* '[','Z' |0x80,KEYCODE_SHIFT_TAB, */ + '[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ + '[','2','~' |0x80,KEYCODE_INSERT , + /* ESC [ 2 ; 3 ~ - Alt-Insert */ + '[','3','~' |0x80,KEYCODE_DELETE , + /* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */ + /* ESC [ 3 ; 3 ~ - Alt-Delete */ + /* ESC [ 3 ; 5 ~ - Ctrl-Delete */ + '[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ + '[','5','~' |0x80,KEYCODE_PAGEUP , + /* ESC [ 5 ; 3 ~ - Alt-PgUp */ + /* ESC [ 5 ; 5 ~ - Ctrl-PgUp */ + /* ESC [ 5 ; 7 ~ - Ctrl-Alt-PgUp */ + '[','6','~' |0x80,KEYCODE_PAGEDOWN, + '[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ + '[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ +#if 0 + '[','1','1','~'|0x80,KEYCODE_FUN1 , /* old xterm, deprecated by ESC O P */ + '[','1','2','~'|0x80,KEYCODE_FUN2 , /* old xterm... */ + '[','1','3','~'|0x80,KEYCODE_FUN3 , /* old xterm... */ + '[','1','4','~'|0x80,KEYCODE_FUN4 , /* old xterm... */ + '[','1','5','~'|0x80,KEYCODE_FUN5 , + /* [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5 */ + '[','1','7','~'|0x80,KEYCODE_FUN6 , + '[','1','8','~'|0x80,KEYCODE_FUN7 , + '[','1','9','~'|0x80,KEYCODE_FUN8 , + '[','2','0','~'|0x80,KEYCODE_FUN9 , + '[','2','1','~'|0x80,KEYCODE_FUN10 , + '[','2','3','~'|0x80,KEYCODE_FUN11 , + '[','2','4','~'|0x80,KEYCODE_FUN12 , + /* ESC [ 2 4 ; 2 ~ - Shift-F12 */ + /* ESC [ 2 4 ; 3 ~ - Alt-F12 */ + /* ESC [ 2 4 ; 4 ~ - Alt-Shift-F12 */ + /* ESC [ 2 4 ; 5 ~ - Ctrl-F12 */ + /* ESC [ 2 4 ; 6 ~ - Ctrl-Shift-F12 */ +#endif + /* '[','1',';','5','A' |0x80,KEYCODE_CTRL_UP , - unused */ + /* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused */ + '[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT, + '[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT , + /* '[','1',';','3','A' |0x80,KEYCODE_ALT_UP , - unused */ + /* '[','1',';','3','B' |0x80,KEYCODE_ALT_DOWN , - unused */ + '[','1',';','3','C' |0x80,KEYCODE_ALT_RIGHT, + '[','1',';','3','D' |0x80,KEYCODE_ALT_LEFT , + /* '[','3',';','3','~' |0x80,KEYCODE_ALT_DELETE, - unused */ + 0 + }; + + pfd.fd = fd; + pfd.events = POLLIN; + + buffer++; /* saved chars counter is in buffer[-1] now */ + + start_over: + errno = 0; + n = (unsigned char)buffer[-1]; + if (n == 0) { + /* If no data, wait for input. + * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful + * if fd can be in non-blocking mode. + */ + if (timeout >= -1) { + if (safe_poll(&pfd, 1, timeout) == 0) { + /* Timed out */ + errno = EAGAIN; + return -1; + } + } + /* It is tempting to read more than one byte here, + * but it breaks pasting. Example: at shell prompt, + * user presses "c","a","t" and then pastes "\nline\n". + * When we were reading 3 bytes here, we were eating + * "li" too, and cat was getting wrong input. + */ + n = safe_read(fd, buffer, 1); + if (n <= 0) + return -1; + } + + { + unsigned char c = buffer[0]; + n--; + if (n) + memmove(buffer, buffer + 1, n); + /* Only ESC starts ESC sequences */ + if (c != 27) { + buffer[-1] = n; + return c; + } + } + + /* Loop through known ESC sequences */ + seq = esccmds; + while (*seq != '\0') { + /* n - position in sequence we did not read yet */ + int i = 0; /* position in sequence to compare */ + + /* Loop through chars in this sequence */ + while (1) { + /* So far escape sequence matched up to [i-1] */ + if (n <= i) { + /* Need more chars, read another one if it wouldn't block. + * Note that escape sequences come in as a unit, + * so if we block for long it's not really an escape sequence. + * Timeout is needed to reconnect escape sequences + * split up by transmission over a serial console. */ + if (safe_poll(&pfd, 1, 50) == 0) { + /* No more data! + * Array is sorted from shortest to longest, + * we can't match anything later in array - + * anything later is longer than this seq. + * Break out of both loops. */ + goto got_all; + } + errno = 0; + if (safe_read(fd, buffer + n, 1) <= 0) { + /* If EAGAIN, then fd is O_NONBLOCK and poll lied: + * in fact, there is no data. */ + if (errno != EAGAIN) { + /* otherwise: it's EOF/error */ + buffer[-1] = 0; + return -1; + } + goto got_all; + } + n++; + } + if (buffer[i] != (seq[i] & 0x7f)) { + /* This seq doesn't match, go to next */ + seq += i; + /* Forward to last char */ + while (!(*seq & 0x80)) + seq++; + /* Skip it and the keycode which follows */ + seq += 2; + break; + } + if (seq[i] & 0x80) { + /* Entire seq matched */ + n = 0; + /* n -= i; memmove(...); + * would be more correct, + * but we never read ahead that much, + * and n == i here. */ + buffer[-1] = 0; + return (signed char)seq[i+1]; + } + i++; + } + } + /* We did not find matching sequence. + * We possibly read and stored more input in buffer[] by now. + * n = bytes read. Try to read more until we time out. + */ + while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ + if (safe_poll(&pfd, 1, 50) == 0) { + /* No more data! */ + break; + } + errno = 0; + if (safe_read(fd, buffer + n, 1) <= 0) { + /* If EAGAIN, then fd is O_NONBLOCK and poll lied: + * in fact, there is no data. */ + if (errno != EAGAIN) { + /* otherwise: it's EOF/error */ + buffer[-1] = 0; + return -1; + } + break; + } + n++; + /* Try to decipher "ESC [ NNN ; NNN R" sequence */ + if ((ENABLE_FEATURE_EDITING_ASK_TERMINAL + || ENABLE_FEATURE_VI_ASK_TERMINAL + || ENABLE_FEATURE_LESS_ASK_TERMINAL + ) + && n >= 5 + && buffer[0] == '[' + && buffer[n-1] == 'R' + && isdigit(buffer[1]) + ) { + char *end; + unsigned long row, col; + + row = strtoul(buffer + 1, &end, 10); + if (*end != ';' || !isdigit(end[1])) + continue; + col = strtoul(end + 1, &end, 10); + if (*end != 'R') + continue; + if (row < 1 || col < 1 || (row | col) > 0x7fff) + continue; + + buffer[-1] = 0; + /* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */ + col |= (((-1 << 15) | row) << 16); + /* Return it in high-order word */ + return ((int64_t) col << 32) | (uint32_t)KEYCODE_CURSOR_POS; + } + } + got_all: + + if (n <= 1) { + /* Alt-x is usually returned as ESC x. + * Report ESC, x is remembered for the next call. + */ + buffer[-1] = n; + return 27; + } + + /* We were doing "buffer[-1] = n; return c;" here, but this results + * in unknown key sequences being interpreted as ESC + garbage. + * This was not useful. Pretend there was no key pressed, + * go and wait for a new keypress: + */ + buffer[-1] = 0; + goto start_over; +} + +void FAST_FUNC read_key_ungets(char *buffer, const char *str, unsigned len) +{ + unsigned cur_len = (unsigned char)buffer[0]; + if (len > KEYCODE_BUFFER_SIZE-1 - cur_len) + len = KEYCODE_BUFFER_SIZE-1 - cur_len; + memcpy(buffer + 1 + cur_len, str, len); + buffer[0] += len; +} diff --git a/safe_poll.c b/safe_poll.c new file mode 100644 index 0000000..b492a81 --- /dev/null +++ b/safe_poll.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2007 by Denys Vlasenko <vda.linux@googlemail.com> + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +#include "libbb.h" + +/* Wrapper which restarts poll on EINTR or ENOMEM. + * On other errors does perror("poll") and returns. + * Warning! May take longer than timeout_ms to return! */ +int FAST_FUNC safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout) +{ + while (1) { + int n = poll(ufds, nfds, timeout); + if (n >= 0) + return n; + /* Make sure we inch towards completion */ + if (timeout > 0) + timeout--; + /* E.g. strace causes poll to return this */ + if (errno == EINTR) + continue; + /* Kernel is very low on memory. Retry. */ + /* I doubt many callers would handle this correctly! */ + if (errno == ENOMEM) + continue; + bb_perror_msg("poll"); + return n; + } +} diff --git a/safe_write.c b/safe_write.c new file mode 100644 index 0000000..aad50f5 --- /dev/null +++ b/safe_write.c @@ -0,0 +1,29 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" + +ssize_t FAST_FUNC safe_write(int fd, const void *buf, size_t count) +{ + ssize_t n; + + for (;;) { + n = write(fd, buf, count); + if (n >= 0 || errno != EINTR) + break; + /* Some callers set errno=0, are upset when they see EINTR. + * Returning EINTR is wrong since we retry write(), + * the "error" was transient. + */ + errno = 0; + /* repeat the write() */ + } + + return n; +} diff --git a/verror_msg.c b/verror_msg.c new file mode 100644 index 0000000..00f01ce --- /dev/null +++ b/verror_msg.c @@ -0,0 +1,182 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#if ENABLE_FEATURE_SYSLOG +# include <syslog.h> +#endif + +#if ENABLE_FEATURE_SYSLOG +smallint syslog_level = LOG_ERR; +#endif +smallint logmode = LOGMODE_STDIO; +const char *msg_eol = "\n"; + +void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr) +{ + char *msg, *msg1; + char stack_msg[80]; + int applet_len, strerr_len, msgeol_len, used; + + if (!logmode) + return; + + if (!s) /* nomsg[_and_die] uses NULL fmt */ + s = ""; /* some libc don't like printf(NULL) */ + + applet_len = strlen(applet_name) + 2; /* "applet: " */ + strerr_len = strerr ? strlen(strerr) : 0; + msgeol_len = strlen(msg_eol); + + /* This costs ~90 bytes of code, but avoids costly + * malloc()[in vasprintf]+realloc()+memmove()+free() in 99% of cases. + * ~40% speedup. + */ + if ((int)sizeof(stack_msg) - applet_len > 0) { + va_list p2; + + /* It is not portable to use va_list twice, need to va_copy it */ + va_copy(p2, p); + used = vsnprintf(stack_msg + applet_len, (int)sizeof(stack_msg) - applet_len, s, p2); + va_end(p2); + msg = stack_msg; + used += applet_len; + if (used < (int)sizeof(stack_msg) - 3 - msgeol_len - strerr_len) + goto add_pfx_and_sfx; + } + + used = vasprintf(&msg, s, p); + if (used < 0) + return; + + /* This is ugly and costs +60 bytes compared to multiple + * fprintf's, but is guaranteed to do a single write. + * This is needed for e.g. httpd logging, when multiple + * children can produce log messages simultaneously. */ + + /* can't use xrealloc: it calls error_msg on failure, + * that may result in a recursion */ + /* +3 is for ": " before strerr and for terminating NUL */ + msg1 = realloc(msg, applet_len + used + strerr_len + msgeol_len + 3); + if (!msg1) { + msg[used++] = '\n'; /* overwrites NUL */ + applet_len = 0; + } else { + msg = msg1; + /* TODO: maybe use writev instead of memmoving? Need full_writev? */ + memmove(msg + applet_len, msg, used); + used += applet_len; + add_pfx_and_sfx: + strcpy(msg, applet_name); + msg[applet_len - 2] = ':'; + msg[applet_len - 1] = ' '; + if (strerr) { + if (s[0]) { /* not perror_nomsg? */ + msg[used++] = ':'; + msg[used++] = ' '; + } + strcpy(&msg[used], strerr); + used += strerr_len; + } + strcpy(&msg[used], msg_eol); + used += msgeol_len; + } + + if (logmode & LOGMODE_STDIO) { + fflush(NULL); + full_write(STDERR_FILENO, msg, used); + } +#if ENABLE_FEATURE_SYSLOG + if (logmode & LOGMODE_SYSLOG) { + syslog(syslog_level, "%s", msg + applet_len); + } +#endif + if (msg != stack_msg) + free(msg); +} + +#ifdef VERSION_WITH_WRITEV +/* Code size is approximately the same, but currently it's the only user + * of writev in entire bbox. __libc_writev in uclibc is ~50 bytes. */ +void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr) +{ + int strerr_len, msgeol_len; + struct iovec iov[3]; + +#define used (iov[2].iov_len) +#define msgv (iov[2].iov_base) +#define msgc ((char*)(iov[2].iov_base)) +#define msgptr (&(iov[2].iov_base)) + + if (!logmode) + return; + + if (!s) /* nomsg[_and_die] uses NULL fmt */ + s = ""; /* some libc don't like printf(NULL) */ + + /* Prevent "derefing type-punned ptr will break aliasing rules" */ + used = vasprintf((char**)(void*)msgptr, s, p); + if (used < 0) + return; + + /* This is ugly and costs +60 bytes compared to multiple + * fprintf's, but is guaranteed to do a single write. + * This is needed for e.g. httpd logging, when multiple + * children can produce log messages simultaneously. */ + + strerr_len = strerr ? strlen(strerr) : 0; + msgeol_len = strlen(msg_eol); + /* +3 is for ": " before strerr and for terminating NUL */ + msgv = xrealloc(msgv, used + strerr_len + msgeol_len + 3); + if (strerr) { + msgc[used++] = ':'; + msgc[used++] = ' '; + strcpy(msgc + used, strerr); + used += strerr_len; + } + strcpy(msgc + used, msg_eol); + used += msgeol_len; + + if (logmode & LOGMODE_STDIO) { + iov[0].iov_base = (char*)applet_name; + iov[0].iov_len = strlen(applet_name); + iov[1].iov_base = (char*)": "; + iov[1].iov_len = 2; + /*iov[2].iov_base = msgc;*/ + /*iov[2].iov_len = used;*/ + fflush_all(); + writev(STDERR_FILENO, iov, 3); + } +# if ENABLE_FEATURE_SYSLOG + if (logmode & LOGMODE_SYSLOG) { + syslog(LOG_ERR, "%s", msgc); + } +# endif + free(msgc); +} +#endif + + +void FAST_FUNC bb_error_msg_and_die(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p, NULL); + va_end(p); + xfunc_die(); +} + +void FAST_FUNC bb_error_msg(const char *s, ...) +{ + va_list p; + + va_start(p, s); + bb_verror_msg(s, p, NULL); + va_end(p); +} diff --git a/xatonum.h b/xatonum.h new file mode 100644 index 0000000..45ebbfc --- /dev/null +++ b/xatonum.h @@ -0,0 +1,185 @@ +/* vi: set sw=4 ts=4: */ +/* + * ascii-to-numbers implementations for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN + +/* Provides extern declarations of functions */ +#define DECLARE_STR_CONV(type, T, UT) \ +\ +unsigned type xstrto##UT##_range_sfx(const char *str, int b, unsigned type l, unsigned type u, const struct suffix_mult *sfx) FAST_FUNC; \ +unsigned type xstrto##UT##_range(const char *str, int b, unsigned type l, unsigned type u) FAST_FUNC; \ +unsigned type xstrto##UT##_sfx(const char *str, int b, const struct suffix_mult *sfx) FAST_FUNC; \ +unsigned type xstrto##UT(const char *str, int b) FAST_FUNC; \ +unsigned type xato##UT##_range_sfx(const char *str, unsigned type l, unsigned type u, const struct suffix_mult *sfx) FAST_FUNC; \ +unsigned type xato##UT##_range(const char *str, unsigned type l, unsigned type u) FAST_FUNC; \ +unsigned type xato##UT##_sfx(const char *str, const struct suffix_mult *sfx) FAST_FUNC; \ +unsigned type xato##UT(const char *str) FAST_FUNC; \ +type xstrto##T##_range_sfx(const char *str, int b, type l, type u, const struct suffix_mult *sfx) FAST_FUNC; \ +type xstrto##T##_range(const char *str, int b, type l, type u) FAST_FUNC; \ +type xstrto##T(const char *str, int b) FAST_FUNC; \ +type xato##T##_range_sfx(const char *str, type l, type u, const struct suffix_mult *sfx) FAST_FUNC; \ +type xato##T##_range(const char *str, type l, type u) FAST_FUNC; \ +type xato##T##_sfx(const char *str, const struct suffix_mult *sfx) FAST_FUNC; \ +type xato##T(const char *str) FAST_FUNC; \ + +/* Unsigned long long functions always exist */ +DECLARE_STR_CONV(long long, ll, ull) + + +/* Provides inline definitions of functions */ +/* (useful for mapping them to the type of the same width) */ +#define DEFINE_EQUIV_STR_CONV(narrow, N, W, UN, UW) \ +\ +static ALWAYS_INLINE \ +unsigned narrow xstrto##UN##_range_sfx(const char *str, int b, unsigned narrow l, unsigned narrow u, const struct suffix_mult *sfx) \ +{ return xstrto##UW##_range_sfx(str, b, l, u, sfx); } \ +static ALWAYS_INLINE \ +unsigned narrow xstrto##UN##_range(const char *str, int b, unsigned narrow l, unsigned narrow u) \ +{ return xstrto##UW##_range(str, b, l, u); } \ +static ALWAYS_INLINE \ +unsigned narrow xstrto##UN##_sfx(const char *str, int b, const struct suffix_mult *sfx) \ +{ return xstrto##UW##_sfx(str, b, sfx); } \ +static ALWAYS_INLINE \ +unsigned narrow xstrto##UN(const char *str, int b) \ +{ return xstrto##UW(str, b); } \ +static ALWAYS_INLINE \ +unsigned narrow xato##UN##_range_sfx(const char *str, unsigned narrow l, unsigned narrow u, const struct suffix_mult *sfx) \ +{ return xato##UW##_range_sfx(str, l, u, sfx); } \ +static ALWAYS_INLINE \ +unsigned narrow xato##UN##_range(const char *str, unsigned narrow l, unsigned narrow u) \ +{ return xato##UW##_range(str, l, u); } \ +static ALWAYS_INLINE \ +unsigned narrow xato##UN##_sfx(const char *str, const struct suffix_mult *sfx) \ +{ return xato##UW##_sfx(str, sfx); } \ +static ALWAYS_INLINE \ +unsigned narrow xato##UN(const char *str) \ +{ return xato##UW(str); } \ +static ALWAYS_INLINE \ +narrow xstrto##N##_range_sfx(const char *str, int b, narrow l, narrow u, const struct suffix_mult *sfx) \ +{ return xstrto##W##_range_sfx(str, b, l, u, sfx); } \ +static ALWAYS_INLINE \ +narrow xstrto##N##_range(const char *str, int b, narrow l, narrow u) \ +{ return xstrto##W##_range(str, b, l, u); } \ +static ALWAYS_INLINE \ +narrow xstrto##N(const char *str, int b) \ +{ return xstrto##W(str, b); } \ +static ALWAYS_INLINE \ +narrow xato##N##_range_sfx(const char *str, narrow l, narrow u, const struct suffix_mult *sfx) \ +{ return xato##W##_range_sfx(str, l, u, sfx); } \ +static ALWAYS_INLINE \ +narrow xato##N##_range(const char *str, narrow l, narrow u) \ +{ return xato##W##_range(str, l, u); } \ +static ALWAYS_INLINE \ +narrow xato##N##_sfx(const char *str, const struct suffix_mult *sfx) \ +{ return xato##W##_sfx(str, sfx); } \ +static ALWAYS_INLINE \ +narrow xato##N(const char *str) \ +{ return xato##W(str); } \ + +/* If long == long long, then just map them one-to-one */ +#if ULONG_MAX == ULLONG_MAX +DEFINE_EQUIV_STR_CONV(long, l, ll, ul, ull) +#else +/* Else provide extern defs */ +DECLARE_STR_CONV(long, l, ul) +#endif + +/* Same for int -> [long] long */ +#if UINT_MAX == ULLONG_MAX +DEFINE_EQUIV_STR_CONV(int, i, ll, u, ull) +#elif UINT_MAX == ULONG_MAX +DEFINE_EQUIV_STR_CONV(int, i, l, u, ul) +#else +DECLARE_STR_CONV(int, i, u) +#endif + +/* Specialized */ + +uint32_t BUG_xatou32_unimplemented(void); +static ALWAYS_INLINE uint32_t xatou32(const char *numstr) +{ + if (UINT_MAX == 0xffffffff) + return xatou(numstr); + if (ULONG_MAX == 0xffffffff) + return xatoul(numstr); + return BUG_xatou32_unimplemented(); +} + +/* Non-aborting kind of convertors: bb_strto[u][l]l */ + +/* On exit: errno = 0 only if there was non-empty, '\0' terminated value + * errno = EINVAL if value was not '\0' terminated, but otherwise ok + * Return value is still valid, caller should just check whether end[0] + * is a valid terminating char for particular case. OTOH, if caller + * requires '\0' terminated input, [s]he can just check errno == 0. + * errno = ERANGE if value had alphanumeric terminating char ("1234abcg"). + * errno = ERANGE if value is out of range, missing, etc. + * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok ) + * return value is all-ones in this case. + */ + +unsigned long long bb_strtoull(const char *arg, char **endp, int base) FAST_FUNC; +long long bb_strtoll(const char *arg, char **endp, int base) FAST_FUNC; + +#if ULONG_MAX == ULLONG_MAX +static ALWAYS_INLINE +unsigned long bb_strtoul(const char *arg, char **endp, int base) +{ return bb_strtoull(arg, endp, base); } +static ALWAYS_INLINE +long bb_strtol(const char *arg, char **endp, int base) +{ return bb_strtoll(arg, endp, base); } +#else +unsigned long bb_strtoul(const char *arg, char **endp, int base) FAST_FUNC; +long bb_strtol(const char *arg, char **endp, int base) FAST_FUNC; +#endif + +#if UINT_MAX == ULLONG_MAX +static ALWAYS_INLINE +unsigned bb_strtou(const char *arg, char **endp, int base) +{ return bb_strtoull(arg, endp, base); } +static ALWAYS_INLINE +int bb_strtoi(const char *arg, char **endp, int base) +{ return bb_strtoll(arg, endp, base); } +#elif UINT_MAX == ULONG_MAX +static ALWAYS_INLINE +unsigned bb_strtou(const char *arg, char **endp, int base) +{ return bb_strtoul(arg, endp, base); } +static ALWAYS_INLINE +int bb_strtoi(const char *arg, char **endp, int base) +{ return bb_strtol(arg, endp, base); } +#else +unsigned bb_strtou(const char *arg, char **endp, int base) FAST_FUNC; +int bb_strtoi(const char *arg, char **endp, int base) FAST_FUNC; +#endif + +uint32_t BUG_bb_strtou32_unimplemented(void); +static ALWAYS_INLINE +uint32_t bb_strtou32(const char *arg, char **endp, int base) +{ + if (sizeof(uint32_t) == sizeof(unsigned)) + return bb_strtou(arg, endp, base); + if (sizeof(uint32_t) == sizeof(unsigned long)) + return bb_strtoul(arg, endp, base); + return BUG_bb_strtou32_unimplemented(); +} +static ALWAYS_INLINE +int32_t bb_strtoi32(const char *arg, char **endp, int base) +{ + if (sizeof(int32_t) == sizeof(int)) + return bb_strtoi(arg, endp, base); + if (sizeof(int32_t) == sizeof(long)) + return bb_strtol(arg, endp, base); + return BUG_bb_strtou32_unimplemented(); +} + +/* Floating point */ + +double bb_strtod(const char *arg, char **endp) FAST_FUNC; + +POP_SAVED_FUNCTION_VISIBILITY |
