diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV index d63f19fc8..efe2560e0 100644 --- a/configure/CONFIG_SITE_ENV +++ b/configure/CONFIG_SITE_ENV @@ -57,8 +57,11 @@ EPICS_TS_NTP_INET= # Prompt string # IOCSH_HISTSIZE # Number of lines of command history to keep. +# IOCSH_HISTEDIT_DISABLE +# Prevents use of readline or equivalent if defined. IOCSH_PS1="epics> " IOCSH_HISTSIZE=50 +IOCSH_HISTEDIT_DISABLE= # Log Server: # EPICS_IOC_LOG_INET diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index b2e5fdc65..2a91b73c0 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,17 @@ --> +

Refactoring of epicsReadline

+ +

The epicsReadline code has been reorganized to allow the commandline history +editor to be disabled at runtime. The EPICS_COMMANDLINE_LIBRARY build setting +still selects the preferred editor, but the new IOCSH_HISTEDIT_DISABLE +environment variable can be set at runtime to disable history editing and make +the IOC or other program use the basic editor instead. This is useful when +starting and controlling an IOC from another program through its stdin and +stdout streams since history editors often insert invisible escape codes into +the stdout stream, making it hard to parse.

+

Callback subsystem API

Added a new macro callbackGetPriority(prio, callback) to the diff --git a/src/libCom/env/envDefs.h b/src/libCom/env/envDefs.h index b2a594f44..c5b947d57 100644 --- a/src/libCom/env/envDefs.h +++ b/src/libCom/env/envDefs.h @@ -69,6 +69,7 @@ epicsShareExtern const ENV_PARAM EPICS_CMD_PROTO_PORT; epicsShareExtern const ENV_PARAM EPICS_AR_PORT; epicsShareExtern const ENV_PARAM IOCSH_PS1; epicsShareExtern const ENV_PARAM IOCSH_HISTSIZE; +epicsShareExtern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; epicsShareExtern const ENV_PARAM *env_param_list[]; diff --git a/src/libCom/osi/epicsReadline.c b/src/libCom/osi/epicsReadline.c new file mode 100644 index 000000000..7cc569014 --- /dev/null +++ b/src/libCom/osi/epicsReadline.c @@ -0,0 +1,149 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Revision-Id$ */ +/* Author: Eric Norum Date: 12DEC2001 */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "envDefs.h" +#include "epicsReadline.h" + +#define EPICS_COMMANDLINE_LIBRARY_EPICS 0 +#define EPICS_COMMANDLINE_LIBRARY_LIBTECLA 1 +#define EPICS_COMMANDLINE_LIBRARY_READLINE 2 +#define EPICS_COMMANDLINE_LIBRARY_READLINE_CURSES 2 +#define EPICS_COMMANDLINE_LIBRARY_READLINE_NCURSES 2 + +#ifndef EPICS_COMMANDLINE_LIBRARY +# define EPICS_COMMANDLINE_LIBRARY EPICS_COMMANDLINE_LIBRARY_EPICS +#endif + +struct osdContext; +struct readlineContext { + FILE *in; + char *line; + struct osdContext *osd; +}; + +static void osdReadlineBegin(struct readlineContext *); +static char * osdReadline(const char *prompt, struct readlineContext *); +static void osdReadlineEnd(struct readlineContext *); + +#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_EPICS + +static void osdReadlineBegin(struct readlineContext * c) {} +static char * osdReadline(const char *prompt, struct readlineContext * c) { return NULL; } +static void osdReadlineEnd(struct readlineContext * c) {} + +#else + +# if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE +# include "gnuReadline.c" +# else +# include "osdReadline.c" +# endif + +#endif + +/* + * Create a command-line context + */ +void * epicsShareAPI +epicsReadlineBegin(FILE *in) +{ + struct readlineContext *readlineContext = malloc(sizeof *readlineContext); + + if (readlineContext) { + readlineContext->in = in; + readlineContext->line = NULL; + if (!envGetConfigParamPtr(&IOCSH_HISTEDIT_DISABLE)) + osdReadlineBegin(readlineContext); + } + return readlineContext; +} + +/* + * Read a line of input + */ +char * epicsShareAPI +epicsReadline (const char *prompt, void *context) +{ + struct readlineContext *readlineContext = context; + FILE *in; + char *line; + int c; /* char is unsigned on some archs, EOF is -ve */ + int linelen = 0; + int linesize = 50; + + if (readlineContext->osd) + return osdReadline(prompt, readlineContext); + + free(readlineContext->line); + readlineContext->line = NULL; + if ((in = readlineContext->in) == NULL) { + in = stdin; + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + } + line = (char *)malloc(linesize); + if (line == NULL) { + printf("Out of memory!\n"); + return NULL; + } + while ((c = getc(in)) != '\n') { + if (c == EOF) { + if (ferror(in)) { + if ((errno == EINTR) || (errno == EPIPE)) { + clearerr(in); + continue; + } + } + free (line); + return NULL; + } + if ((linelen + 1) >= linesize) { + char *cp; + + linesize += 50; + cp = (char *)realloc(line, linesize); + if (cp == NULL) { + printf("Out of memory!\n"); + free(line); + return NULL; + } + line = cp; + } + line[linelen++] = c; + } + line[linelen] = '\0'; + readlineContext->line = line; + return line; +} + +/* + * Destroy a command-line context + */ +void epicsShareAPI +epicsReadlineEnd (void *context) +{ + if (context) { + struct readlineContext *readlineContext = context; + + if (readlineContext->osd) + osdReadlineEnd(readlineContext); + else + free(readlineContext->line); + free(readlineContext); + } +} + diff --git a/src/libCom/osi/os/default/epicsReadline.h b/src/libCom/osi/epicsReadline.h similarity index 66% rename from src/libCom/osi/os/default/epicsReadline.h rename to src/libCom/osi/epicsReadline.h index f6ddf2791..1d0e8b761 100644 --- a/src/libCom/osi/os/default/epicsReadline.h +++ b/src/libCom/osi/epicsReadline.h @@ -1,14 +1,13 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ -#ifndef _EPICS_READLINE_H -#define _EPICS_READLINE_H +#ifndef INC_epicsReadline_H +#define INC_epicsReadline_H #ifdef __cplusplus extern "C" { @@ -25,4 +24,4 @@ epicsShareFunc void epicsShareAPI epicsReadlineEnd (void *context); } #endif -#endif /* _EPICS_READLINE_H */ +#endif /* INC_epicsReadline_H */ diff --git a/src/libCom/osi/os/RTEMS/osdReadline.c b/src/libCom/osi/os/RTEMS/osdReadline.c new file mode 100644 index 000000000..6f1e6732c --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdReadline.c @@ -0,0 +1,70 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Revision-Id$ */ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +struct osdContext {}; + +/* + * Create a command-line context + */ +static void +osdReadlineBegin (struct readlineContext *context) +{ + GetLine *gl; + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 0) + i = 0; + + gl = new_GetLine(200, i * 40); + if (gl) { + context->osd = (struct osdContext *) gl; + if (context->in) + gl_change_terminal(gl, context->in, stdout, NULL); + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + GetLine *gl = (GetLine *) context->osd; + char *line; + + line = gl_get_line(gl, prompt ? prompt : "", NULL, -1); + if (line) { + char *nl = strchr(line, '\n'); + + if (nl) + *nl = '\0'; + return line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd(struct readlineContext *context) +{ + GetLine *gl = (GetLine *) context->osd; + + del_GetLine(gl); +} + diff --git a/src/libCom/osi/os/default/epicsReadline.c b/src/libCom/osi/os/default/epicsReadline.c deleted file mode 100644 index 6c442e376..000000000 --- a/src/libCom/osi/os/default/epicsReadline.c +++ /dev/null @@ -1,376 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Saskatchewan -* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne -* National Laboratory. -* EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* $Revision-Id$ */ -/* Author: Eric Norum Date: 12DEC2001 */ - -#include -#include -#include - -#define epicsExportSharedSymbols -#include "envDefs.h" -#include "epicsReadline.h" - -#define EPICS_COMMANDLINE_LIBRARY_EPICS 0 -#define EPICS_COMMANDLINE_LIBRARY_LIBTECLA 1 -#define EPICS_COMMANDLINE_LIBRARY_READLINE 2 -#define EPICS_COMMANDLINE_LIBRARY_READLINE_CURSES 2 -#define EPICS_COMMANDLINE_LIBRARY_READLINE_NCURSES 2 - -#ifndef EPICS_COMMANDLINE_LIBRARY -#define EPICS_COMMANDLINE_LIBRARY EPICS_COMMANDLINE_LIBRARY_EPICS -#endif - - - -#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_LIBTECLA -#include -#include - -/* - * Create a command-line context - */ -void * epicsShareAPI -epicsReadlineBegin (FILE *in) -{ - GetLine *gl; - long i = 50; - - envGetLongConfigParam(&IOCSH_HISTSIZE, &i); - if (i < 0) i = 0; - gl = new_GetLine(200, i * 40); - if ((gl != NULL) && (in != NULL)) - gl_change_terminal(gl, in, stdout, NULL); - return gl; -} - -/* - * Read a line of input - */ -char * epicsShareAPI -epicsReadline (const char *prompt, void *context) -{ - char *line; - char *nl; - - line = gl_get_line(context, prompt ? prompt : "", NULL, -1); - if ((line != NULL) && ((nl = strchr(line, '\n')) != NULL)) - *nl = '\0'; - return line; -} - -/* - * Destroy a command-line context - */ -void epicsShareAPI -epicsReadlineEnd(void *context) -{ - del_GetLine(context); -} - - -#elif EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE - -#include -#include - -struct readlineContext { - FILE *in; - char *line; -}; - -/* - * Create a command-line context - */ -void * epicsShareAPI -epicsReadlineBegin(FILE *in) -{ - struct readlineContext *readlineContext; - - readlineContext = malloc(sizeof *readlineContext); - if (readlineContext != NULL) { - readlineContext->in = in; - readlineContext->line = NULL; - if (in == NULL) { - long i = 50; - - envGetLongConfigParam(&IOCSH_HISTSIZE, &i); - if (i < 0) i = 0; - stifle_history (i); - rl_bind_key ('\t', rl_insert); - } - } - return readlineContext; -} - -/* - * Read a line of input - */ -char * epicsShareAPI -epicsReadline (const char *prompt, void *context) -{ - struct readlineContext *readlineContext = context; - - int c; /* char is unsigned on some archs, EOF is -ve */ - char *line = NULL; - int linelen = 0; - int linesize = 50; - - free (readlineContext->line); - readlineContext->line = NULL; - if (readlineContext->in == NULL) { - line = readline (prompt); - } - else { - line = (char *)malloc (linesize * sizeof *line); - if (line == NULL) { - printf ("Out of memory!\n"); - return NULL; - } - if (prompt) { - fputs (prompt, stdout); - fflush (stdout); - } - while ((c = getc (readlineContext->in)) != '\n') { - if (c == EOF) { - free (line); - line = NULL; - break; - } - if ((linelen + 1) >= linesize) { - char *cp; - - linesize += 50; - cp = (char *)realloc (line, linesize * sizeof *line); - if (cp == NULL) { - printf ("Out of memory!\n"); - free (line); - line = NULL; - break; - } - line = cp; - } - line[linelen++] = c; - } - if (line) - line[linelen] = '\0'; - } - readlineContext->line = line; - if (line && line[0] != '\0') - add_history (line); - return line; -} - -/* - * Destroy a command-line context - */ -void epicsShareAPI -epicsReadlineEnd (void *context) -{ - struct readlineContext *readlineContext = context; - - if (readlineContext) { - free(readlineContext->line); - free(readlineContext); - } -} - - -#elif EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_EPICS - -#if defined(vxWorks) - -#include -#include -#define LEDLIB_LINESIZE 1000 - -#ifndef _WRS_VXWORKS_MAJOR -typedef int LED_ID; -#endif - -struct readlineContext { - LED_ID ledId; - char line[LEDLIB_LINESIZE]; - FILE *in; -}; - -/* - * Create a command-line context - */ -void * epicsShareAPI -epicsReadlineBegin(FILE *in) -{ - struct readlineContext *readlineContext; - - readlineContext = malloc(sizeof *readlineContext); - if (readlineContext != NULL) { - readlineContext->ledId = (LED_ID) ERROR; - readlineContext->in = in; - if (in == NULL) { - long i = 50; - - envGetLongConfigParam(&IOCSH_HISTSIZE, &i); - if (i < 1) i = 1; - readlineContext->ledId = ledOpen(fileno(stdin), fileno(stdout), i); - if (readlineContext->ledId == (LED_ID) ERROR) { - readlineContext->in = stdin; - printf("Warning -- Unabled to allocate space for command-line history.\n"); - printf("Warning -- Command-line editting disabled.\n"); - } - } - } - return readlineContext; -} - -/* - * Read a line of input - */ -char * epicsShareAPI -epicsReadline (const char *prompt, void *context) -{ - struct readlineContext *readlineContext = context; - int i; - - if (prompt) { - fputs(prompt, stdout); - fflush(stdout); - } - if (readlineContext->ledId != (LED_ID) ERROR) { - i = ledRead(readlineContext->ledId, readlineContext->line, LEDLIB_LINESIZE-1); - if (i < 0) - return NULL; - } - else { - if (fgets(readlineContext->line, LEDLIB_LINESIZE, readlineContext->in) == NULL) - return NULL; - i = strlen(readlineContext->line); - } - if ((i >= 1) && (readlineContext->line[i-1] == '\n')) - readlineContext->line[i-1] = '\0'; - else - readlineContext->line[i] = '\0'; - return readlineContext->line; -} - -/* - * Destroy a command-line context - */ -void epicsShareAPI -epicsReadlineEnd (void *context) -{ - struct readlineContext *readlineContext = context; - - if (readlineContext) { - if (readlineContext->ledId != (LED_ID) ERROR) - ledClose(readlineContext->ledId); - free(readlineContext); - } -} - -#else /* !vxWorks */ - -struct readlineContext { - FILE *in; - char *line; -}; - -/* - * Create a command-line context - */ -void * epicsShareAPI -epicsReadlineBegin(FILE *in) -{ - struct readlineContext *readlineContext; - - readlineContext = malloc(sizeof *readlineContext); - if (readlineContext != NULL) { - readlineContext->in = in; - readlineContext->line = NULL; - } - return readlineContext; -} - -/* - * Read a line of input - */ -char * epicsShareAPI -epicsReadline (const char *prompt, void *context) -{ - struct readlineContext *readlineContext = context; - - int c; /* char is unsigned on some archs, EOF is -ve */ - char *line = NULL; - int linelen = 0; - int linesize = 50; - FILE *in; - - free (readlineContext->line); - readlineContext->line = NULL; - if ((in = readlineContext->in) == NULL) { - in = stdin; - if (prompt != NULL) { - fputs (prompt, stdout); - fflush (stdout); - } - } - line = (char *)malloc (linesize * sizeof *line); - if (line == NULL) { - printf ("Out of memory!\n"); - return NULL; - } - while ((c = getc (in)) != '\n') { - if (c == EOF) { - if (ferror(in)) { - if ((errno == EINTR) || (errno == EPIPE)) { - clearerr(in); - continue; - } - } - free (line); - return NULL; - } - if ((linelen + 1) >= linesize) { - char *cp; - - linesize += 50; - cp = (char *)realloc (line, linesize * sizeof *line); - if (cp == NULL) { - printf ("Out of memory!\n"); - free (line); - return NULL; - } - line = cp; - } - line[linelen++] = c; - } - line[linelen] = '\0'; - readlineContext->line = line; - return line; -} - -/* - * Destroy a command-line context - */ -void epicsShareAPI -epicsReadlineEnd (void *context) -{ - struct readlineContext *readlineContext = context; - - if (readlineContext) { - free(readlineContext->line); - free(readlineContext); - } -} - -#endif /* !vxWorks */ - -#else - -# error "Unsupported EPICS_COMMANDLINE_LIBRARY" - -#endif diff --git a/src/libCom/osi/os/default/gnuReadline.c b/src/libCom/osi/os/default/gnuReadline.c new file mode 100644 index 000000000..d24589e77 --- /dev/null +++ b/src/libCom/osi/os/default/gnuReadline.c @@ -0,0 +1,106 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Revision-Id$ */ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +struct osdContext {} present; + +/* + * Create a command-line context + */ +static void +osdReadlineBegin(struct readlineContext *context) +{ + context->osd = &present; + if (context->in == NULL) { + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 0) + i = 0; + stifle_history(i); + rl_bind_key('\t', rl_insert); + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + char *line; + + free(context->line); + context->line = NULL; + if (context->in == NULL) { + line = readline(prompt); + } + else { + int c; /* char is unsigned on some archs; EOF is -ve */ + int linelen = 0; + int linesize = 50; + + line = malloc(linesize); + if (line == NULL) { + printf("Out of memory!\n"); + return NULL; + } + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + while ((c = getc(context->in)) != '\n') { + if (c == EOF) { + free(line); + line = NULL; + break; + } + if ((linelen + 1) >= linesize) { + char *cp; + + linesize += 50; + cp = (char *)realloc(line, linesize); + if (cp == NULL) { + printf ("Out of memory!\n"); + free(line); + line = NULL; + break; + } + line = cp; + } + line[linelen++] = c; + } + if (line) + line[linelen] = '\0'; + } + context->line = line; + if (line && *line) + add_history(line); + return line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd (struct readlineContext *context) +{ + if (context->osd) { + free(context->line); + } +} + diff --git a/src/libCom/osi/os/vxWorks/osdReadline.c b/src/libCom/osi/os/vxWorks/osdReadline.c new file mode 100644 index 000000000..e0208893e --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdReadline.c @@ -0,0 +1,101 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Revision-Id$ */ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +/* FIXME: Remove line-lenth limitation */ +#define LEDLIB_LINESIZE 1000 + +#ifndef _WRS_VXWORKS_MAJOR +typedef int LED_ID; +#endif + +struct osdContext { + LED_ID ledId; + char line[LEDLIB_LINESIZE]; +}; + +/* + * Create a command-line context + */ +static void +osdReadlineBegin(struct readlineContext *context) +{ + struct osdContext osd = malloc(sizeof *osd); + + if (osd != NULL) { + osd->ledId = (LED_ID) ERROR; + if (context->in == NULL) { + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 1) + i = 1; + + osd->ledId = ledOpen(fileno(stdin), fileno(stdout), i); + if (osd->ledId == (LED_ID) ERROR) { + context->in = stdin; + printf("Warning -- Unabled to allocate space for command-line history.\n"); + printf("Warning -- Command-line editting disabled.\n"); + } + } + context->osd = osd; + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + struct osdContext *osd = context->osd; + int i; + + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + if (osd->ledId != (LED_ID) ERROR) { + i = ledRead(osd->ledId, osd->line, LEDLIB_LINESIZE-1); + if (i < 0) + return NULL; + } + else { + if (fgets(osd->line, LEDLIB_LINESIZE, context->in) == NULL) + return NULL; + i = strlen(osd->line); + } + if ((i >= 1) && (osd->line[i-1] == '\n')) + osd->line[i-1] = '\0'; + else + osd->line[i] = '\0'; + return osd->line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd (struct readlineContext *context) +{ + LED_ID ledId = context->osd->ledId; + + if (ledId != (LED_ID) ERROR) + ledClose(ledId); + free(context->osd); +} +