summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-02-02 10:25:53 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-02-02 10:25:53 -0600
commitc9b74c175ad4502245cace41464aba69f8a1eaec (patch)
tree7e2b4789194c6b3e6865e24aee5bac6a7c0fe484
parente364cc0f190f65ca37a08fbba3daa577d23cf51b (diff)
downloadnuttx-c9b74c175ad4502245cace41464aba69f8a1eaec.tar.gz
nuttx-c9b74c175ad4502245cace41464aba69f8a1eaec.tar.bz2
nuttx-c9b74c175ad4502245cace41464aba69f8a1eaec.zip
Add an EMACS-like command line editor that can be used wit NSH
-rw-r--r--apps/ChangeLog.txt7
-rw-r--r--apps/include/cle.h87
-rw-r--r--apps/nshlib/Kconfig30
-rw-r--r--apps/nshlib/nsh_session.c13
-rw-r--r--apps/nshlib/nsh_stdsession.c13
-rw-r--r--apps/system/Kconfig4
-rw-r--r--apps/system/Make.defs4
-rw-r--r--apps/system/Makefile2
-rw-r--r--apps/system/cle/.gitignore11
-rw-r--r--apps/system/cle/Kconfig37
-rw-r--r--apps/system/cle/Makefile103
-rw-r--r--apps/system/cle/cle.c929
12 files changed, 1232 insertions, 8 deletions
diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt
index 146d0943b..a9450b5d3 100644
--- a/apps/ChangeLog.txt
+++ b/apps/ChangeLog.txt
@@ -811,3 +811,10 @@
untested conditions (2014-1-21).
6.34 2014-xx-xx Gregory Nutt <gnutt@nuttx.org>
+
+ * apps/system/cle: Add a EMACS-like command line editor. This CLE,
+ is really more like readline than the NuttX readline is! Not fully
+ fully on initial checkout (2014-02-02).
+ * apps/nshlib: Use of the standard tiney readline (about .25KB) is now
+ an option and can be replaces with the EMACX-like CLE (about 2KB)
+ (2014-02-02).
diff --git a/apps/include/cle.h b/apps/include/cle.h
new file mode 100644
index 000000000..5cb903212
--- /dev/null
+++ b/apps/include/cle.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+ * apps/include/cle.h
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __APPS_INCLUDE_CLE_H
+#define __APPS_INCLUDE_CLE_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdio.h>
+
+#ifdef CONFIG_SYSTEM_CLE
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cle
+ *
+ * Description:
+ * EMACS-like command line editor. This is actually more like readline
+ * than is the NuttX readline!
+ *
+ ****************************************************************************/
+
+int cle(FAR char *line, uint16_t linelen, FILE *instream, FILE *outstream);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_SYSTEM_CLE */
+#endif /* __APPS_INCLUDE_CLE_H */
diff --git a/apps/nshlib/Kconfig b/apps/nshlib/Kconfig
index 7fb6a52b3..8a9d1e900 100644
--- a/apps/nshlib/Kconfig
+++ b/apps/nshlib/Kconfig
@@ -6,13 +6,37 @@
config NSH_LIBRARY
bool "NSH Library"
default n
- select SYSTEM_READLINE
---help---
- Build the NSH support library. This is used, for example, by examples/nsh
- in order to implement the full NuttShell (NSH).
+ Build the NSH support library. This is used, for example, by
+ examples/nsh in order to implement the full NuttShell (NSH).
if NSH_LIBRARY
+choice
+ prompt "Command Line Editor"
+ default NSH_READLINE
+
+config NSH_READLINE
+ bool "Minimal readline()"
+ select SYSTEM_READLINE
+ ---help---
+ Selects the minimal implementation of readline(). This minimal
+ implementation provides on backspace for command line editing.
+
+config NSH_CLE
+ bool "Command Line Editor"
+ select SYSTEM_CLE
+ ---help---
+ Selects the more extensive, EMACS-like command line editor.
+ Select this option only if (1) you don't mind a modest increase
+ in the FLASH footprint, and (2) you work with a terminal that
+ support VT100 editing commands.
+
+ Selecting this option will add probably 1.5-2KB to the FLASH
+ footprint.
+
+endchoice
+
config NSH_BUILTIN_APPS
bool "Enable built-in applications"
default n
diff --git a/apps/nshlib/nsh_session.c b/apps/nshlib/nsh_session.c
index 2272b1e30..de6710bfe 100644
--- a/apps/nshlib/nsh_session.c
+++ b/apps/nshlib/nsh_session.c
@@ -1,7 +1,7 @@
/****************************************************************************
* apps/nshlib/nsh_session.c
*
- * Copyright (C) 2007-2009, 2011-2013 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2007-2009, 2011-2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,11 @@
#include <stdio.h>
#include <stdlib.h>
-#include <apps/readline.h>
+#ifdef CONFIG_NSH_CLE
+# include <apps/cle.h>
+#else
+# include <apps/readline.h>
+#endif
#include "nsh.h"
#include "nsh_console.h"
@@ -133,8 +137,13 @@ int nsh_session(FAR struct console_stdio_s *pstate)
* or any read failure.
*/
+#ifdef CONFIG_NSH_CLE
+ ret = cle(pstate->cn_line, CONFIG_NSH_LINELEN,
+ INSTREAM(pstate), OUTSTREAM(pstate));
+#else
ret = readline(pstate->cn_line, CONFIG_NSH_LINELEN,
INSTREAM(pstate), OUTSTREAM(pstate));
+#endif
if (ret != EOF)
{
/* Parse process the command */
diff --git a/apps/nshlib/nsh_stdsession.c b/apps/nshlib/nsh_stdsession.c
index 11fae7db7..07def5b3b 100644
--- a/apps/nshlib/nsh_stdsession.c
+++ b/apps/nshlib/nsh_stdsession.c
@@ -1,7 +1,7 @@
/****************************************************************************
* apps/nshlib/nsh_stdsession.c
*
- * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2013-2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,11 @@
#include <stdio.h>
#include <stdlib.h>
-#include <apps/readline.h>
+#ifdef CONFIG_NSH_CLE
+# include <apps/cle.h>
+#else
+# include <apps/readline.h>
+#endif
#include "nsh.h"
#include "nsh_console.h"
@@ -124,7 +128,12 @@ int nsh_session(FAR struct console_stdio_s *pstate)
* or any read failure.
*/
+#ifdef CONFIG_NSH_CLE
+ ret = cle(pstate->cn_line, CONFIG_NSH_LINELEN,
+ stdin, stdout);
+#else
ret = std_readline(pstate->cn_line, CONFIG_NSH_LINELEN);
+#endif
if (ret != EOF)
{
/* Parse process the command */
diff --git a/apps/system/Kconfig b/apps/system/Kconfig
index 15836f1f2..e7d00bc2e 100644
--- a/apps/system/Kconfig
+++ b/apps/system/Kconfig
@@ -63,6 +63,10 @@ menu "USB Monitor"
source "$APPSDIR/system/usbmonitor/Kconfig"
endmenu
+menu "EMACS-like Command Line Editor"
+source "$APPSDIR/system/cle/Kconfig"
+endmenu
+
menu "VI Work-Alike Editor"
source "$APPSDIR/system/vi/Kconfig"
endmenu
diff --git a/apps/system/Make.defs b/apps/system/Make.defs
index c0af2ec15..33a40c0aa 100644
--- a/apps/system/Make.defs
+++ b/apps/system/Make.defs
@@ -102,6 +102,10 @@ ifeq ($(CONFIG_SYSTEM_USBMSC),y)
CONFIGURED_APPS += system/usbmsc
endif
+ifeq ($(CONFIG_SYSTEM_CLE),y)
+CONFIGURED_APPS += system/cle
+endif
+
ifeq ($(CONFIG_SYSTEM_VI),y)
CONFIGURED_APPS += system/vi
endif
diff --git a/apps/system/Makefile b/apps/system/Makefile
index 08a67d22d..149c7a4ab 100644
--- a/apps/system/Makefile
+++ b/apps/system/Makefile
@@ -37,7 +37,7 @@
# Sub-directories containing system task
-SUBDIRS = cdcacm composite flash_eraseall free i2c inifile install
+SUBDIRS = cdcacm cle composite flash_eraseall free i2c inifile install
SUBDIRS += nxplayer poweroff ramtest ramtron readline sdcard stackmonitor
SUBDIRS += sysinfo usbmonitor usbmsc vi zmodem
diff --git a/apps/system/cle/.gitignore b/apps/system/cle/.gitignore
new file mode 100644
index 000000000..83bd7b811
--- /dev/null
+++ b/apps/system/cle/.gitignore
@@ -0,0 +1,11 @@
+/Make.dep
+/.depend
+/.built
+/*.asm
+/*.rel
+/*.lst
+/*.sym
+/*.adb
+/*.lib
+/*.src
+/*.obj
diff --git a/apps/system/cle/Kconfig b/apps/system/cle/Kconfig
new file mode 100644
index 000000000..d846a0cf8
--- /dev/null
+++ b/apps/system/cle/Kconfig
@@ -0,0 +1,37 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config SYSTEM_CLE
+ bool "Tiny VI work-alike command line editor"
+ default n
+ ---help---
+ Enable support for NuttX tiny EMACS-like command line editor.
+
+ Omitted features:
+ - No keypad cursor control support
+ - No word oriented operations.
+
+ Assumptions and Limitations:
+ - A VT100 host terminal is assumed.
+ - A fixed width character set (like Courier) is assumed
+
+ Memory Usage: Looks like 1.5-2KB
+
+if SYSTEM_CLE
+
+config SYSTEM_CLE_DEBUGLEVEL
+ int "Debug level"
+ default 0
+ range 0 2
+ ---help---
+ 0=Debug off; 1=Print errors on console; 2=Print debug information
+ on the console.
+
+ Debug output is generated with syslog. The editor works on
+ /dev/console. In order to get both a usable display and also
+ readable debug output, syslog'ing should sent to some device other
+ than /dev/console (which is the default).
+
+endif # SYSTEM_CLE
diff --git a/apps/system/cle/Makefile b/apps/system/cle/Makefile
new file mode 100644
index 000000000..cce4bb109
--- /dev/null
+++ b/apps/system/cle/Makefile
@@ -0,0 +1,103 @@
+############################################################################
+# apps/system/cle/Makefile
+#
+# Copyright (C) 2014 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+-include $(TOPDIR)/.config
+-include $(TOPDIR)/Make.defs
+include $(APPDIR)/Make.defs
+
+ifeq ($(WINTOOL),y)
+INCDIROPT = -w
+endif
+
+# EMACS-like Command Line Editor (CLE)
+
+ASRCS =
+CSRCS = cle.c
+
+AOBJS = $(ASRCS:.S=$(OBJEXT))
+COBJS = $(CSRCS:.c=$(OBJEXT))
+
+SRCS = $(ASRCS) $(CSRCS)
+OBJS = $(AOBJS) $(COBJS)
+
+ifeq ($(CONFIG_WINDOWS_NATIVE),y)
+ BIN = ..\..\libapps$(LIBEXT)
+else
+ifeq ($(WINTOOL),y)
+ BIN = ..\\..\\libapps$(LIBEXT)
+else
+ BIN = ../../libapps$(LIBEXT)
+endif
+endif
+
+ROOTDEPPATH = --dep-path .
+
+# Common build
+
+VPATH =
+
+all: .built
+.PHONY: context depend clean distclean
+
+$(AOBJS): %$(OBJEXT): %.S
+ $(call ASSEMBLE, $<, $@)
+
+$(COBJS): %$(OBJEXT): %.c
+ $(call COMPILE, $<, $@)
+
+.built: $(OBJS)
+ $(call ARCHIVE, $(BIN), $(OBJS))
+ $(Q) touch .built
+
+context:
+
+# Create dependencies
+
+.depend: Makefile $(SRCS)
+ $(Q) $(MKDEP) $(ROOTDEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep
+ $(Q) touch $@
+
+depend: .depend
+
+clean:
+ $(call DELFILE, .built)
+ $(call CLEAN)
+
+distclean: clean
+ $(call DELFILE, Make.dep)
+ $(call DELFILE, .depend)
+
+-include Make.dep
+
diff --git a/apps/system/cle/cle.c b/apps/system/cle/cle.c
new file mode 100644
index 000000000..6ed8d12aa
--- /dev/null
+++ b/apps/system/cle/cle.c
@@ -0,0 +1,929 @@
+/****************************************************************************
+ * apps/system/cle/cle.c
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/ascii.h>
+#include <nuttx/vt100.h>
+
+#include <apps/cle.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Some environments may return CR as end-of-line, others LF, and others
+ * both. If not specified, the logic here assumes either (but not both) as
+ * the default.
+ */
+
+#if defined(CONFIG_EOL_IS_CR)
+# undef CONFIG_EOL_IS_LF
+# undef CONFIG_EOL_IS_BOTH_CRLF
+# undef CONFIG_EOL_IS_EITHER_CRLF
+#elif defined(CONFIG_EOL_IS_LF)
+# undef CONFIG_EOL_IS_CR
+# undef CONFIG_EOL_IS_BOTH_CRLF
+# undef CONFIG_EOL_IS_EITHER_CRLF
+#elif defined(CONFIG_EOL_IS_BOTH_CRLF)
+# undef CONFIG_EOL_IS_CR
+# undef CONFIG_EOL_IS_LF
+# undef CONFIG_EOL_IS_EITHER_CRLF
+#elif defined(CONFIG_EOL_IS_EITHER_CRLF)
+# undef CONFIG_EOL_IS_CR
+# undef CONFIG_EOL_IS_LF
+# undef CONFIG_EOL_IS_BOTH_CRLF
+#else
+# undef CONFIG_EOL_IS_CR
+# undef CONFIG_EOL_IS_LF
+# undef CONFIG_EOL_IS_BOTH_CRLF
+# define CONFIG_EOL_IS_EITHER_CRLF 1
+#endif
+
+/* Control characters */
+
+#undef CTRL
+#define CTRL(a) ((a) & 0x1f)
+
+#define CLE_BEL(cle) cle_putch(cle,CTRL('G'))
+
+/* Sizes of things */
+
+#define TABSIZE 8 /* A TAB is eight characters */
+#define TABMASK 7 /* Mask for TAB alignment */
+#define NEXT_TAB(p) (((p) + TABSIZE) & ~TABMASK)
+
+/* Debug */
+
+#ifndef CONFIG_SYSEM_CLE_DEBUGLEVEL
+# define CONFIG_SYSEM_CLE_DEBUGLEVEL 0
+#endif
+
+#ifdef CONFIG_CPP_HAVE_VARARGS
+# if CONFIG_SYSEM_CLE_DEBUGLEVEL > 0
+# define vidbg(format, arg...) \
+ syslog(EXTRA_FMT format EXTRA_ARG, ##arg)
+# define vvidbg(format, ap) \
+ vsyslog(format, ap)
+# else
+# define vidbg(x...)
+# define vvidbg(x...)
+# endif
+
+# if CONFIG_SYSEM_CLE_DEBUGLEVEL > 1
+# define vivdbg(format, arg...) \
+ syslog(EXTRA_FMT format EXTRA_ARG, ##arg)
+# else
+# define vivdbg(x...)
+# endif
+#else
+# if CONFIG_SYSEM_CLE_DEBUGLEVEL > 0
+# define vidbg syslog
+# define vvidbg vsyslog
+# else
+# define vidbg (void)
+# define vvidbg (void)
+# endif
+
+# if CONFIG_SYSEM_CLE_DEBUGLEVEL > 1
+# define vivdbg syslog
+# else
+# define vivdbg (void)
+# endif
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* VI Key Bindings */
+
+enum cle_key_e
+{
+ KEY_BEGINLINE = CTRL('A'), /* Move cursor to start of current line */
+ KEY_LEFT = CTRL('B'), /* Move left one character */
+ KEY_DEL = CTRL('D'), /* Delete a single character at the cursor position*/
+ KEY_ENDLINE = CTRL('E'), /* Move cursor to end of current line */
+ KEY_RIGHT = CTRL('F'), /* Move right one character */
+ KEY_DELLEFT = CTRL('H'), /* Delete character, left (backspace) */
+ KEY_DELEOL = CTRL('K'), /* Delete to the end of the line */
+ KEY_DELLINE = CTRL('U'), /* Delete the entire line */
+ KEY_QUOTE = '\\', /* The next character is quote (use literal value) */
+};
+
+/* This structure describes the overall state of the editor */
+
+struct cle_s
+{
+ uint16_t curpos; /* Current cursor position */
+ uint16_t cursave; /* Saved cursor position */
+ uint16_t row; /* This is the row that we are editing iun */
+ uint16_t coloffs; /* Left cursor offset */
+ uint16_t linelen; /* Size of the line buffer */
+ uint16_t nchars; /* Size of data in the line buffer */
+ int infd; /* Input file descriptor */
+ int outfd; /* Output file descriptor */
+ FAR char *line; /* Line buffer */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Low-level display and data entry functions */
+
+static void cle_write(FAR struct cle_s *cle, FAR const char *buffer,
+ uint16_t buflen);
+static void cle_putch(FAR struct cle_s *cle, char ch);
+static int cle_getch(FAR struct cle_s *cle);
+static void cle_cursoron(FAR struct cle_s *cle);
+static void cle_cursoroff(FAR struct cle_s *cle);
+static void cle_setcursor(FAR struct cle_s *cle, uint16_t column);
+static int cle_getcursor(FAR struct cle_s *cle, uint16_t *prow,
+ uint16_t *pcolumn);
+static void cle_clrtoeol(FAR struct cle_s *cle);
+
+/* Editor function */
+
+static bool cle_opentext(FAR struct cle_s *cle, uint16_t pos,
+ uint16_t increment);
+static void cle_closetext(FAR struct cle_s *cle, uint16_t pos, uint16_t size);
+static void cle_showtext(FAR struct cle_s *cle);
+static void cle_insertch(FAR struct cle_s *cle, char ch);
+static int cle_editloop(FAR struct cle_s *cle);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* VT100 escape sequences */
+
+static const char g_cursoron[] = VT100_CURSORON;
+static const char g_cursoroff[] = VT100_CURSOROFF;
+static const char g_getcursor[] = VT100_GETCURSOR;
+static const char g_erasetoeol[] = VT100_CLEAREOL;
+static const char g_attriboff[] = VT100_MODESOFF;
+static const char g_fmtcursorpos[] = VT100_FMT_CURSORPOS;
+
+/* Error format strings */
+
+static const char g_fmtnotvalid[] = "Command not valid";
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Low-level display and data entry functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cle_write
+ *
+ * Description:
+ * Write a sequence of bytes to the console device (stdout, fd = 1).
+ *
+ ****************************************************************************/
+
+static void cle_write(FAR struct cle_s *cle, FAR const char *buffer,
+ uint16_t buflen)
+{
+ ssize_t nwritten;
+ uint16_t nremaining = buflen;
+
+ //vivdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);
+
+ /* Loop until all bytes have been successuflly written (or until a
+ * un-recoverable error is encountered)
+ */
+
+ do
+ {
+ /* Take the next gulp */
+
+ nwritten = write(cle->outfd, buffer, buflen);
+
+ /* Handle write errors. write() should neve return 0. */
+
+ if (nwritten <= 0)
+ {
+ /* EINTR is not really an error; it simply means that a signal was
+ * received while waiting for write.
+ */
+
+ int errcode = errno;
+ if (nwritten == 0 || errcode != EINTR)
+ {
+ vidbg("ERROR: write to stdout failed: %d\n", errcode);
+ return;
+ }
+ }
+
+ /* Decrement the count of bytes remaining to be sent (to handle the
+ * case of a partial write)
+ */
+
+ else
+ {
+ nremaining -= nwritten;
+ }
+ }
+ while (nremaining > 0);
+}
+
+/****************************************************************************
+ * Name: cle_putch
+ *
+ * Description:
+ * Write a single character to the console device.
+ *
+ ****************************************************************************/
+
+static void cle_putch(FAR struct cle_s *cle, char ch)
+{
+ cle_write(cle, &ch, 1);
+}
+
+/****************************************************************************
+ * Name: cle_getch
+ *
+ * Description:
+ * Get a single character from the console device (stdin, fd = 0).
+ *
+ ****************************************************************************/
+
+static int cle_getch(FAR struct cle_s *cle)
+{
+ char buffer;
+ ssize_t nread;
+
+ /* Loop until we successfully read a character (or until an unexpected
+ * error occurs).
+ */
+
+ do
+ {
+ /* Read one character from the incoming stream */
+
+ nread = read(cle->infd, &buffer, 1);
+
+ /* Check for error or end-of-file. */
+
+ if (nread <= 0)
+ {
+ /* EINTR is not really an error; it simply means that a signal we
+ * received while waiting for input.
+ */
+
+ int errcode = errno;
+ if (nread == 0 || errcode != EINTR)
+ {
+ vidbg("ERROR: read from stdin failed: %d\n", errcode);
+ return -EIO;
+ }
+ }
+ }
+ while (nread < 1);
+
+ /* On success, return the character that was read */
+
+ vivdbg("Returning: %c[%02x]\n", isprint(buffer) ? buffer : '.', buffer);
+ return buffer;
+}
+
+/****************************************************************************
+ * Name: cle_cursoron
+ *
+ * Description:
+ * Turn on the cursor
+ *
+ ****************************************************************************/
+
+static void cle_cursoron(FAR struct cle_s *cle)
+{
+ /* Send the VT100 CURSORON command */
+
+ cle_write(cle, g_cursoron, sizeof(g_cursoron));
+}
+
+/****************************************************************************
+ * Name: cle_cursoroff
+ *
+ * Description:
+ * Turn off the cursor
+ *
+ ****************************************************************************/
+
+static void cle_cursoroff(FAR struct cle_s *cle)
+{
+ /* Send the VT100 CURSOROFF command */
+
+ cle_write(cle, g_cursoroff, sizeof(g_cursoroff));
+}
+
+/****************************************************************************
+ * Name: cle_setcursor
+ *
+ * Description:
+ * Move the current cursor position to position (row,col)
+ *
+ ****************************************************************************/
+
+static void cle_setcursor(FAR struct cle_s *cle, uint16_t column)
+{
+ char buffer[16];
+ int len;
+
+ vivdbg("row=%d column=%d\n", row, column);
+
+ /* Format the cursor position command. The origin is (1,1). */
+
+ len = snprintf(buffer, 16, g_fmtcursorpos, cle->row, column + cle->coloffs);
+
+ /* Send the VT100 CURSORPOS command */
+
+ cle_write(cle, buffer, len);
+}
+
+/****************************************************************************
+ * Name: cle_getcursor
+ *
+ * Description:
+ * Get the current cursor position.
+ *
+ ****************************************************************************/
+
+static int cle_getcursor(FAR struct cle_s *cle, FAR uint16_t *prow,
+ FAR uint16_t *pcolumn)
+{
+ uint32_t row;
+ uint32_t column;
+ int nbad;
+ int ch;
+
+ /* Send the VT100 GETCURSOR command */
+
+ cle_write(cle, g_getcursor, sizeof(g_getcursor));
+
+ /* We expect to get ESCv;hR where v is the row and h is the column */
+
+ nbad = 0;
+ for (;;)
+ {
+ /* Get the next character from the input */
+
+ ch = cle_getch(cle);
+ if (ch == ASCII_ESC)
+ {
+ break;
+ }
+ else if (ch < 0)
+ {
+ return -EIO;
+ }
+ else if (++nbad > 3)
+ {
+ /* We are probably talking to a non-VT100 terminal! */
+
+ return -EINVAL;
+ }
+ }
+
+ /* Now we expect to see a numeric value terminated with ';' */
+
+ row = 0;
+ for (;;)
+ {
+ /* Get the next character from the input */
+
+ ch = cle_getch(cle);
+ if (isdigit(ch))
+ {
+ row = 10*row + (ch - '0');
+ }
+ else if (ch == ';')
+ {
+ break;
+ }
+ else if (ch < 0)
+ {
+ return -EIO;
+ }
+ else
+ {
+ return -EINVAL;
+ }
+ }
+
+ /* Now we expect to see another numeric value terminated with 'R' */
+
+ column = 0;
+ for (;;)
+ {
+ /* Get the next character from the input */
+
+ ch = cle_getch(cle);
+ if (isdigit(ch))
+ {
+ column = 10*column + (ch - '0');
+ }
+ else if (ch == 'R')
+ {
+ break;
+ }
+ else if (ch < 0)
+ {
+ return -EIO;
+ }
+ else
+ {
+ return -EINVAL;
+ }
+ }
+
+ vivdbg("row=%ld column=%ld\n", row, column);
+
+ /* Make sure that the values are within range */
+
+ if (row <= UINT16_MAX && column <= UINT16_MAX)
+ {
+ *prow = row;
+ *pcolumn = column;
+ return OK;
+ }
+
+ return -ERANGE;
+}
+
+/****************************************************************************
+ * Name: cle_clrtoeol
+ *
+ * Description:
+ * Clear the display from the current cursor position to the end of the
+ * current line.
+ *
+ ****************************************************************************/
+
+static void cle_clrtoeol(FAR struct cle_s *cle)
+{
+ /* Send the VT100 ERASETOEOL command */
+
+ cle_write(cle, g_erasetoeol, sizeof(g_erasetoeol));
+}
+
+/****************************************************************************
+ * Name: cle_opentext
+ *
+ * Description:
+ * Make space for new text of size 'increment' at the specified cursor
+ * position.
+ *
+ ****************************************************************************/
+
+static bool cle_opentext(FAR struct cle_s *cle, uint16_t pos, uint16_t increment)
+{
+ int i;
+
+ vivdbg("pos=%ld increment=%ld\n", (long)pos, (long)increment);
+
+ /* Check if there is space in the line buffer to open up a region the size
+ * of 'increment'
+ */
+
+ if (cle->nchars + increment > cle->linelen)
+ {
+ return false;
+ }
+
+ /* Move text to make space for new text of size 'increment' at the current
+ * cursor position
+ */
+
+ for (i = cle->nchars - 1; i >= pos; i--)
+ {
+ cle->line[i + increment] = cle->line[i];
+ }
+
+ /* Adjust end of file position */
+
+ cle->nchars += increment;
+ return true;
+}
+
+/****************************************************************************
+ * Name: cle_closetext
+ *
+ * Description:
+ * Delete a region in the line buffer by copying the end of the line buffer
+ * over the deleted region and adjusting the size of the region.
+ *
+ ****************************************************************************/
+
+static void cle_closetext(FAR struct cle_s *cle, uint16_t pos, uint16_t size)
+{
+ int i;
+
+ vivdbg("pos=%ld size=%ld\n", (long)pos, (long)size);
+
+ /* Close up the gap to remove 'size' characters at 'pos' */
+
+ for (i = pos + size; i < cle->nchars; i++)
+ {
+ cle->line[i - size] = cle->line[i];
+ }
+
+ /* Adjust sizes and positions */
+
+ cle->nchars -= size;
+
+ /* Check if the cursor position is beyond the deleted region */
+
+ if (cle->curpos > pos + size)
+ {
+ /* Yes... just subtract the size of the deleted region */
+
+ cle->curpos -= size;
+ }
+
+ /* What if the position is within the deleted region? Set it to the
+ * beginning of the deleted region.
+ */
+
+ else if (cle->curpos > pos)
+ {
+ cle->curpos = pos;
+ }
+}
+
+/****************************************************************************
+ * Name: cle_showtext
+ *
+ * Description:
+ * Update the display based on the last operation. This function is
+ * called at the beginning of the processing loop in Command and Insert
+ * modes (and also in the continuous replace mode).
+ *
+ ****************************************************************************/
+
+static void cle_showtext(FAR struct cle_s *cle)
+{
+ uint16_t column;
+ uint16_t tabcol;
+
+ /* Turn off the cursor during the update. */
+
+ cle_cursoroff(cle);
+
+ /* Set the cursor position to the beginning of this row */
+
+ cle_setcursor(cle, 0);
+ cle_clrtoeol(cle);
+
+ /* Loop for each column */
+
+ for (column = 0; column < cle->nchars && column < cle->linelen; column++)
+ {
+ /* Perform TAB expansion */
+
+ if (cle->line[column] == '\t')
+ {
+ tabcol = NEXT_TAB(column);
+ if (tabcol < cle->linelen)
+ {
+ for (; column < tabcol; column++)
+ {
+ cle_putch(cle, ' ');
+ }
+ }
+ else
+ {
+ /* Break out of the loop... there is nothing left on the
+ * line but whitespace.
+ */
+
+ break;
+ }
+ }
+
+ /* Add the normal character to the display */
+
+ else
+ {
+ cle_putch(cle, cle->line[column]);
+ column++;
+ }
+ }
+
+ /* Turn the cursor back on */
+
+ cle_cursoron(cle);
+}
+
+/****************************************************************************
+ * Name: cle_insertch
+ *
+ * Description:
+ * Insert one character into the line buffer
+ *
+ ****************************************************************************/
+
+static void cle_insertch(FAR struct cle_s *cle, char ch)
+{
+ vivdbg("curpos=%ld ch=%c[%02x]\n", cle->curpos, isprint(ch) ? ch : '.', ch);
+
+ /* Make space in the buffer for the new character */
+
+ if (cle_opentext(cle, cle->curpos, 1))
+ {
+ /* Add the new character to the buffer */
+
+ cle->line[cle->curpos++] = ch;
+ }
+}
+
+/****************************************************************************
+ * Name: cle_editloop
+ *
+ * Description:
+ * Command line editor loop
+ *
+ ****************************************************************************/
+
+static int cle_editloop(FAR struct cle_s *cle)
+{
+ /* Loop while we are in command mode */
+
+ for (;;)
+ {
+ int ch;
+
+ /* Make sure that the display reflects the current state */
+
+ cle_showtext(cle);
+ cle_setcursor(cle, cle->curpos);
+
+ /* Get the next character from the input */
+
+ ch = cle_getch(cle);
+ if (ch < 0)
+ {
+ return -EIO;
+ }
+
+ /* Then handle the character. */
+
+ switch (ch)
+ {
+ case KEY_BEGINLINE: /* Move cursor to start of current line */
+ {
+ cle->curpos = 0;
+ }
+ break;
+
+ case KEY_LEFT: /* Move the cursor left 1 character */
+ {
+ if (cle->curpos > 0)
+ {
+ cle->curpos--;
+ }
+ else
+ {
+ CLE_BEL(cle);
+ }
+ }
+ break;
+
+ case KEY_DEL: /* Delete 1 character at the cursor */
+ case ASCII_DEL:
+ {
+ if (cle->curpos < cle->nchars)
+ {
+ cle_closetext(cle, cle->curpos, 1);
+ }
+ else
+ {
+ CLE_BEL(cle);
+ }
+ }
+ break;
+
+ case KEY_ENDLINE: /* Move cursor to end of current line */
+ {
+ if (cle->nchars > 0)
+ {
+ cle->curpos = cle->nchars - 1;
+ }
+ else
+ {
+ CLE_BEL(cle);
+ }
+ }
+ break;
+
+ case KEY_RIGHT: /* Move the cursor right one character */
+ {
+ if (cle->curpos < cle->nchars)
+ {
+ cle->curpos++;
+ }
+ else
+ {
+ CLE_BEL(cle);
+ }
+ }
+ break;
+
+ case KEY_DELLEFT: /* Delete 1 character before the cursor */
+ //case ASCII_BS:
+ {
+ if (cle->curpos > 0)
+ {
+ cle_closetext(cle, --cle->curpos, 1);
+ }
+ else
+ {
+ CLE_BEL(cle);
+ }
+ }
+ break;
+
+ case KEY_DELEOL: /* Delete to the end of the line */
+ {
+ cle->nchars = (cle->nchars > 0 ? cle->curpos + 1 : 0);
+ }
+ break;
+
+ case KEY_DELLINE: /* Delete to the end of the line */
+ {
+ cle->nchars = 0;
+ cle->curpos = 0;
+ }
+ break;
+
+ case KEY_QUOTE: /* Quoted character follows */
+ {
+ ch = cle_getch(cle);
+ if (ch < 0)
+ {
+ return -EIO;
+ }
+
+ /* Insert the next character unconditionally */
+
+ cle_insertch(cle, ch);
+ }
+ break;
+
+ /* Newline terminates editing */
+
+#if defined(CONFIG_EOL_IS_CR)
+ case '\r': /* CR terminates line */
+ {
+ return OK;
+ }
+ break;
+
+#elif defined(CONFIG_EOL_IS_BOTH_CRLF)
+ case '\r': /* Wait for the LF */
+ break;
+#endif
+
+#if defined(CONFIG_EOL_IS_LF) || defined(CONFIG_EOL_IS_BOTH_CRLF)
+ case '\n': /* LF terminates line */
+ {
+ return OK;
+ }
+ break;
+#endif
+
+#ifdef CONFIG_EOL_IS_EITHER_CRLF
+ case '\r': /* Ether CR or LF terminates line */
+ case '\n':
+ {
+ return OK;
+ }
+ break;
+#endif
+ /* Text to insert or unimplemented/invalid keypresses */
+
+ default:
+ {
+ /* Ignore all control characters except for tab and newline */
+
+ if (!iscntrl(ch) || ch == '\t')
+ {
+ /* Insert the filtered character into the buffer */
+
+ cle_insertch(cle, ch);
+ }
+ else
+ {
+ CLE_BEL(cle);
+ }
+ }
+ break;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Command line processing
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cle
+ *
+ * Description:
+ * EMACS-like command line editor. This is actually more like readline
+ * than is the NuttX readline!
+ *
+ ****************************************************************************/
+
+int cle(FAR char *line, uint16_t linelen, FILE *instream, FILE *outstream)
+{
+ FAR struct cle_s cle;
+ uint16_t column;
+ int ret;
+
+ /* Initialize the cle state structure */
+
+ cle.linelen = linelen;
+ cle.line = line;
+
+ /* REVISIT: Non-standard, non-portable */
+
+ cle.infd = instream->fs_fd;
+ cle.outfd = outstream->fs_fd;
+
+ /* Get the current cursor position */
+
+ ret = cle_getcursor(&cle, &cle.row, &column);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Turn the column number into an offset */
+
+ if (column < 1)
+ {
+ return -EINVAL;
+ }
+
+ cle.coloffs = column - 1;
+
+ /* The editor loop */
+
+ return cle_editloop(&cle);
+}