summaryrefslogtreecommitdiff
path: root/nanobox.c
diff options
context:
space:
mode:
Diffstat (limited to 'nanobox.c')
-rw-r--r--nanobox.c255
1 files changed, 185 insertions, 70 deletions
diff --git a/nanobox.c b/nanobox.c
index 2298d5a..38125d8 100644
--- a/nanobox.c
+++ b/nanobox.c
@@ -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;
+}