From 9f2b12c2529e0fbe02693c35a82bd35168637c23 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 30 Sep 2013 11:34:04 -0600 Subject: Changes to NSH and readline to permit NSH to work on a platform with no file system --- apps/ChangeLog.txt | 6 + apps/examples/nsh/nsh_main.c | 2 +- apps/include/readline.h | 37 ++++- apps/nshlib/Makefile | 13 +- apps/nshlib/nsh.h | 8 + apps/nshlib/nsh_builtin.c | 6 +- apps/nshlib/nsh_console.c | 53 ++++-- apps/nshlib/nsh_console.h | 28 ++-- apps/nshlib/nsh_parse.c | 27 +++- apps/nshlib/nsh_session.c | 4 +- apps/nshlib/nsh_stdsession.c | 152 +++++++++++++++++ apps/system/readline/Makefile | 34 ++-- apps/system/readline/readline.c | 269 +++++------------------------- apps/system/readline/readline.h | 131 +++++++++++++++ apps/system/readline/readline_common.c | 288 +++++++++++++++++++++++++++++++++ apps/system/readline/std_readline.c | 158 ++++++++++++++++++ 16 files changed, 943 insertions(+), 273 deletions(-) create mode 100644 apps/nshlib/nsh_stdsession.c create mode 100644 apps/system/readline/readline.h create mode 100644 apps/system/readline/readline_common.c create mode 100644 apps/system/readline/std_readline.c (limited to 'apps') diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt index 238496022..e6d773c8f 100644 --- a/apps/ChangeLog.txt +++ b/apps/ChangeLog.txt @@ -667,3 +667,9 @@ * system/cdcacm: Move examples/cdcacm to system/cdcacm (2013-9-25). * system/composite: Move examples/composite to system/composite (2013-9-25). + * apps/nshlib and apps/readline: Numerous changes to get NSH + working with no file system. Basically this suppresses I/O + redirection and replaces file I/O with calls to low-level + console read/write functions. Suggested by Alan Carvalho de Assis + in a somewhat different form. (2013-9-30). + diff --git a/apps/examples/nsh/nsh_main.c b/apps/examples/nsh/nsh_main.c index 92aa58696..7bd34615e 100644 --- a/apps/examples/nsh/nsh_main.c +++ b/apps/examples/nsh/nsh_main.c @@ -1,7 +1,7 @@ /**************************************************************************** * examples/nsh/nsh_main.c * - * Copyright (C) 2007-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without diff --git a/apps/include/readline.h b/apps/include/readline.h index 6da6f3fd7..463643534 100644 --- a/apps/include/readline.h +++ b/apps/include/readline.h @@ -41,6 +41,7 @@ ****************************************************************************/ #include +#include /**************************************************************************** * Pre-Processor Definitions @@ -90,7 +91,41 @@ extern "C" { * **************************************************************************/ -EXTERN ssize_t readline(FAR char *buf, int buflen, FILE *instream, FILE *outstream); +#if CONFIG_NFILE_STREAMS > 0 +ssize_t readline(FAR char *buf, int buflen, FILE *instream, FILE *outstream); +#endif + +/**************************************************************************** + * Name: std_readline + * + * std_readline is requivalent to readline except that it uses only stdin + * and stdout. + * + * This version of realine assumes that we are reading and writing to + * a VT100 console. This will not work well if 'instream' or 'outstream' + * corresponds to a raw byte steam. + * + * This function is inspired by the GNU readline but is an entirely + * different creature. + * + * Input Parameters: + * buf - The user allocated buffer to be filled. + * buflen - the size of the buffer. + * instream - The stream to read characters from + * outstream - The stream to each characters to. + * + * Returned values: + * On success, the (positive) number of bytes transferred is returned. + * EOF is returned to indicate either an end of file condition or a + * failure. + * + **************************************************************************/ + +#if CONFIG_NFILE_STREAMS > 0 +# define std_readline(b,s) readline(b,s,stdin,stdout) +#else +ssize_t std_readline(FAR char *buf, int buflen); +#endif #undef EXTERN #ifdef __cplusplus diff --git a/apps/nshlib/Makefile b/apps/nshlib/Makefile index 76cdac40d..a1586bb00 100644 --- a/apps/nshlib/Makefile +++ b/apps/nshlib/Makefile @@ -40,9 +40,14 @@ include $(APPDIR)/Make.defs # NSH Library ASRCS = -CSRCS = nsh_init.c nsh_parse.c nsh_console.c nsh_script.c nsh_session.c -CSRCS += nsh_fscmds.c nsh_ddcmd.c nsh_proccmds.c nsh_mmcmds.c nsh_envcmds.c -CSRCS += nsh_dbgcmds.c +CSRCS = nsh_init.c nsh_parse.c nsh_console.c nsh_script.c nsh_fscmds.c +CSRCS += nsh_ddcmd.c nsh_proccmds.c nsh_mmcmds.c nsh_envcmds.c nsh_dbgcmds.c + +ifeq ($(CONFIG_NFILE_STREAMS),0) +CSRCS += nsh_stdsession.c +else +CSRCS += nsh_session.c +endif ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) CSRCS += nsh_builtin.c @@ -105,7 +110,7 @@ endif endif ROOTDEPPATH = --dep-path . -VPATH = +VPATH = # Build targets diff --git a/apps/nshlib/nsh.h b/apps/nshlib/nsh.h index 9f1bb40fe..bcad42b1d 100644 --- a/apps/nshlib/nsh.h +++ b/apps/nshlib/nsh.h @@ -68,6 +68,12 @@ # endif #endif +#if CONFIG_NFILE_STREAMS == 0 +# undef CONFIG_NSH_TELNET +# undef CONFIG_NSH_FILE_APPS +# undef CONFIG_NSH_TELNET +#endif + /* Telnetd requires networking support */ #ifndef CONFIG_NET @@ -441,7 +447,9 @@ struct nsh_parser_s #ifndef CONFIG_NSH_DISABLEBG bool np_bg; /* true: The last command executed in background */ #endif +#if CONFIG_NFILE_STREAMS > 0 bool np_redirect; /* true: Output from the last command was re-directed */ +#endif bool np_fail; /* true: The last command failed */ #ifndef CONFIG_NSH_DISABLESCRIPT uint8_t np_ndx; /* Current index into np_st[] */ diff --git a/apps/nshlib/nsh_builtin.c b/apps/nshlib/nsh_builtin.c index 0819377de..59e54ae85 100644 --- a/apps/nshlib/nsh_builtin.c +++ b/apps/nshlib/nsh_builtin.c @@ -131,7 +131,7 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, { /* The application was successfully started with pre-emption disabled. * In the simplest cases, the application will not have run because the - * the scheduler is locked. But in the case where I/O was redirected, a + * the scheduler is locked. But in the case where I/O was redirected, a * proxy task ran and broke our lock. As result, the application may * have aso ran if its priority was higher than than the priority of * this thread. @@ -166,7 +166,7 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, * do so. * * Also, if CONFIG_SCHED_HAVE_PARENT is defined waitpid() might fail - * even if task is still active: If the I/O was re-directed by a + * even if task is still active: If the I/O was re-directed by a * proxy task, then the ask is a child of the proxy, and not this * task. waitpid() fails with ECHILD in either case. */ @@ -200,7 +200,7 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, else { /* We can't return the exact status (nsh has nowhere to put it) - * so just pass back zero/nonzero in a fashion that doesn't look + * so just pass back zero/nonzero in a fashion that doesn't look * like an error. */ diff --git a/apps/nshlib/nsh_console.c b/apps/nshlib/nsh_console.c index 1b8f5f6ac..410abc19d 100644 --- a/apps/nshlib/nsh_console.c +++ b/apps/nshlib/nsh_console.c @@ -1,7 +1,7 @@ /**************************************************************************** - * apps/nshlib/nsh_serial.c + * apps/nshlib/nsh_console.c * - * Copyright (C) 2007-2009, 2011-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -61,11 +61,13 @@ * Private Types ****************************************************************************/ +#if CONFIG_NFILE_STREAMS > 0 struct serialsave_s { int cn_outfd; /* Re-directed output file descriptor */ FILE *cn_outstream; /* Re-directed output stream */ }; +#endif /**************************************************************************** * Private Function Prototypes @@ -80,10 +82,14 @@ static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, static int nsh_consoleoutput(FAR struct nsh_vtbl_s *vtbl, FAR const char *fmt, ...); static FAR char *nsh_consolelinebuffer(FAR struct nsh_vtbl_s *vtbl); + +#if CONFIG_NFILE_DESCRIPTORS > 0 static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); +#endif + static void nsh_consoleexit(FAR struct nsh_vtbl_s *vtbl, int exitstatus) noreturn_function; @@ -103,6 +109,7 @@ static void nsh_consoleexit(FAR struct nsh_vtbl_s *vtbl, int exitstatus) * Name: nsh_openifnotopen ****************************************************************************/ +#if CONFIG_NFILE_DESCRIPTORS > 0 static int nsh_openifnotopen(struct console_stdio_s *pstate) { /* The stream is open in a lazy fashion. This is done because the file @@ -117,8 +124,10 @@ static int nsh_openifnotopen(struct console_stdio_s *pstate) return ERROR; } } + return 0; } +#endif /**************************************************************************** * Name: nsh_closeifnotclosed @@ -128,6 +137,7 @@ static int nsh_openifnotopen(struct console_stdio_s *pstate) * ****************************************************************************/ +#if CONFIG_NFILE_STREAMS > 0 static void nsh_closeifnotclosed(struct console_stdio_s *pstate) { if (pstate->cn_outstream == OUTSTREAM(pstate)) @@ -151,6 +161,7 @@ static void nsh_closeifnotclosed(struct console_stdio_s *pstate) pstate->cn_outstream = NULL; } } +#endif /**************************************************************************** * Name: nsh_consolewrite @@ -167,6 +178,7 @@ static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buf FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; ssize_t ret; +#if CONFIG_NFILE_DESCRIPTORS > 0 /* The stream is open in a lazy fashion. This is done because the file * descriptor may be opened on a different task than the stream. The * actual open will then occur with the first output from the new task. @@ -185,6 +197,10 @@ static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buf dbg("[%d] Failed to send buffer: %d\n", pstate->cn_outfd, errno); } return ret; +#else + printf("%s", buffer); + return nbytes; +#endif } /**************************************************************************** @@ -197,9 +213,10 @@ static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buf static int nsh_consoleoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...) { +#if CONFIG_NFILE_DESCRIPTORS > 0 FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; va_list ap; - int ret; + int ret; /* The stream is open in a lazy fashion. This is done because the file * descriptor may be opened on a different task than the stream. The @@ -210,12 +227,21 @@ static int nsh_consoleoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...) { return ERROR; } - + va_start(ap, fmt); ret = vfprintf(pstate->cn_outstream, fmt, ap); va_end(ap); - + return ret; +#else + char dest[64 * 16]; + + va_start(ap, fmt); + vsprintf(dest, fmt, ap); + va_end(ap); + + return printf(dest); +#endif } /**************************************************************************** @@ -260,6 +286,7 @@ static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl) { FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; +#if CONFIG_NFILE_DESCRIPTORS > 0 /* Close the output stream */ nsh_closeifnotclosed(pstate); @@ -268,6 +295,7 @@ static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl) #ifdef CONFIG_NSH_CONDEV (void)fclose(pstate->cn_constream); +#endif #endif /* Then release the vtable container */ @@ -292,7 +320,7 @@ static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl) * * nsh_consolerelease() will perform the clean-up when the clone is * destroyed. - * + * * 2) Redirected foreground commands of the form: command > xyz.txt * * In this case: @@ -305,9 +333,10 @@ static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl) * ****************************************************************************/ +#if CONFIG_NFILE_DESCRIPTORS > 0 static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save) { - FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; + FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; FAR struct serialsave_s *ssave = (FAR struct serialsave_s *)save; /* Case 1: Redirected foreground commands */ @@ -321,7 +350,7 @@ static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t if (pstate->cn_outstream) { - fflush(pstate->cn_outstream); + fflush(pstate->cn_outstream); } /* Save the current fd and stream values. These will be restored @@ -346,6 +375,7 @@ static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t pstate->cn_outfd = fd; pstate->cn_outstream = NULL; } +#endif /**************************************************************************** * Name: nsh_consoleundirect @@ -355,6 +385,7 @@ static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t * ****************************************************************************/ +#if CONFIG_NFILE_DESCRIPTORS > 0 static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save) { FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; @@ -364,6 +395,7 @@ static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save) pstate->cn_outfd = ssave->cn_outfd; pstate->cn_outstream = ssave->cn_outstream; } +#endif /**************************************************************************** * Name: nsh_consoleexit @@ -403,9 +435,11 @@ FAR struct console_stdio_s *nsh_newconsole(void) pstate->cn_vtbl.write = nsh_consolewrite; pstate->cn_vtbl.output = nsh_consoleoutput; pstate->cn_vtbl.linebuffer = nsh_consolelinebuffer; + pstate->cn_vtbl.exit = nsh_consoleexit; + +#if CONFIG_NFILE_STREAMS > 0 pstate->cn_vtbl.redirect = nsh_consoleredirect; pstate->cn_vtbl.undirect = nsh_consoleundirect; - pstate->cn_vtbl.exit = nsh_consoleexit; /* (Re-) open the console input device */ @@ -432,6 +466,7 @@ FAR struct console_stdio_s *nsh_newconsole(void) pstate->cn_outfd = OUTFD(pstate); pstate->cn_outstream = OUTSTREAM(pstate); +#endif } return pstate; } diff --git a/apps/nshlib/nsh_console.h b/apps/nshlib/nsh_console.h index 4dc2938cb..73bab493b 100644 --- a/apps/nshlib/nsh_console.h +++ b/apps/nshlib/nsh_console.h @@ -1,7 +1,7 @@ /**************************************************************************** * apps/nshlib/nsh_console.h * - * Copyright (C) 2007-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -74,16 +74,18 @@ /* Are we using the NuttX console for I/O? Or some other character device? */ -#ifdef CONFIG_NSH_CONDEV -# define INFD(p) ((p)->cn_confd) -# define INSTREAM(p) ((p)->cn_constream) -# define OUTFD(p) ((p)->cn_confd) -# define OUTSTREAM(p) ((p)->cn_constream) -#else -# define INFD(p) 0 -# define INSTREAM(p) stdin -# define OUTFD(p) 1 -# define OUTSTREAM(p) stdout +#if CONFIG_NFILE_STREAMS > 0 +# ifdef CONFIG_NSH_CONDEV +# define INFD(p) ((p)->cn_confd) +# define INSTREAM(p) ((p)->cn_constream) +# define OUTFD(p) ((p)->cn_confd) +# define OUTSTREAM(p) ((p)->cn_constream) +# else +# define INFD(p) 0 +# define INSTREAM(p) stdin +# define OUTFD(p) 1 +# define OUTSTREAM(p) stdout +# endif #endif /**************************************************************************** @@ -107,8 +109,10 @@ struct nsh_vtbl_s ssize_t (*write)(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes); int (*output)(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); FAR char *(*linebuffer)(FAR struct nsh_vtbl_s *vtbl); +#if CONFIG_NFILE_DESCRIPTORS > 0 void (*redirect)(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); void (*undirect)(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); +#endif void (*exit)(FAR struct nsh_vtbl_s *vtbl, int exitstatus) noreturn_function; /* Parser state data */ @@ -128,6 +132,7 @@ struct console_stdio_s /* NSH input/output streams */ +#if CONFIG_NFILE_STREAMS > 0 #ifdef CONFIG_NSH_CONDEV int cn_confd; /* Console I/O file descriptor */ #endif @@ -136,6 +141,7 @@ struct console_stdio_s FILE *cn_constream; /* Console I/O stream (possibly redirected) */ #endif FILE *cn_outstream; /* Output stream */ +#endif /* Line input buffer */ diff --git a/apps/nshlib/nsh_parse.c b/apps/nshlib/nsh_parse.c index 3f17149f5..d449bf61e 100644 --- a/apps/nshlib/nsh_parse.c +++ b/apps/nshlib/nsh_parse.c @@ -821,6 +821,7 @@ static void nsh_releaseargs(struct cmdarg_s *arg) FAR struct nsh_vtbl_s *vtbl = arg->vtbl; int i; +#if CONFIG_NFILE_STREAMS > 0 /* If the output was redirected, then file descriptor should * be closed. The created task has its one, independent copy of * the file descriptor @@ -830,6 +831,7 @@ static void nsh_releaseargs(struct cmdarg_s *arg) { (void)close(arg->fd); } +#endif /* Released the cloned vtbl instance */ @@ -1297,9 +1299,11 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) FAR char *argv[MAX_ARGV_ENTRIES]; FAR char *saveptr; FAR char *cmd; +#if CONFIG_NFILE_STREAMS > 0 FAR char *redirfile = NULL; - int fd = -1; int oflags = 0; +#endif + int fd = -1; int argc; int ret; @@ -1309,7 +1313,9 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) #ifndef CONFIG_NSH_DISABLEBG vtbl->np.np_bg = false; #endif +#if CONFIG_NFILE_STREAMS > 0 vtbl->np.np_redirect = false; +#endif /* Parse out the command at the beginning of the line */ @@ -1388,6 +1394,7 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) } #endif +#if CONFIG_NFILE_STREAMS > 0 /* Check if the output was re-directed using > or >> */ if (argc > 2) @@ -1412,6 +1419,7 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) argc -= 2; } } +#endif /* Check if the maximum number of arguments was exceeded */ @@ -1480,7 +1488,11 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) */ #if defined(CONFIG_NSH_BUILTIN_APPS) && (!defined(CONFIG_NSH_FILE_APPS) || !defined(CONFIG_FS_BINFS)) +#if CONFIG_NFILE_STREAMS > 0 ret = nsh_builtin(vtbl, argv[0], argv, redirfile, oflags); +#else + ret = nsh_builtin(vtbl, argv[0], argv, NULL, 0); +#endif if (ret >= 0) { /* nsh_builtin() returned 0 or 1. This means that the builtin @@ -1488,12 +1500,14 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) * successfully). So certainly it is not an NSH command. */ +#if CONFIG_NFILE_STREAMS > 0 /* Free the redirected output file path */ if (redirfile) { nsh_freefullpath(redirfile); } +#endif /* Save the result: success if 0; failure if 1 */ @@ -1506,6 +1520,7 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) #endif +#if CONFIG_NFILE_STREAMS > 0 /* Redirected output? */ if (vtbl->np.np_redirect) @@ -1526,6 +1541,7 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) goto errout; } } +#endif /* Handle the case where the command is executed in background. * However is app is to be started as builtin new process will @@ -1561,12 +1577,14 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) goto errout_with_redirect; } +#if CONFIG_NFILE_STREAMS > 0 /* Handle redirection of output via a file descriptor */ if (vtbl->np.np_redirect) { (void)nsh_redirect(bkgvtbl, fd, NULL); } +#endif /* Get the execution priority of this task */ @@ -1634,12 +1652,14 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) { uint8_t save[SAVE_SIZE]; +#if CONFIG_NFILE_STREAMS > 0 /* Handle redirection of output via a file descriptor */ if (vtbl->np.np_redirect) { nsh_redirect(vtbl, fd, save); } +#endif /* Then execute the command in "foreground" -- i.e., while the user waits * for the next prompt. nsh_execute will return: @@ -1650,6 +1670,7 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) ret = nsh_execute(vtbl, argc, argv); +#if CONFIG_NFILE_STREAMS > 0 /* Restore the original output. Undirect will close the redirection * file descriptor. */ @@ -1658,6 +1679,7 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) { nsh_undirect(vtbl, save); } +#endif /* Mark errors so that it is possible to test for non-zero return values * in nsh scripts. @@ -1677,11 +1699,14 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline) #ifndef CONFIG_NSH_DISABLEBG errout_with_redirect: +#if CONFIG_NFILE_STREAMS > 0 if (vtbl->np.np_redirect) { close(fd); } #endif +#endif + errout: return nsh_saveresult(vtbl, true); } diff --git a/apps/nshlib/nsh_session.c b/apps/nshlib/nsh_session.c index 0c5249672..2272b1e30 100644 --- a/apps/nshlib/nsh_session.c +++ b/apps/nshlib/nsh_session.c @@ -94,7 +94,7 @@ * * Returned Values: * EXIT_SUCESS or EXIT_FAILURE is returned. - * + * ****************************************************************************/ int nsh_session(FAR struct console_stdio_s *pstate) @@ -150,7 +150,7 @@ int nsh_session(FAR struct console_stdio_s *pstate) else { - fprintf(pstate->cn_outstream, g_fmtcmdfailed, "nsh_session", + fprintf(pstate->cn_outstream, g_fmtcmdfailed, "nsh_session", "readline", NSH_ERRNO_OF(-ret)); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/apps/nshlib/nsh_stdsession.c b/apps/nshlib/nsh_stdsession.c new file mode 100644 index 000000000..11fae7db7 --- /dev/null +++ b/apps/nshlib/nsh_stdsession.c @@ -0,0 +1,152 @@ +/**************************************************************************** + * apps/nshlib/nsh_stdsession.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 Gregory Nutt 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 + +#include +#include + +#include + +#include "nsh.h" +#include "nsh_console.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_session + * + * Description: + * This is the common session logic or an NSH session that uses only stdin + * and stdout. + * + * This function: + * - Executes the NSH logic script + * - Presents a greeting + * - Then provides a prompt then gets and processes the command line. + * - This continues until an error occurs, then the session returns. + * + * Input Parameters: + * pstate - Abstracts the underlying session. + * + * Returned Values: + * EXIT_SUCESS only + * + ****************************************************************************/ + +int nsh_session(FAR struct console_stdio_s *pstate) +{ + int ret; + + DEBUGASSERT(pstate); + + /* Present a greeting */ + + printf("%s", g_nshgreeting); + + /* Then enter the command line parsing loop */ + + for (;;) + { + /* For the case of debugging the USB console... dump collected USB trace data */ + +#ifdef CONFIG_NSH_USBDEV_TRACE + nsh_usbtrace(); +#endif + + /* Display the prompt string */ + + printf("%s", g_nshprompt); + + /* Get the next line of input. readline() returns EOF on end-of-file + * or any read failure. + */ + + ret = std_readline(pstate->cn_line, CONFIG_NSH_LINELEN); + if (ret != EOF) + { + /* Parse process the command */ + + (void)nsh_parse(&pstate->cn_vtbl, pstate->cn_line); + } + + /* Readline normally returns the number of characters read, + * but will return EOF on end of file or if an error occurs. + * EOF will cause the session to terminate. + */ + + else + { + printf(g_fmtcmdfailed, "nsh_session", "readline", NSH_ERRNO_OF(-ret)); + return EXIT_SUCCESS; + } + } + + /* We do not get here, but this is necessary to keep some compilers happy. + * But others will complain that this code is not reachable. + */ + + return EXIT_SUCCESS; +} diff --git a/apps/system/readline/Makefile b/apps/system/readline/Makefile index 040fd2e28..1f60f45fc 100644 --- a/apps/system/readline/Makefile +++ b/apps/system/readline/Makefile @@ -1,7 +1,7 @@ ############################################################################ # apps/system/readline/Makefile # -# Copyright (C) 2012 Gregory Nutt. All rights reserved. +# Copyright (C) 2012-2013 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -38,37 +38,43 @@ include $(APPDIR)/Make.defs ifeq ($(WINTOOL),y) -INCDIROPT = -w +INCDIROPT = -w endif # The Readline Library -ASRCS = -CSRCS = readline.c +ASRCS = +CSRCS = readline_common.c -AOBJS = $(ASRCS:.S=$(OBJEXT)) -COBJS = $(CSRCS:.c=$(OBJEXT)) +ifeq ($(CONFIG_NFILE_STREAMS),0) +CSRCS += std_readline.c +else +CSRCS += readline.c +endif + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) -SRCS = $(ASRCS) $(CSRCS) -OBJS = $(AOBJS) $(COBJS) +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) ifeq ($(CONFIG_WINDOWS_NATIVE),y) - BIN = ..\..\libapps$(LIBEXT) + BIN = ..\..\libapps$(LIBEXT) else ifeq ($(WINTOOL),y) - BIN = ..\\..\\libapps$(LIBEXT) + BIN = ..\\..\\libapps$(LIBEXT) else - BIN = ../../libapps$(LIBEXT) + BIN = ../../libapps$(LIBEXT) endif endif -ROOTDEPPATH = --dep-path . +ROOTDEPPATH = --dep-path . # Common build -VPATH = +VPATH = -all: .built +all: .built .PHONY: context depend clean distclean $(AOBJS): %$(OBJEXT): %.S diff --git a/apps/system/readline/readline.c b/apps/system/readline/readline.c index 5486a68eb..13e07e652 100644 --- a/apps/system/readline/readline.c +++ b/apps/system/readline/readline.c @@ -39,61 +39,32 @@ #include -#include +#include #include #include -#include -#include #include -#include - -#include -#include +#include #include +#include "readline.h" /**************************************************************************** - * Definitions + * Pre-processor Definitions ****************************************************************************/ -/* In some systems, the underlying serial logic may automatically echo - * characters back to the console. We will assume that that is not the case - & here - */ - -#define CONFIG_READLINE_ECHO 1 - -/* 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 /**************************************************************************** * Private Type Declarations ****************************************************************************/ +struct readline_s +{ + struct rl_common_s vtbl; + int infd; +#ifdef CONFIG_READLINE_ECHO + int outfd; +#endif +}; + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -105,23 +76,23 @@ /**************************************************************************** * Private Data ****************************************************************************/ -/* [K is the VT100 command erases to the end of the line. */ - -static const char g_erasetoeol[] = VT100_CLEAREOL; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: readline_rawgetc + * Name: readline_getc ****************************************************************************/ -static inline int readline_rawgetc(int infd) +static int readline_getc(FAR struct rl_common_s *vtbl) { + FAR struct readline_s *priv = (FAR struct readline_s *)vtbl; char buffer; ssize_t nread; + DEBUGASSERT(priv); + /* Loop until we successfully read a character (or until an unexpected * error occurs). */ @@ -130,7 +101,7 @@ static inline int readline_rawgetc(int infd) { /* Read one character from the incoming stream */ - nread = read(infd, &buffer, 1); + nread = read(priv->infd, &buffer, 1); /* Check for end-of-file. */ @@ -166,15 +137,18 @@ static inline int readline_rawgetc(int infd) } /**************************************************************************** - * Name: readline_consoleputc + * Name: readline_putc ****************************************************************************/ #ifdef CONFIG_READLINE_ECHO -static inline void readline_consoleputc(int ch, int outfd) +static void readline_putc(FAR struct rl_common_s *vtbl, int ch) { + FAR struct readline_s *priv = (FAR struct readline_s *)vtbl; char buffer = ch; ssize_t nwritten; + DEBUGASSERT(priv); + /* Loop until we successfully write a character (or until an unexpected * error occurs). */ @@ -183,7 +157,7 @@ static inline void readline_consoleputc(int ch, int outfd) { /* Write the character to the outgoing stream */ - nwritten = write(outfd, &buffer, 1); + nwritten = write(priv->outfd, &buffer, 1); /* Check for irrecoverable write errors. */ @@ -197,13 +171,17 @@ static inline void readline_consoleputc(int ch, int outfd) #endif /**************************************************************************** - * Name: readline_consolewrite + * Name: readline_write ****************************************************************************/ #ifdef CONFIG_READLINE_ECHO -static inline void readline_consolewrite(int outfd, FAR const char *buffer, size_t buflen) +static void readline_write(FAR struct rl_common_s *vtbl, + FAR const char *buffer, size_t buflen) { - (void)write(outfd, buffer, buflen); + FAR struct readline_s *priv = (FAR struct readline_s *)vtbl; + DEBUGASSERT(priv && buffer && buflen > 0); + + (void)write(priv->outfd, buffer, buflen); } #endif @@ -242,187 +220,24 @@ static inline void readline_consolewrite(int outfd, FAR const char *buffer, size ssize_t readline(FAR char *buf, int buflen, FILE *instream, FILE *outstream) { - int infd; - int outfd; - int escape; - int nch; + struct readline_s vtbl; /* Sanity checks */ - if (!instream || !outstream || !buf || buflen < 1) - { - return -EINVAL; - } - - if (buflen < 2) - { - *buf = '\0'; - return 0; - } - - /* Extract the file descriptions. This is cheating (and horribly non- - * standard) - */ - - infd = instream->fs_fd; - outfd = outstream->fs_fd; - - /* [K is the VT100 command that erases to the end of the line. */ - -#ifdef CONFIG_READLINE_ECHO - readline_consolewrite(outfd, g_erasetoeol, sizeof(g_erasetoeol)); -#endif - - /* Read characters until we have a full line. On each the loop we must - * be assured that there are two free bytes in the line buffer: One for - * the next character and one for the null terminator. - */ - - escape = 0; - nch = 0; - - for(;;) - { - /* Get the next character. readline_rawgetc() returns EOF on any - * errors or at the end of file. - */ - - int ch = readline_rawgetc(infd); - - /* Check for end-of-file or read error */ - - if (ch == EOF) - { - /* Did we already received some data? */ - - if (nch > 0) - { - /* Yes.. Terminate the line (which might be zero length) - * and return the data that was received. The end-of-file - * or error condition will be reported next time. - */ - - buf[nch] = '\0'; - return nch; - } - - return EOF; - } - - /* Are we processing a VT100 escape sequence */ - - else if (escape) - { - /* Yes, is it an [, 3 byte sequence */ - - if (ch != ASCII_LBRACKET || escape == 2) - { - /* We are finished with the escape sequence */ + DEBUGASSERT(instream && outstream); - escape = 0; - ch = 'a'; - } - else - { - /* The next character is the end of a 3-byte sequence. - * NOTE: Some of the [ sequences are longer than - * 3-bytes, but I have not encountered any in normal use - * yet and, so, have not provided the decoding logic. - */ + /* Set up the vtbl structure */ - escape = 2; - } - } - - /* Check for backspace - * - * There are several notions of backspace, for an elaborate summary see - * http://www.ibb.net/~anne/keyboard.html. There is no clean solution. - * Here both DEL and backspace are treated like backspace here. The - * Unix/Linux screen terminal by default outputs DEL (0x7f) when the - * backspace key is pressed. - */ - - else if (ch == ASCII_BS || ch == ASCII_DEL) - { - /* Eliminate that last character in the buffer. */ - - if (nch > 0) - { - nch--; + vtbl.vtbl.rl_getc = readline_getc; + vtbl.infd = instream->fs_fd; #ifdef CONFIG_READLINE_ECHO - /* Echo the backspace character on the console. Always output - * the backspace character because the VT100 terminal doesn't - * understand DEL properly. - */ - - readline_consoleputc(ASCII_BS, outfd); - readline_consolewrite(outfd, g_erasetoeol, sizeof(g_erasetoeol)); + vtbl.vtbl.rl_putc = readline_putc; + vtbl.vtbl.rl_write = readline_write; + vtbl.outfd = outstream->fs_fd; #endif - } - } - - /* Check for the beginning of a VT100 escape sequence */ - - else if (ch == ASCII_ESC) - { - /* The next character is escaped */ - escape = 1; - } + /* The let the common readline logic do the work */ - /* Check for end-of-line. This is tricky only in that some - * environments may return CR as end-of-line, others LF, and - * others both. - */ - -#if defined(CONFIG_EOL_IS_LF) || defined(CONFIG_EOL_IS_BOTH_CRLF) - else if (ch == '\n') -#elif defined(CONFIG_EOL_IS_CR) - else if (ch == '\r') -#elif CONFIG_EOL_IS_EITHER_CRLF - else if (ch == '\n' || ch == '\r') -#endif - { - /* The newline is stored in the buffer along with the null - * terminator. - */ - - buf[nch++] = '\n'; - buf[nch] = '\0'; - -#ifdef CONFIG_READLINE_ECHO - /* Echo the newline to the console */ - - readline_consoleputc('\n', outfd); -#endif - return nch; - } - - /* Otherwise, check if the character is printable and, if so, put the - * character in the line buffer - */ - - else if (isprint(ch)) - { - buf[nch++] = ch; - -#ifdef CONFIG_READLINE_ECHO - /* Echo the character to the console */ - - readline_consoleputc(ch, outfd); -#endif - /* Check if there is room for another character and the line's - * null terminator. If not then we have to end the line now. - */ - - if (nch + 1 >= buflen) - { - buf[nch] = '\0'; - return nch; - } - } - } + return readline_common(&vtbl.vtbl, buf, buflen); } - diff --git a/apps/system/readline/readline.h b/apps/system/readline/readline.h new file mode 100644 index 000000000..386d6d060 --- /dev/null +++ b/apps/system/readline/readline.h @@ -0,0 +1,131 @@ +/**************************************************************************** + * apps/system/readline/readline.h + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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_SYSTEM_READLINE_READLINE_H +#define __APPS_SYSTEM_READLINE_READLINE_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* In some systems, the underlying serial logic may automatically echo + * characters back to the console. We will assume that that is not the case + & here + */ + +#define CONFIG_READLINE_ECHO 1 + +/* 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 + +/* Helper macros */ + +#define RL_GETC(v) ((v)->rl_getc(v)) +#define RL_PUTC(v,ch) ((v)->rl_putc(v,ch)) +#define RL_WRITE(v,b,s) ((v)->rl_write(v,b,s)) + +/**************************************************************************** + * Public Type Declarations + ****************************************************************************/ + +struct rl_common_s +{ + int (*rl_getc)(FAR struct rl_common_s *vtbl); +#ifdef CONFIG_READLINE_ECHO + void (*rl_putc)(FAR struct rl_common_s *vtbl, int ch); + void (*rl_write)(FAR struct rl_common_s *vtbl, FAR const char *buffer, + size_t buflen); +#endif +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: readline_common + * + * Common logic shared by readline and std_readline(). + * + * Input Parameters: + * buf - The user allocated buffer to be filled. + * buflen - the size of the buffer. + * instream - The stream to read characters from + * outstream - The stream to each characters to. + * + * Returned values: + * On success, the (positive) number of bytes transferred is returned. + * EOF is returned to indicate either an end of file condition or a + * failure. + * + **************************************************************************/ + +ssize_t readline_common(FAR struct rl_common_s *vtbl, FAR char *buf, int buflen); + +#endif /* __APPS_SYSTEM_READLINE_READLINE_H */ diff --git a/apps/system/readline/readline_common.c b/apps/system/readline/readline_common.c new file mode 100644 index 000000000..7f3806eac --- /dev/null +++ b/apps/system/readline/readline_common.c @@ -0,0 +1,288 @@ +/**************************************************************************** + * apps/system/readline/readline_common.c + * + * Copyright (C) 2007-2008, 2011-2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "readline.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* [K is the VT100 command erases to the end of the line. */ + +static const char g_erasetoeol[] = VT100_CLEAREOL; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: readline_common + * + * readline() reads in at most one less than 'buflen' characters from + * 'instream' and stores them into the buffer pointed to by 'buf'. + * Characters are echoed on 'outstream'. Reading stops after an EOF or a + * newline. If a newline is read, it is stored into the buffer. A null + * terminator is stored after the last character in the buffer. + * + * This version of realine assumes that we are reading and writing to + * a VT100 console. This will not work well if 'instream' or 'outstream' + * corresponds to a raw byte steam. + * + * This function is inspired by the GNU readline but is an entirely + * different creature. + * + * Input Parameters: + * buf - The user allocated buffer to be filled. + * buflen - the size of the buffer. + * instream - The stream to read characters from + * outstream - The stream to each characters to. + * + * Returned values: + * On success, the (positive) number of bytes transferred is returned. + * EOF is returned to indicate either an end of file condition or a + * failure. + * + **************************************************************************/ + +ssize_t readline_common(FAR struct rl_common_s *vtbl, FAR char *buf, int buflen) +{ + int escape; + int nch; + + /* Sanity checks */ + + DEBUGASSERT(buf && buflen > 0); + + if (buflen < 2) + { + *buf = '\0'; + return 0; + } + + /* [K is the VT100 command that erases to the end of the line. */ + +#ifdef CONFIG_READLINE_ECHO + RL_WRITE(vtbl, g_erasetoeol, sizeof(g_erasetoeol)); +#endif + + /* Read characters until we have a full line. On each the loop we must + * be assured that there are two free bytes in the line buffer: One for + * the next character and one for the null terminator. + */ + + escape = 0; + nch = 0; + + for(;;) + { + /* Get the next character. readline_rawgetc() returns EOF on any + * errors or at the end of file. + */ + + int ch = RL_GETC(vtbl); + + /* Check for end-of-file or read error */ + + if (ch == EOF) + { + /* Did we already received some data? */ + + if (nch > 0) + { + /* Yes.. Terminate the line (which might be zero length) + * and return the data that was received. The end-of-file + * or error condition will be reported next time. + */ + + buf[nch] = '\0'; + return nch; + } + + return EOF; + } + + /* Are we processing a VT100 escape sequence */ + + else if (escape) + { + /* Yes, is it an [, 3 byte sequence */ + + if (ch != ASCII_LBRACKET || escape == 2) + { + /* We are finished with the escape sequence */ + + escape = 0; + ch = 'a'; + } + else + { + /* The next character is the end of a 3-byte sequence. + * NOTE: Some of the [ sequences are longer than + * 3-bytes, but I have not encountered any in normal use + * yet and, so, have not provided the decoding logic. + */ + + escape = 2; + } + } + + /* Check for backspace + * + * There are several notions of backspace, for an elaborate summary see + * http://www.ibb.net/~anne/keyboard.html. There is no clean solution. + * Here both DEL and backspace are treated like backspace here. The + * Unix/Linux screen terminal by default outputs DEL (0x7f) when the + * backspace key is pressed. + */ + + else if (ch == ASCII_BS || ch == ASCII_DEL) + { + /* Eliminate that last character in the buffer. */ + + if (nch > 0) + { + nch--; + +#ifdef CONFIG_READLINE_ECHO + /* Echo the backspace character on the console. Always output + * the backspace character because the VT100 terminal doesn't + * understand DEL properly. + */ + + RL_PUTC(vtbl, ASCII_BS); + RL_WRITE(vtbl, g_erasetoeol, sizeof(g_erasetoeol)); +#endif + } + } + + /* Check for the beginning of a VT100 escape sequence */ + + else if (ch == ASCII_ESC) + { + /* The next character is escaped */ + + escape = 1; + } + + /* Check for end-of-line. This is tricky only in that some + * environments may return CR as end-of-line, others LF, and + * others both. + */ + +#if defined(CONFIG_EOL_IS_LF) || defined(CONFIG_EOL_IS_BOTH_CRLF) + else if (ch == '\n') +#elif defined(CONFIG_EOL_IS_CR) + else if (ch == '\r') +#elif CONFIG_EOL_IS_EITHER_CRLF + else if (ch == '\n' || ch == '\r') +#endif + { + /* The newline is stored in the buffer along with the null + * terminator. + */ + + buf[nch++] = '\n'; + buf[nch] = '\0'; + +#ifdef CONFIG_READLINE_ECHO + /* Echo the newline to the console */ + + RL_PUTC(vtbl, '\n'); +#endif + return nch; + } + + /* Otherwise, check if the character is printable and, if so, put the + * character in the line buffer + */ + + else if (isprint(ch)) + { + buf[nch++] = ch; + +#ifdef CONFIG_READLINE_ECHO + /* Echo the character to the console */ + + RL_PUTC(vtbl, ch); +#endif + /* Check if there is room for another character and the line's + * null terminator. If not then we have to end the line now. + */ + + if (nch + 1 >= buflen) + { + buf[nch] = '\0'; + return nch; + } + } + } +} + diff --git a/apps/system/readline/std_readline.c b/apps/system/readline/std_readline.c new file mode 100644 index 000000000..8778f1677 --- /dev/null +++ b/apps/system/readline/std_readline.c @@ -0,0 +1,158 @@ +/**************************************************************************** + * apps/system/readline/std_readline.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include + +#include "readline.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int readline_getc(FAR struct rl_common_s *vtbl); +static void readline_putc(FAR struct rl_common_s *vtbl, int ch); +static void readline_write(FAR struct rl_common_s *vtbl, + FAR const char *buffer, size_t buflen); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct rl_common_s g_stdreadline = +{ + readline_getc +#ifdef CONFIG_READLINE_ECHO + , readline_putc + , readline_write +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: readline_getc + ****************************************************************************/ + +static int readline_getc(FAR struct rl_common_s *vtbl) +{ + return up_getc(); +} + +/**************************************************************************** + * Name: readline_putc + ****************************************************************************/ + +#ifdef CONFIG_READLINE_ECHO +static void readline_putc(FAR struct rl_common_s *vtbl, int ch) +{ + up_putc(ch); +} +#endif + +/**************************************************************************** + * Name: readline_write + ****************************************************************************/ + +#ifdef CONFIG_READLINE_ECHO +static void readline_write(FAR struct rl_common_s *vtbl, + FAR const char *buffer, size_t buflen) +{ + for (; buflen > 0; buflen--) + { + up_putc(*buffer++); + } +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: std_readline + * + * readline() reads in at most one less than 'buflen' characters from + * 'instream' and stores them into the buffer pointed to by 'buf'. + * Characters are echoed on 'outstream'. Reading stops after an EOF or a + * newline. If a newline is read, it is stored into the buffer. A null + * terminator is stored after the last character in the buffer. + * + * This version of realine assumes that we are reading and writing to + * a VT100 console. This will not work well if 'instream' or 'outstream' + * corresponds to a raw byte steam. + * + * This function is inspired by the GNU readline but is an entirely + * different creature. + * + * Input Parameters: + * buf - The user allocated buffer to be filled. + * buflen - the size of the buffer. + * instream - The stream to read characters from + * outstream - The stream to each characters to. + * + * Returned values: + * On success, the (positive) number of bytes transferred is returned. + * EOF is returned to indicate either an end of file condition or a + * failure. + * + **************************************************************************/ + +ssize_t std_readline(FAR char *buf, int buflen) +{ + return readline_common((FAR struct rl_common_s *)&g_stdreadline, + buf, buflen); +} -- cgit v1.2.3