CELIX-415: Refactors shell tui and ads support for configuring and detecting if ANSI control sequences are useable
Project: http://git-wip-us.apache.org/repos/asf/celix/repo Commit: http://git-wip-us.apache.org/repos/asf/celix/commit/7d379221 Tree: http://git-wip-us.apache.org/repos/asf/celix/tree/7d379221 Diff: http://git-wip-us.apache.org/repos/asf/celix/diff/7d379221 Branch: refs/heads/master Commit: 7d379221262f4625faf62f3877a8192ec680638d Parents: 0a78d5d Author: Pepijn Noltes <[email protected]> Authored: Sun Nov 12 21:21:23 2017 +0100 Committer: Pepijn Noltes <[email protected]> Committed: Sun Nov 12 21:21:23 2017 +0100 ---------------------------------------------------------------------- .../private/include/dm_shell_list_command.h | 8 +- .../private/src/dm_shell_activator.c | 46 ++- .../private/src/dm_shell_list_command.c | 18 +- dependency_manager/readme.md | 13 + .../public/include/service_tracker_customizer.h | 2 + shell_tui/README.md | 10 +- shell_tui/private/include/shell_tui.h | 20 +- shell_tui/private/src/activator.c | 86 +++-- shell_tui/private/src/history.c | 7 - shell_tui/private/src/shell_tui.c | 369 ++++++++++++------- 10 files changed, 362 insertions(+), 217 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/dependency_manager/private/include/dm_shell_list_command.h ---------------------------------------------------------------------- diff --git a/dependency_manager/private/include/dm_shell_list_command.h b/dependency_manager/private/include/dm_shell_list_command.h index e8405c2..6ab0581 100644 --- a/dependency_manager/private/include/dm_shell_list_command.h +++ b/dependency_manager/private/include/dm_shell_list_command.h @@ -26,10 +26,14 @@ extern "C" { #include <stdlib.h> #include <string.h> - #include "command.h" -void dmListCommand_execute(bundle_context_pt context, char * line, FILE *out, FILE *err); +typedef struct dm_command_handle { + bundle_context_pt context; + bool useColors; +} dm_command_handle_t; + +void dmListCommand_execute(dm_command_handle_t* handle, char * line, FILE *out, FILE *err); #ifdef __cplusplus } http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/dependency_manager/private/src/dm_shell_activator.c ---------------------------------------------------------------------- diff --git a/dependency_manager/private/src/dm_shell_activator.c b/dependency_manager/private/src/dm_shell_activator.c index 43b8e3e..833051b 100644 --- a/dependency_manager/private/src/dm_shell_activator.c +++ b/dependency_manager/private/src/dm_shell_activator.c @@ -31,9 +31,12 @@ #include "dm_shell_list_command.h" +#define DM_SHELL_USE_ANSI_COLORS "DM_SHELL_USE_ANSI_COLORS" + struct bundle_instance { service_registration_pt reg; - command_service_pt dmCommand; + command_service_t dmCommand; + dm_command_handle_t dmHandle; }; typedef struct bundle_instance * bundle_instance_pt; @@ -50,6 +53,11 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData return CELIX_ENOMEM; } + bi->dmHandle.context = context; + const char* config = NULL; + bundleContext_getPropertyWithDefault(context, DM_SHELL_USE_ANSI_COLORS, "true", &config); + bi->dmHandle.useColors = config != NULL && strncmp("true", config, 5) == 0; + (*userData) = bi; return CELIX_SUCCESS; @@ -58,27 +66,18 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) { celix_status_t status = CELIX_SUCCESS; bundle_instance_pt bi = (bundle_instance_pt) userData; - command_service_pt commandService = calloc(1, sizeof(*commandService)); - - if (commandService != NULL) { - commandService->handle = context; - commandService->executeCommand = (void *)dmListCommand_execute; - - properties_pt props = properties_create(); - properties_set(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE); - properties_set(props, OSGI_SHELL_COMMAND_NAME, "dm"); - properties_set(props, OSGI_SHELL_COMMAND_USAGE, "dm"); - properties_set(props, OSGI_SHELL_COMMAND_DESCRIPTION, - "Gives an overview of the component managemend by a dependency manager."); - - bi->dmCommand = commandService; - - status = bundleContext_registerService(context, (char *) OSGI_SHELL_COMMAND_SERVICE_NAME, commandService, props, - &bi->reg); - } else { - status = CELIX_ENOMEM; - free(commandService); - } + + bi->dmCommand.handle = &bi->dmHandle; + bi->dmCommand.executeCommand = (void *)dmListCommand_execute; + + properties_pt props = properties_create(); + properties_set(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE); + properties_set(props, OSGI_SHELL_COMMAND_NAME, "dm"); + properties_set(props, OSGI_SHELL_COMMAND_USAGE, "dm"); + properties_set(props, OSGI_SHELL_COMMAND_DESCRIPTION, + "Gives an overview of the component managemend by a dependency manager."); + + status = bundleContext_registerService(context, OSGI_SHELL_COMMAND_SERVICE_NAME, &bi->dmCommand, props, &bi->reg); return status; } @@ -91,9 +90,6 @@ celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context) celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt context) { bundle_instance_pt bi = (bundle_instance_pt) userData; - if (bi != NULL) { - free(bi->dmCommand); - } free(bi); return CELIX_SUCCESS; } http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/dependency_manager/private/src/dm_shell_list_command.c ---------------------------------------------------------------------- diff --git a/dependency_manager/private/src/dm_shell_list_command.c b/dependency_manager/private/src/dm_shell_list_command.c index c6fe68c..1600710 100644 --- a/dependency_manager/private/src/dm_shell_list_command.c +++ b/dependency_manager/private/src/dm_shell_list_command.c @@ -26,6 +26,7 @@ #include <stdlib.h> #include <string.h> #include <dm_dependency_manager.h> +#include <dm_shell_list_command.h> #include "dm_info.h" #include "service_reference.h" #include "array_list.h" @@ -39,28 +40,25 @@ static const char * const WARNING_COLOR = "\033[93m"; static const char * const NOK_COLOR = "\033[91m"; static const char * const END_COLOR = "\033[m"; -void dmListCommand_execute(bundle_context_pt context, char * line, FILE *out, FILE *err) { +void dmListCommand_execute(dm_command_handle_t* handle, char * line, FILE *out, FILE *err) { + array_list_pt servRefs = NULL; int i; - bundleContext_getServiceReferences(context, DM_INFO_SERVICE_NAME ,NULL, &servRefs); + bundleContext_getServiceReferences(handle->context, DM_INFO_SERVICE_NAME ,NULL, &servRefs); if(servRefs==NULL){ fprintf(out, "Invalid dm_info ServiceReferences List\n"); return; } - char *term = getenv("TERM"); - bool colors = false; - if (strcmp("xterm-256color", term) == 0) { - colors = true; - } + bool colors = handle->useColors; for(i = 0; i < arrayList_size(servRefs); i++) { dm_dependency_manager_info_pt info = NULL; dm_info_service_pt infoServ = NULL; service_reference_pt servRef = NULL; servRef = arrayList_get(servRefs, i); - bundleContext_getService(context, servRef, (void**)&infoServ); + bundleContext_getService(handle->context, servRef, (void**)&infoServ); infoServ->getInfo(infoServ->handle, &info); int cmpCnt; @@ -117,8 +115,8 @@ void dmListCommand_execute(bundle_context_pt context, char * line, FILE *out, FI infoServ->destroyInfo(infoServ->handle, info); - bundleContext_ungetService(context,servRef,NULL); - bundleContext_ungetServiceReference(context,servRef); + bundleContext_ungetService(handle->context, servRef, NULL); + bundleContext_ungetServiceReference(handle->context, servRef); } http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/dependency_manager/readme.md ---------------------------------------------------------------------- diff --git a/dependency_manager/readme.md b/dependency_manager/readme.md index 1289c46..864fc3a 100644 --- a/dependency_manager/readme.md +++ b/dependency_manager/readme.md @@ -113,6 +113,19 @@ celix_status_t dm_destroy(void * userData, bundle_context_pt context, dm_depende return CELIX_SUCCESS; } ``` + + +### Dependency Manager Shell support + +There is support for retrieving information of the dm components with +use of the `dm` command. This command will print all known dm component, +their state, provided interfaces and required interfaces. + +#### Dependency Manager Shell Config Options + +- DM_SHELL_USE_ANSI_COLORS - Wether to use ANSI colors when printing dm +info. default is true. + ### References For more information examples please see http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/framework/public/include/service_tracker_customizer.h ---------------------------------------------------------------------- diff --git a/framework/public/include/service_tracker_customizer.h b/framework/public/include/service_tracker_customizer.h index 660bbb4..19672d8 100644 --- a/framework/public/include/service_tracker_customizer.h +++ b/framework/public/include/service_tracker_customizer.h @@ -44,6 +44,8 @@ typedef celix_status_t (*modified_callback_pt)(void *handle, service_reference_p typedef celix_status_t (*removed_callback_pt)(void *handle, service_reference_pt reference, void *service); typedef struct serviceTrackerCustomizer *service_tracker_customizer_pt; +typedef struct serviceTrackerCustomizer service_tracker_customizer_t; + FRAMEWORK_EXPORT celix_status_t serviceTrackerCustomizer_create(void *handle, adding_callback_pt addingFunction, http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/shell_tui/README.md ---------------------------------------------------------------------- diff --git a/shell_tui/README.md b/shell_tui/README.md index 4cdcda4..a637214 100644 --- a/shell_tui/README.md +++ b/shell_tui/README.md @@ -1,6 +1,12 @@ -## Shell TUI +# Shell TUI The Celix Shell TUI implements a textual user interface for the Celix Shell. -###### CMake option +## CMake option BUILD_SHELL_TUI=ON + +## Config options + +- SHELL_USE_ANSI_CONTROL_SEQUENCES - Wether to use ANSI control +sequences to support backspace, left, up, etc key commands in the +shell tui. Default is true if a TERM environment is set else false. http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/shell_tui/private/include/shell_tui.h ---------------------------------------------------------------------- diff --git a/shell_tui/private/include/shell_tui.h b/shell_tui/private/include/shell_tui.h index 29f5eda..b561ff6 100644 --- a/shell_tui/private/include/shell_tui.h +++ b/shell_tui/private/include/shell_tui.h @@ -29,24 +29,16 @@ #include <stdlib.h> -#include "bundle_context.h" #include "celix_threads.h" -#include "service_reference.h" #include "shell.h" -#include "service_tracker.h" -struct shellTuiActivator { - bundle_context_pt context; - shell_service_pt shell; - service_tracker_pt tracker; - bool running; - celix_thread_t runnable; - celix_thread_mutex_t mutex; -}; +typedef struct shell_tui shell_tui_t ; -typedef struct shellTuiActivator * shell_tui_activator_pt; +shell_tui_t* shellTui_create(bool useAnsiControlSequences); +celix_status_t shellTui_start(shell_tui_t* shellTui); +celix_status_t shellTui_stop(shell_tui_t* shellTui); +void shellTui_destroy(shell_tui_t* shellTui); -celix_status_t shellTui_start(shell_tui_activator_pt activator); -celix_status_t shellTui_stop(shell_tui_activator_pt activator); +celix_status_t shellTui_setShell(shell_tui_t* shellTui, shell_service_t* svc); #endif /* SHELL_TUI_H_ */ http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/shell_tui/private/src/activator.c ---------------------------------------------------------------------- diff --git a/shell_tui/private/src/activator.c b/shell_tui/private/src/activator.c index ec28088..f25dd5b 100644 --- a/shell_tui/private/src/activator.c +++ b/shell_tui/private/src/activator.c @@ -24,6 +24,7 @@ * \copyright Apache License, Version 2.0 */ #include <stdlib.h> +#include <string.h> #include "bundle_context.h" #include "bundle_activator.h" @@ -32,36 +33,65 @@ #include "shell_tui.h" #include "service_tracker.h" +#define SHELL_USE_ANSI_CONTROL_SEQUENCES "SHELL_USE_ANSI_CONTROL_SEQUENCES" + +typedef struct shell_tui_activator { + shell_tui_t* shellTui; + service_tracker_pt tracker; + shell_service_t* currentSvc; + bool useAnsiControlSequences; +} shell_tui_activator_t; + static celix_status_t activator_addShellService(void *handle, service_reference_pt ref, void *svc) { - shell_tui_activator_pt act = (shell_tui_activator_pt) handle; - celixThreadMutex_lock(&act->mutex); - act->shell = svc; - celixThreadMutex_unlock(&act->mutex); + shell_tui_activator_t* act = (shell_tui_activator_t*) handle; + act->currentSvc = svc; + shellTui_setShell(act->shellTui, svc); return CELIX_SUCCESS; } static celix_status_t activator_removeShellService(void *handle, service_reference_pt ref, void *svc) { - shell_tui_activator_pt act = (shell_tui_activator_pt) handle; - celixThreadMutex_lock(&act->mutex); - if (act->shell == svc) { - act->shell = NULL; + shell_tui_activator_t* act = (shell_tui_activator_t*) handle; + if (act->currentSvc == svc) { + act->currentSvc = NULL; + shellTui_setShell(act->shellTui, NULL); } - celixThreadMutex_unlock(&act->mutex); return CELIX_SUCCESS; } celix_status_t bundleActivator_create(bundle_context_pt context, void **userData) { celix_status_t status = CELIX_SUCCESS; - shell_tui_activator_pt activator = (shell_tui_activator_pt) calloc(1, sizeof(*activator)); + shell_tui_activator_t* activator = calloc(1, sizeof(*activator)); + + if (activator != NULL) { + bool useCommands; + const char* config = NULL; + bundleContext_getProperty(context, SHELL_USE_ANSI_CONTROL_SEQUENCES, &config); + if (config != NULL) { + useCommands = strncmp("true", config, 5) == 0; + } else { + char *term = getenv("TERM"); + useCommands = term != NULL; + } - if (activator) { - activator->shell = NULL; - celixThreadMutex_create(&activator->mutex, NULL); - (*userData) = activator; + activator->shellTui = shellTui_create(useCommands); + + service_tracker_customizer_t* cust = NULL; + serviceTrackerCustomizer_create(activator, NULL, activator_addShellService, NULL, activator_removeShellService, &cust); + serviceTracker_create(context, OSGI_SHELL_SERVICE_NAME, cust, &activator->tracker); } - else { + + if (activator != NULL && activator->shellTui != NULL) { + (*userData) = activator; + } else { + if (activator != NULL) { + shellTui_destroy(activator->shellTui); + if (activator->tracker != NULL) { + serviceTracker_destroy(activator->tracker); + } + } + free(activator); status = CELIX_ENOMEM; } @@ -71,39 +101,33 @@ celix_status_t bundleActivator_create(bundle_context_pt context, void **userData celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) { celix_status_t status = CELIX_SUCCESS; - shell_tui_activator_pt act = (shell_tui_activator_pt) userData; + shell_tui_activator_t* act = (shell_tui_activator_t*) userData; - service_tracker_customizer_pt cust = NULL; - serviceTrackerCustomizer_create(userData, NULL, activator_addShellService, NULL, activator_removeShellService, &cust); - serviceTracker_create(context, (char *) OSGI_SHELL_SERVICE_NAME, cust, &act->tracker); + act->currentSvc = NULL; serviceTracker_open(act->tracker); - - shellTui_start(act); + shellTui_start(act->shellTui); return status; } celix_status_t bundleActivator_stop(void * userData, bundle_context_pt context) { celix_status_t status = CELIX_SUCCESS; - shell_tui_activator_pt act = (shell_tui_activator_pt) userData; + shell_tui_activator_t* act = (shell_tui_activator_t*) userData; if (act != NULL) { - shellTui_stop(act); - if (act->tracker != NULL) { - serviceTracker_close(act->tracker); - } + serviceTracker_close(act->tracker); + shellTui_stop(act->shellTui); } return status; } celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt context) { - shell_tui_activator_pt activator = (shell_tui_activator_pt) userData; + shell_tui_activator_t* act = (shell_tui_activator_t*) userData; - if (activator->tracker != NULL) { - serviceTracker_destroy(activator->tracker); - } - free(activator); + shellTui_destroy(act->shellTui); + serviceTracker_destroy(act->tracker); + free(act); return CELIX_SUCCESS; } http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/shell_tui/private/src/history.c ---------------------------------------------------------------------- diff --git a/shell_tui/private/src/history.c b/shell_tui/private/src/history.c index ee77f2e..e58c502 100644 --- a/shell_tui/private/src/history.c +++ b/shell_tui/private/src/history.c @@ -16,13 +16,6 @@ *specific language governing permissions and limitations *under the License. */ -/* - * activator.c - * - * \date Jan 15, 2016 - * \author <a href="mailto:[email protected]">Apache Celix Project Team</a> - * \copyright Apache License, Version 2.0 - */ #include "history.h" #include <stdlib.h> http://git-wip-us.apache.org/repos/asf/celix/blob/7d379221/shell_tui/private/src/shell_tui.c ---------------------------------------------------------------------- diff --git a/shell_tui/private/src/shell_tui.c b/shell_tui/private/src/shell_tui.c index 7c2fa3b..f421e6e 100644 --- a/shell_tui/private/src/shell_tui.c +++ b/shell_tui/private/src/shell_tui.c @@ -39,6 +39,7 @@ #include "utils.h" #include <signal.h> #include <unistd.h> +#include <fcntl.h> #include "history.h" #define LINE_SIZE 256 @@ -56,6 +57,25 @@ #define KEY_DEL1 '3' #define KEY_DEL2 '~' +struct shell_tui { + celix_thread_mutex_t mutex; //protects shell + shell_service_t* shell; + celix_thread_t thread; + + int readPipeFd; + int writePipeFd; + + bool useAnsiControlSequences; +}; + +typedef struct shell_context { + char in[LINE_SIZE]; + char buffer[LINE_SIZE]; + char dline[LINE_SIZE]; + int pos; + history_t* hist; +} shell_context_t; + // static function declarations static void remove_newlines(char* line); static void clearLine(); @@ -63,161 +83,254 @@ static void cursorLeft(int n); static void writeLine(const char*line, int pos); static int autoComplete(shell_service_pt shellSvc, char *in, int cursorPos, size_t maxLen); static void* shellTui_runnable(void *data); +static void shellTui_parseInputForControl(shell_tui_t* shellTui, shell_context_t* ctx); +static void shellTui_parseInput(shell_tui_t* shellTui, shell_context_t* ctx); +static void writePrompt(void); +shell_tui_t* shellTui_create(bool useAnsiControlSequences) { + shell_tui_t* result = calloc(1, sizeof(*result)); + if (result != NULL) { + result->useAnsiControlSequences = useAnsiControlSequences; + celixThreadMutex_create(&result->mutex, NULL); + } + return result; +} -celix_status_t shellTui_start(shell_tui_activator_pt activator) { - +celix_status_t shellTui_start(shell_tui_t* shellTui) { celix_status_t status = CELIX_SUCCESS; - activator->running = true; - celixThread_create(&activator->runnable, NULL, shellTui_runnable, activator); + int fds[2]; + int rc = pipe(fds); + if (rc == 0) { + shellTui->readPipeFd = fds[0]; + shellTui->writePipeFd = fds[1]; + fcntl(shellTui->writePipeFd, F_SETFL, O_NONBLOCK); + celixThread_create(&shellTui->thread, NULL, shellTui_runnable, shellTui); + } else { + fprintf(stderr, "Cannot create pipe"); + status = CELIX_BUNDLE_EXCEPTION; + } return status; } -celix_status_t shellTui_stop(shell_tui_activator_pt act) { +celix_status_t shellTui_stop(shell_tui_t* shellTui) { celix_status_t status = CELIX_SUCCESS; - act->running = false; - celixThread_kill(act->runnable, SIGUSR1); - celixThread_join(act->runnable, NULL); + write(shellTui->writePipeFd, "\0", 1); //trigger select to stop + celixThread_join(shellTui->thread, NULL); + close(shellTui->writePipeFd); + close(shellTui->readPipeFd); return status; } +void shellTui_destroy(shell_tui_t* shellTui) { + if (shellTui == NULL) return; + + celixThreadMutex_destroy(&shellTui->mutex); + free(shellTui); +} +celix_status_t shellTui_setShell(shell_tui_t* shellTui, shell_service_t* svc) { + celixThreadMutex_lock(&shellTui->mutex); + shellTui->shell = svc; + celixThreadMutex_unlock(&shellTui->mutex); + return CELIX_SUCCESS; +} static void* shellTui_runnable(void *data) { - shell_tui_activator_pt act = (shell_tui_activator_pt) data; + shell_tui_t* shellTui = (shell_tui_t*) data; - char in[LINE_SIZE] = ""; - char buffer[LINE_SIZE]; - int pos = 0; - char dline[LINE_SIZE]; - fd_set rfds; - struct timeval tv; + //setup shell context + shell_context_t ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.hist = historyCreate(); + //setup term struct termios term_org, term_new; - tcgetattr(STDIN_FILENO, &term_org); - term_new = term_org; - term_new.c_lflag &= ~( ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &term_new); + if (shellTui->useAnsiControlSequences) { + tcgetattr(STDIN_FILENO, &term_org); + term_new = term_org; + term_new.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &term_new); + } - history_t *hist = historyCreate(); + //setup file descriptors + fd_set rfds; + int nfds = shellTui->writePipeFd > STDIN_FILENO ? (shellTui->writePipeFd +1) : (STDIN_FILENO + 1); - while (act->running) { - char * line = NULL; - writeLine(in, pos); + for (;;) { + if (shellTui->useAnsiControlSequences) { + writeLine(ctx.in, ctx.pos); + } else { + writePrompt(); + } FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); + FD_SET(shellTui->readPipeFd, &rfds); - tv.tv_sec = 1; - tv.tv_usec = 0; - - if (select(1, &rfds, NULL, NULL, &tv) > 0) { - int nr_chars = read(STDIN_FILENO, buffer, LINE_SIZE-pos); - for(int bufpos = 0; bufpos < nr_chars; bufpos++) { - if (buffer[bufpos] == KEY_ESC1 && buffer[bufpos+1] == KEY_ESC2) { - switch (buffer[bufpos+2]) { - case KEY_UP: - if(historySize(hist) > 0) { - strncpy(in, historyGetPrevLine(hist), LINE_SIZE); - pos = strlen(in); - writeLine(in, pos); - } - break; - case KEY_DOWN: - if(historySize(hist) > 0) { - strncpy(in, historyGetNextLine(hist), LINE_SIZE); - pos = strlen(in); - writeLine(in, pos); - } - break; - case KEY_RIGHT: - if (pos < strlen(in)) { - pos++; - } - writeLine(in, pos); - break; - case KEY_LEFT: - if (pos > 0) { - pos--; - } - writeLine(in, pos); - break; - case KEY_DEL1: - if(buffer[bufpos+3] == KEY_DEL2) { - bufpos++; // delete cmd takes 4 chars - int len = strlen(in); - if (pos < len) { - for (int i = pos; i <= len; i++) { - in[i] = in[i + 1]; - } - } - writeLine(in, pos); - } - break; - default: - // Unsupported char, do nothing - break; - } - bufpos+=2; - continue; - } else if(buffer[bufpos] == KEY_BACKSPACE) { // backspace - if(pos > 0) { - int len = strlen(in); - for(int i = pos-1; i <= len; i++) { - in[i] = in[i+1]; - } - pos--; - } - writeLine(in, pos); - continue; - } else if(buffer[bufpos] == KEY_TAB) { - pos = autoComplete(act->shell, in, pos, LINE_SIZE); - continue; - } else if(buffer[bufpos] != KEY_ENTER) { //text - if(in[pos] == '\0') { - in[pos+1] = '\0'; - } - in[pos] = buffer[bufpos]; - pos++; - writeLine(in, pos); - fflush(stdout); - continue; + if (select(nfds, &rfds, NULL, NULL, NULL) > 0) { + if (FD_ISSET(shellTui->readPipeFd, &rfds)) { + break; //something is written to the pipe -> exit thread + } else if (FD_ISSET(STDIN_FILENO, &rfds)) { + if (shellTui->useAnsiControlSequences) { + shellTui_parseInputForControl(shellTui, &ctx); + } else { + shellTui_parseInput(shellTui, &ctx); } - writeLine(in, pos); - write(STDOUT_FILENO, "\n", 1); - remove_newlines(in); - history_addLine(hist, in); + } + } + } + tcsetattr(STDIN_FILENO, TCSANOW, &term_org); //TODO or after SIGINT + historyDestroy(ctx.hist); + + return NULL; +} - memset(dline, 0, LINE_SIZE); - strncpy(dline, in, LINE_SIZE); +static void shellTui_parseInput(shell_tui_t* shellTui, shell_context_t* ctx) { + char* buffer = ctx->buffer; + char* in = ctx->in; + int pos = ctx->pos; - pos = 0; - in[pos] = '\0'; + char* line = NULL; - line = utils_stringTrim(dline); - if ((strlen(line) == 0) || (act->shell == NULL)) { - continue; - } - historyLineReset(hist); - celixThreadMutex_lock(&act->mutex); - if (act->shell != NULL) { - act->shell->executeCommand(act->shell->shell, line, stdout, stderr); - pos = 0; - nr_chars = 0; - celixThreadMutex_unlock(&act->mutex); + + int nr_chars = read(STDIN_FILENO, buffer, LINE_SIZE-pos-1); + for(int bufpos = 0; bufpos < nr_chars; bufpos++) { + if (buffer[bufpos] == KEY_ENTER) { //end of line -> forward command + line = utils_stringTrim(in); + celixThreadMutex_lock(&shellTui->mutex); + if (shellTui->shell != NULL) { + printf("Providing command '%s' from in '%s'\n", line, in); + shellTui->shell->executeCommand(shellTui->shell->shell, line, stdout, stderr); + } else { + fprintf(stderr, "Shell service not available\n"); + } + celixThreadMutex_unlock(&shellTui->mutex); + pos = 0; + in[pos] = '\0'; + } else { //text + in[pos] = buffer[bufpos]; + in[pos+1] = '\0'; + pos++; + continue; + } + } // for + ctx->pos = pos; +} + +static void shellTui_parseInputForControl(shell_tui_t* shellTui, shell_context_t* ctx) { + char* buffer = ctx->buffer; + char* in = ctx->in; + char* dline = ctx->dline; + history_t* hist = ctx->hist; + int pos = ctx->pos; + + char* line = NULL; + + int nr_chars = read(STDIN_FILENO, buffer, LINE_SIZE-pos-1); + for(int bufpos = 0; bufpos < nr_chars; bufpos++) { + if (buffer[bufpos] == KEY_ESC1 && buffer[bufpos+1] == KEY_ESC2) { + switch (buffer[bufpos+2]) { + case KEY_UP: + if(historySize(hist) > 0) { + strncpy(in, historyGetPrevLine(hist), LINE_SIZE); + pos = strlen(in); + writeLine(in, pos); + } break; - } else { - fprintf(stderr, "Shell service not available\n"); + case KEY_DOWN: + if(historySize(hist) > 0) { + strncpy(in, historyGetNextLine(hist), LINE_SIZE); + pos = strlen(in); + writeLine(in, pos); + } + break; + case KEY_RIGHT: + if (pos < strlen(in)) { + pos++; + } + writeLine(in, pos); + break; + case KEY_LEFT: + if (pos > 0) { + pos--; + } + writeLine(in, pos); + break; + case KEY_DEL1: + if(buffer[bufpos+3] == KEY_DEL2) { + bufpos++; // delete cmd takes 4 chars + int len = strlen(in); + if (pos < len) { + for (int i = pos; i <= len; i++) { + in[i] = in[i + 1]; + } + } + writeLine(in, pos); + } + break; + default: + // Unsupported char, do nothing + break; + } + bufpos+=2; + continue; + } else if (buffer[bufpos] == KEY_BACKSPACE) { // backspace + if(pos > 0) { + int len = strlen(in); + for(int i = pos-1; i <= len; i++) { + in[i] = in[i+1]; } - celixThreadMutex_unlock(&act->mutex); - } // for + pos--; + } + writeLine(in, pos); + continue; + } else if(buffer[bufpos] == KEY_TAB) { + celixThreadMutex_lock(&shellTui->mutex); + if (shellTui != NULL) { + pos = autoComplete(shellTui->shell, in, pos, LINE_SIZE); + } + celixThreadMutex_unlock(&shellTui->mutex); + continue; + } else if (buffer[bufpos] != KEY_ENTER) { //not end of line -> text + if (in[pos] == '\0') { + in[pos+1] = '\0'; + } + in[pos] = buffer[bufpos]; + pos++; + writeLine(in, pos); + fflush(stdout); + continue; } - } - tcsetattr(STDIN_FILENO, TCSANOW, &term_org); - historyDestroy(hist); - return NULL; + //parse enter + writeLine(in, pos); + write(STDOUT_FILENO, "\n", 1); + remove_newlines(in); + history_addLine(hist, in); + + memset(dline, 0, LINE_SIZE); + strncpy(dline, in, LINE_SIZE); + + pos = 0; + in[pos] = '\0'; + + line = utils_stringTrim(dline); + if ((strlen(line) == 0)) { + continue; + } + historyLineReset(hist); + celixThreadMutex_lock(&shellTui->mutex); + if (shellTui->shell != NULL) { + shellTui->shell->executeCommand(shellTui->shell->shell, line, stdout, stderr); + pos = 0; + nr_chars = 0; + } else { + fprintf(stderr, "Shell service not available\n"); + } + celixThreadMutex_unlock(&shellTui->mutex); + } // for + ctx->pos = pos; } static void remove_newlines(char* line) { @@ -242,14 +355,18 @@ static void cursorLeft(int n) { } } -static void writeLine(const char*line, int pos) { +static void writePrompt(void) { + write(STDIN_FILENO, PROMPT, strlen(PROMPT)); +} + +static void writeLine(const char* line, int pos) { clearLine(); write(STDOUT_FILENO, PROMPT, strlen(PROMPT)); write(STDOUT_FILENO, line, strlen(line)); cursorLeft(strlen(line)-pos); } -static int autoComplete(shell_service_pt shellSvc, char *in, int cursorPos, size_t maxLen) { +static int autoComplete(shell_service_t* shellSvc, char *in, int cursorPos, size_t maxLen) { array_list_pt commandList = NULL; array_list_pt possibleCmdList = NULL; shellSvc->getCommands(shellSvc->shell, &commandList);
