You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by je...@apache.org on 2019/12/05 12:36:17 UTC

[mynewt-core] branch master updated (7bba744 -> 8615b0d)

This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git.


    from 7bba744  hw/scripts: Fix typo
     new aa42b4b  sys/console: Add history provider api
     new ecc5cfe  sys/console: Add in-RAM implementation of history
     new 5defd58  sys/console: Use separate package for history
     new dadac27  sys/console: Add log-back history provider
     new c29bde7  sys/console: Add autocomplete from history
     new 8615b0d  sys/console: Clean up syscfg value names

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../console/full/history_log}/pkg.yml              |  12 +-
 sys/console/full/history_log/src/history_log.c     | 358 +++++++++++++++++++
 sys/{fault => console/full/history_log}/syscfg.yml |  37 +-
 .../console/full/history_ram}/pkg.yml              |  10 +-
 sys/console/full/history_ram/src/history_ram.c     | 234 +++++++++++++
 .../full/history_ram}/syscfg.yml                   |  15 +-
 sys/console/full/include/console/history.h         |  87 +++++
 sys/console/full/pkg.yml                           |   4 +
 sys/console/full/src/console.c                     | 382 +++++++++------------
 sys/console/full/syscfg.yml                        |  31 +-
 10 files changed, 898 insertions(+), 272 deletions(-)
 copy {test/testutil => sys/console/full/history_log}/pkg.yml (82%)
 create mode 100644 sys/console/full/history_log/src/history_log.c
 copy sys/{fault => console/full/history_log}/syscfg.yml (59%)
 copy {test/testutil => sys/console/full/history_ram}/pkg.yml (82%)
 create mode 100644 sys/console/full/history_ram/src/history_ram.c
 copy sys/{flash_map => console/full/history_ram}/syscfg.yml (77%)
 create mode 100644 sys/console/full/include/console/history.h


[mynewt-core] 04/06: sys/console: Add log-back history provider

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit dadac27a303131673ee8b738beaefbb6b820771d
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Wed Nov 13 15:56:53 2019 +0100

    sys/console: Add log-back history provider
    
    This adds code that can store console history in system
    log.
---
 sys/console/full/{ => history_log}/pkg.yml         |  16 +-
 sys/console/full/history_log/src/history_log.c     | 358 +++++++++++++++++++++
 .../full/{pkg.yml => history_log/syscfg.yml}       |  40 +--
 sys/console/full/pkg.yml                           |   2 +
 sys/console/full/syscfg.yml                        |   1 +
 5 files changed, 387 insertions(+), 30 deletions(-)

diff --git a/sys/console/full/pkg.yml b/sys/console/full/history_log/pkg.yml
similarity index 72%
copy from sys/console/full/pkg.yml
copy to sys/console/full/history_log/pkg.yml
index a035e28..0b4f904 100644
--- a/sys/console/full/pkg.yml
+++ b/sys/console/full/history_log/pkg.yml
@@ -6,7 +6,7 @@
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-# 
+#
 #  http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing,
@@ -17,8 +17,8 @@
 # under the License.
 #
 
-pkg.name: sys/console/full
-pkg.description: Text-based IO interface.
+pkg.name: sys/console/full/history_log
+pkg.description: Log-backed console history.
 pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
 pkg.homepage: "http://mynewt.apache.org/"
 pkg.keywords:
@@ -26,13 +26,7 @@ pkg.keywords:
 pkg.deps:
     - "@apache-mynewt-core/hw/hal"
     - "@apache-mynewt-core/kernel/os"
-pkg.deps.CONSOLE_UART:
-    - "@apache-mynewt-core/hw/drivers/uart"
-pkg.deps.CONSOLE_RTT:
-    - "@apache-mynewt-core/hw/drivers/rtt"
-pkg.deps.'CONSOLE_HISTORY == "ram"':
-    - "@apache-mynewt-core/sys/console/full/history_ram"
-pkg.apis: console
+    - "@apache-mynewt-core/sys/console/full"
 
 pkg.init:
-    console_pkg_init: 'MYNEWT_VAL(CONSOLE_SYSINIT_STAGE)'
+    console_history_pkg_init: 'MYNEWT_VAL(CONSOLE_HISTORY_LOG_SYSINIT_STAGE)'
diff --git a/sys/console/full/history_log/src/history_log.c b/sys/console/full/history_log/src/history_log.c
new file mode 100644
index 0000000..aceecaf
--- /dev/null
+++ b/sys/console/full/history_log/src/history_log.c
@@ -0,0 +1,358 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdint.h>
+#include <ctype.h>
+#include <os/mynewt.h>
+#if MYNEWT_VAL(LOG_FCB)
+#include <fcb/fcb.h>
+#endif
+#if MYNEWT_VAL(LOG_FCB2)
+#include <fcb/fcb2.h>
+#endif
+#include <log/log.h>
+#include <console/history.h>
+
+#define HISTORY_CACHE_SIZE MYNEWT_VAL(CONSOLE_HISTORY_LOG_CACHE_SIZE)
+
+#if MYNEWT_VAL(LOG_FCB2) && defined(FLASH_AREA_CONSOLE_HISTORY) && !defined(MYNEWT_VAL_CONSOLE_HISTORY_LOG_NAME)
+static struct fcb2 history_fcb;
+static struct log history_fcb_log;
+#endif
+static struct log *history_log;
+
+/*
+ * Circular buffer for commands.
+ * Lines are separated by '\0'.
+ * history_ptr points to place where new command will be stored. It always
+ * points to '\0'.
+ * history_cache[history_ptr] == 0
+ * history_cache[(history_ptr - 1) MOD CONSOLE_HISTORY_LOG_CACHE_SIZE] == 0
+ *
+ * COM1.COM2 ARG.COM3 -t...COMn.COMn-1 ARG.
+ *                       ^--- history_ptr
+ */
+static char history_cache[HISTORY_CACHE_SIZE];
+
+static size_t history_ptr;
+/* Helper macros to move pointer in circular buffer */
+#define PREV_PTR(p) ((p > history_cache) ? p - 1 : (history_cache + HISTORY_CACHE_SIZE - 1))
+#define NEXT_PTR(p) ((p < history_cache + HISTORY_CACHE_SIZE - 1) ? p + 1 : history_cache)
+#define PTR_ADD(p, a) (((p) + (a) < history_cache + HISTORY_CACHE_SIZE) ? (p) + (a) : (p) + (a) - HISTORY_CACHE_SIZE)
+#define PTR_SUB(p, a) (((p) - (a) >= history_cache) ? (p) - (a) : (p) - (a) + HISTORY_CACHE_SIZE)
+
+/*
+ * Given p that points to null terminator ending string stored in
+ * history change circular buffer, returns pointer to null terminator
+ * that is placed before string.
+ */
+static const char *
+move_back(const char *p)
+{
+    if (p == NULL) {
+        return NULL;
+    }
+
+    p = PREV_PTR(p);
+    /* If *p now is 0, function was called on oldest element in history */
+    if (*p == '\0') {
+        return NULL;
+    }
+    /* Move to the beginning and beyond (just one char) so p points to '\0' */
+    for (; *p; p = PREV_PTR(p)) ;
+
+    return p;
+}
+
+/*
+ * Given p that points to null terminator before string stored in
+ * history change circular buffer, returns pointer to null terminator
+ * that is just after string.
+ */
+static const char *
+move_forward(const char *p)
+{
+    if (p == NULL) {
+        return NULL;
+    }
+
+    p = NEXT_PTR(p);
+    if (*p == '\0') {
+        return NULL;
+    }
+
+    for (; *p; p = NEXT_PTR(p)) ;
+
+    /*
+     * If after moving forward to valid null terminator, next character is
+     * also 0, search found head of circular buffer.  Just return NULL
+     * in this case meaning that there is nothing to retrieve.
+     */
+    if (*(NEXT_PTR(p)) == '\0') {
+        return NULL;
+    }
+    return p;
+}
+
+/*
+ * Helper to move in both directions.
+ */
+static const char *
+move(const char *p, history_find_type_t search_type)
+{
+    if (search_type & HFT_NEXT) {
+        return move_forward(p);
+    } else {
+        return move_back(p);
+    }
+}
+
+static history_handle_t
+console_history_add_to_cache(const char *line)
+{
+    char *cache_end = history_cache + history_ptr;
+    /* Let p1 point to last null-terminator */
+    char *p1 = PREV_PTR(cache_end);
+    int len;
+    const char *p2;
+    int entry_num = 0;
+    int found_at = -1;
+    history_handle_t result;
+
+    if (line == NULL) {
+        return SYS_EINVAL;
+    }
+
+    /* Trim from spaces */
+    while (isspace(*line)) {
+        line++;
+    };
+
+    len = strlen(line);
+    if (len == 0) {
+        return SYS_EINVAL;
+    }
+
+    /*
+     * Trim trailing spaces. It does not touch input buffer, it just
+     * corrects len variable.
+     */
+    while (isspace(line[len - 1])) {
+        len--;
+    }
+
+    assert(*cache_end == 0);
+    /* p1 should point to string terminator */
+    assert(*p1 == 0);
+    /* Now p1 point to last character of most recent history line */
+    p1 = PREV_PTR(p1);
+
+    while (*p1) {
+        /* Compare entry in cache with line starting from the end */
+        for (p2 = line + len - 1; p2 >= line && *p1 == *p2; --p2, p1 = PREV_PTR(p1)) ;
+        if (p2 < line && *p1 == '\0') {
+            /* Line was in history cache already */
+            if (entry_num == 0) {
+                /* Last entry matched no need to do anything */
+                return SYS_EALREADY;
+            }
+            found_at = entry_num;
+            break;
+        }
+        /* Line did no match entry_num line from cache, go to the start of
+         * entry */
+        while (*p1) {
+            p1 = PREV_PTR(p1);
+        }
+        /* Skip null terminator of previous entry */
+        p1 = PREV_PTR(p1);
+        entry_num++;
+    }
+
+    if (found_at < 0) {
+        /* p1 will be used to store new line in cache */
+        p1 = cache_end;
+    } else {
+        /*
+         * Line was in the cache, rotate old data.
+         * This will overwrite old copy of command.
+         * Line will be added a new.
+         */
+        p1 = NEXT_PTR(p1);
+        p2 = PTR_ADD(p1, len + 1);
+        while (p2 != cache_end) {
+            *p1 = *p2;
+            p1 = NEXT_PTR(p1);
+            p2 = NEXT_PTR(p2);
+        }
+    }
+    /* Copy current line to the end of cache (including null terminator) */
+    p2 = line;
+    /* Store result sine p1 will be modified */
+    result = (history_handle_t)PREV_PTR(p1);
+
+    /* Copy line to history */
+    for (; len > 0; --len) {
+        *p1 = *p2++;
+        p1 = NEXT_PTR(p1);
+    }
+    *p1 = '\0';
+    p1 = NEXT_PTR(p1);
+
+    /* New head */
+    history_ptr = p1 - history_cache;
+
+    /*
+     * History pointer should point to '\0', if it is not destroy oldest
+     * partial entry.
+     */
+    while (*p1) {
+        *p1 = '\0';
+        p1 = NEXT_PTR(p1);
+    }
+
+    return result;
+}
+
+/*
+ * Function will be called from log_walk and will add history lines.
+ */
+static int
+history_cache_from_log(struct log *log, struct log_offset *log_offset,
+                       const struct log_entry_hdr *hdr,
+                       const void *dptr, uint16_t len)
+{
+    char line[MYNEWT_VAL_CONSOLE_MAX_INPUT_LEN];
+
+    if (len >= MYNEWT_VAL_CONSOLE_MAX_INPUT_LEN) {
+        len = MYNEWT_VAL_CONSOLE_MAX_INPUT_LEN - 1;
+    }
+    if (hdr->ue_module == MYNEWT_VAL(CONSOLE_HISTORY_LOG_MODULE)) {
+        log_read_body(log, dptr, line, 0, len);
+        line[len] = '\0';
+        (void)console_history_add_to_cache(line);
+    }
+
+    return 0;
+}
+
+history_handle_t
+console_history_add(const char *line)
+{
+    history_handle_t added_line;
+
+    added_line = console_history_add_to_cache(line);
+
+    if (added_line > 0 && history_log) {
+        log_printf(history_log, MYNEWT_VAL(CONSOLE_HISTORY_LOG_MODULE),
+                   LOG_LEVEL_MAX, line);
+    }
+    return added_line;
+}
+
+int
+console_history_get(history_handle_t handle, size_t offset, char *buf,
+                    size_t buf_size)
+{
+    const char *p1 = (const char *)handle;
+    size_t copied;
+
+    if (p1 == 0 || p1 < history_cache ||
+        p1 >= history_cache + HISTORY_CACHE_SIZE ||
+        *p1 != '\0') {
+        return SYS_EINVAL;
+    }
+
+    p1 = NEXT_PTR(p1);
+    for (; offset && *p1; --offset, p1 = NEXT_PTR(p1)) ;
+    if (offset > 0) {
+        return 0;
+    }
+    for (copied = 0; buf_size && *p1 != '\0'; --buf_size, ++copied) {
+        *buf++ = *p1;
+        p1 = NEXT_PTR(p1);
+    }
+    return copied;
+}
+
+history_handle_t
+console_history_find(history_handle_t start, history_find_type_t search_type,
+                     void *arg)
+{
+    const char *head = history_cache + history_ptr;
+    const char *p1 = (const char *)start;
+    const char *p2;
+    const char *lp;
+    const char *pattern_limit;
+    int num;
+    history_handle_t result = 0;
+
+    if (p1 == 0) {
+        p1 = PREV_PTR(head);
+    }
+    switch (search_type) {
+    case HFT_PREV:
+    case HFT_NEXT:
+        num = arg ? *(int *)arg : 1;
+        for (; num && p1 != NULL; --num) {
+            p1 = move(p1, search_type);
+        }
+        result = (history_handle_t)p1;
+        break;
+    case HFT_MATCH_PREV:
+    case HFT_MATCH_NEXT:
+        pattern_limit = (const char *)arg + strlen((const char *)arg);
+        for (lp = move(p1, search_type); lp != NULL; lp = move(lp, search_type)) {
+            p2 = arg;
+            p1 = NEXT_PTR(lp);
+            for (; p2 < pattern_limit && *p1 == *p2; ++p2, p1 = NEXT_PTR(p1) ) ;
+            if (p2 == pattern_limit) {
+                result = (history_handle_t)lp;
+                break;
+            }
+        }
+        break;
+    default:
+        break;
+    }
+    return result;
+}
+
+int
+console_history_pkg_init(void)
+{
+    struct log_offset off = { 0 };
+
+#if (MYNEWT_VAL(LOG_FCB) || MYNEWT_VAL(LOG_FCB2)) && defined(MYNEWT_VAL_CONSOLE_HISTORY_LOG_NAME)
+    history_log = log_find(MYNEWT_VAL(CONSOLE_HISTORY_LOG_NAME));
+#elif MYNEWT_VAL(LOG_FCB2) && defined(FLASH_AREA_CONSOLE_HISTORY)
+    /* If there is dedicated flash area for shell history and FCB2 is enabled */
+    fcb2_init_flash_area(&history_fcb, FLASH_AREA_CONSOLE_HISTORY, 0x12C9985, 1);
+    if (log_register("con_hist", &history_fcb_log, &log_fcb_handler,
+        &history_fcb, 0) == 0) {
+        history_log = &history_fcb_log;
+    }
+#endif
+    if (history_log) {
+        log_module_register(MYNEWT_VAL(CONSOLE_HISTORY_LOG_MODULE), "CON-HIST");
+        log_walk_body(history_log, history_cache_from_log, &off);
+    }
+
+    return 0;
+}
diff --git a/sys/console/full/pkg.yml b/sys/console/full/history_log/syscfg.yml
similarity index 52%
copy from sys/console/full/pkg.yml
copy to sys/console/full/history_log/syscfg.yml
index a035e28..4089b2b 100644
--- a/sys/console/full/pkg.yml
+++ b/sys/console/full/history_log/syscfg.yml
@@ -6,7 +6,7 @@
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-# 
+#
 #  http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing,
@@ -17,22 +17,24 @@
 # under the License.
 #
 
-pkg.name: sys/console/full
-pkg.description: Text-based IO interface.
-pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
-pkg.homepage: "http://mynewt.apache.org/"
-pkg.keywords:
-
-pkg.deps:
-    - "@apache-mynewt-core/hw/hal"
-    - "@apache-mynewt-core/kernel/os"
-pkg.deps.CONSOLE_UART:
-    - "@apache-mynewt-core/hw/drivers/uart"
-pkg.deps.CONSOLE_RTT:
-    - "@apache-mynewt-core/hw/drivers/rtt"
-pkg.deps.'CONSOLE_HISTORY == "ram"':
-    - "@apache-mynewt-core/sys/console/full/history_ram"
-pkg.apis: console
+syscfg.defs:
+    CONSOLE_HISTORY_LOG_CACHE_SIZE:
+        description: >
+            Number of RAM bytes used for caching history.
+        value: 512
+    CONSOLE_HISTORY_LOG_NAME:
+        description: >
+            Log name to use for storing console history.
+            When there is no flash area that can be used for storing
+            console history, any existing log can be utilized to
+            store console history.
+        value:
+    CONSOLE_HISTORY_LOG_MODULE:
+        description: >
+            Numeric module ID to use for console history log messages.
+        value: 9
 
-pkg.init:
-    console_pkg_init: 'MYNEWT_VAL(CONSOLE_SYSINIT_STAGE)'
+    CONSOLE_HISTORY_LOG_SYSINIT_STAGE:
+        description: >
+            Sysinit stage for the history log.
+        value: 1000
diff --git a/sys/console/full/pkg.yml b/sys/console/full/pkg.yml
index a035e28..2ef2a95 100644
--- a/sys/console/full/pkg.yml
+++ b/sys/console/full/pkg.yml
@@ -32,6 +32,8 @@ pkg.deps.CONSOLE_RTT:
     - "@apache-mynewt-core/hw/drivers/rtt"
 pkg.deps.'CONSOLE_HISTORY == "ram"':
     - "@apache-mynewt-core/sys/console/full/history_ram"
+pkg.deps.'CONSOLE_HISTORY == "log"':
+    - "@apache-mynewt-core/sys/console/full/history_log"
 pkg.apis: console
 
 pkg.init:
diff --git a/sys/console/full/syscfg.yml b/sys/console/full/syscfg.yml
index 0aebad7..7163884 100644
--- a/sys/console/full/syscfg.yml
+++ b/sys/console/full/syscfg.yml
@@ -48,6 +48,7 @@ syscfg.defs:
         choices:
             - none  # no history support
             - ram   # history kept in ram for current session only
+            - log   # history kept in log
     CONSOLE_MAX_PROMPT_LEN:
         description: 'Maximum number of characters for prompt'
         value: 16


[mynewt-core] 03/06: sys/console: Use separate package for history

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 5defd588995626de4258e6c1c83af4f5404281ec
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Wed Nov 13 15:51:29 2019 +0100

    sys/console: Use separate package for history
    
    In-RAM history code from console.c was dropped in favor
    of history_ram package.
    Functionality should be the same.
---
 sys/console/full/pkg.yml       |   2 +
 sys/console/full/src/console.c | 220 +++++------------------------------------
 sys/console/full/syscfg.yml    |  19 +++-
 3 files changed, 43 insertions(+), 198 deletions(-)

diff --git a/sys/console/full/pkg.yml b/sys/console/full/pkg.yml
index 361cbbe..a035e28 100644
--- a/sys/console/full/pkg.yml
+++ b/sys/console/full/pkg.yml
@@ -30,6 +30,8 @@ pkg.deps.CONSOLE_UART:
     - "@apache-mynewt-core/hw/drivers/uart"
 pkg.deps.CONSOLE_RTT:
     - "@apache-mynewt-core/hw/drivers/rtt"
+pkg.deps.'CONSOLE_HISTORY == "ram"':
+    - "@apache-mynewt-core/sys/console/full/history_ram"
 pkg.apis: console
 
 pkg.init:
diff --git a/sys/console/full/src/console.c b/sys/console/full/src/console.c
index 491b9c2..c296d69 100644
--- a/sys/console/full/src/console.c
+++ b/sys/console/full/src/console.c
@@ -30,6 +30,7 @@
 #include "console/console.h"
 #include "console/ticks.h"
 #include "console_priv.h"
+#include "console/history.h"
 
 /* Control characters */
 #define ESC                0x1b
@@ -111,6 +112,8 @@ static int max_row;
 static char console_prompt[MYNEWT_VAL(CONSOLE_MAX_PROMPT_LEN)];
 /* Length of prompt stored in console_prompt */
 static uint16_t prompt_len;
+/* Current history line, 0 no history line */
+static history_handle_t history_line;
 
 /*
  * Default implementation in case all consoles are disabled - we just ignore any
@@ -689,198 +692,36 @@ console_clear_line(void)
     cursor_clear_line();
 }
 
-#if MYNEWT_VAL(CONSOLE_HISTORY_SIZE) > 0
-
-#ifndef bssnz_t
-/* Just in case bsp.h does not define it, in this case console history will
- * not be preserved across software resets
- */
-#define bssnz_t
-#endif
-
-bssnz_t static char console_hist_lines[ MYNEWT_VAL(CONSOLE_HISTORY_SIZE) ][ MYNEWT_VAL(CONSOLE_MAX_INPUT_LEN) ];
-
-bssnz_t static struct console_hist {
-    uint32_t magic;
-    uint8_t head;
-    uint8_t tail;
-    uint8_t size;
-    uint8_t curr;
-    char *lines[ MYNEWT_VAL(CONSOLE_HISTORY_SIZE) + 1 ];
-} console_hist;
-
-static void
-console_hist_init(void)
-{
-    struct console_hist *sh = &console_hist;
-    int i;
-
-    if (sh->magic != 0xBABEFACE) {
-        memset(console_hist_lines, 0, sizeof(console_hist_lines));
-        memset(&console_hist, 0, sizeof(console_hist));
-
-        sh->size = MYNEWT_VAL(CONSOLE_HISTORY_SIZE) + 1;
-
-        for (i = 0; i < sh->size - 1; i++) {
-            sh->lines[i] = console_hist_lines[i];
-        }
-        sh->magic = 0xBABEFACE;
-    }
-}
-
-static size_t
-trim_whitespace(const char *str, size_t len, char *out)
-{
-    const char *end;
-    size_t out_size;
-
-    if (len == 0) {
-        return 0;
-    }
-
-    /* Trim leading space */
-    while (isspace((unsigned char)*str)) {
-        str++;
-    }
-
-    if (*str == 0) { /* All spaces? */
-        *out = 0;
-        return 0;
-    }
-
-    /* Trim trailing space */
-    end = str + strlen(str) - 1;
-    while (end > str && isspace((unsigned char)*end)) {
-        end--;
-    }
-
-    end++;
-
-    /* Set output size to minimum of trimmed string length and buffer size minus 1 */
-    out_size = min(end - str, len - 1);
-
-    /* Copy trimmed string and add null terminator */
-    memcpy(out, str, out_size);
-    out[out_size] = 0;
-
-    return out_size;
-}
-
-static uint8_t
-ring_buf_next(uint8_t i, uint8_t size)
-{
-    return (uint8_t) ((i + 1) % size);
-}
-
-static uint8_t
-ring_buf_prev(uint8_t i, uint8_t size)
-{
-    return i == 0 ? i = size - 1 : --i;
-}
-
-static bool
-console_hist_is_full(void)
-{
-    struct console_hist *sh = &console_hist;
-
-    return ring_buf_next(sh->head, sh->size) == sh->tail;
-}
-
-static bool
-console_hist_move_to_head(char *line)
-{
-    struct console_hist *sh = &console_hist;
-    char *match = NULL;
-    uint8_t prev, curr;
-
-    curr = sh->tail;
-    while (curr != sh->head) {
-        if (strcmp(sh->lines[curr], line) == 0) {
-            match = sh->lines[curr];
-            break;
-        }
-        curr = ring_buf_next(curr, sh->size);
-    }
-
-    if (!match) {
-        return false;
-    }
-
-    prev = curr;
-    curr = ring_buf_next(curr, sh->size);
-    while (curr != sh->head) {
-        sh->lines[prev] = sh->lines[curr];
-        prev = curr;
-        curr = ring_buf_next(curr, sh->size);
-    }
-
-    sh->lines[prev] = match;
-
-    return true;
-}
+#if !MYNEWT_VAL_CHOICE(CONSOLE_HISTORY, none)
 
 static void
-console_hist_add(char *line)
+console_history_move(char *line, history_find_type_t direction)
 {
-    struct console_hist *sh = &console_hist;
-    char buf[MYNEWT_VAL(CONSOLE_MAX_INPUT_LEN)];
-    size_t len;
+    history_handle_t new_line;
 
-    /* Reset current pointer */
-    sh->curr = sh->head;
+    new_line = console_history_find(history_line, direction, NULL);
 
-    len = trim_whitespace(line, sizeof(buf), buf);
-    if (!len) {
+    /* No more lines backward, do nothing */
+    if (!new_line && direction == HFT_PREV) {
         return;
     }
 
-    if (console_hist_move_to_head(buf)) {
-        return;
-    }
-
-    if (console_hist_is_full()) {
-        /*
-         * We have N buffers, but there are N+1 items in queue so one element is
-         * always empty. If queue is full we need to move buffer from oldest
-         * entry to current head and trim queue tail.
-         */
-        assert(sh->lines[sh->head] == NULL);
-        sh->lines[sh->head] = sh->lines[sh->tail];
-        sh->lines[sh->tail] = NULL;
-        sh->tail = ring_buf_next(sh->tail, sh->size);
+    history_line = new_line;
+    console_clear_line();
+    if (new_line) {
+        cur = console_history_get(new_line, 0, line,
+                                  MYNEWT_VAL_CONSOLE_MAX_INPUT_LEN);
+    } else {
+        line[0] = '\0';
     }
-
-    strcpy(sh->lines[sh->head], buf);
-    sh->head = ring_buf_next(sh->head, sh->size);
-
-    /* Reset current pointer */
-    sh->curr = sh->head;
+    trailing_chars = 0;
+    console_write_nolock(line, cur);
 }
 
 static void
-console_hist_move(char *line, uint8_t direction)
+console_handle_move(char *line, int direction)
 {
-    struct console_hist *sh = &console_hist;
-    char *str = NULL;
-    uint8_t limit = direction ==  ANSI_UP ? sh->tail : sh->head;
-
-    /* no more history to return in this direction */
-    if (sh->curr == limit) {
-        return;
-    }
-
-    if (direction == ANSI_UP) {
-        sh->curr = ring_buf_prev(sh->curr, sh->size);
-    } else {
-        sh->curr = ring_buf_next(sh->curr, sh->size);
-    }
-
-    console_clear_line();
-    str = sh->lines[sh->curr];
-    while (str && *str != '\0') {
-        insert_char(&line[cur], *str);
-        ++str;
-    }
+    console_history_move(line, direction > 0 ? HFT_PREV : HFT_NEXT);
 }
 #endif
 
@@ -952,16 +793,10 @@ handle_ansi(uint8_t byte, char *line)
 
 ansi_cmd:
     switch (byte) {
-#if MYNEWT_VAL(CONSOLE_HISTORY_SIZE) > 0
+#if !MYNEWT_VAL_CHOICE(CONSOLE_HISTORY, none)
     case ANSI_UP:
     case ANSI_DOWN:
-#if MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE) == 0
-        console_blocking_mode();
-#endif
-        console_hist_move(line, byte);
-#if MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE) == 0
-        console_non_blocking_mode();
-#endif
+        console_handle_move(line, byte == ANSI_UP ? 1 : -1);
         break;
 #endif
     case ANSI_BACKWARD:
@@ -1233,9 +1068,10 @@ console_handle_char(uint8_t byte)
                 console_filter_out('\r');
                 console_filter_out('\n');
             }
-#if MYNEWT_VAL(CONSOLE_HISTORY_SIZE) > 0
-            console_hist_add(input->line);
-#endif
+            if (!MYNEWT_VAL_CHOICE(CONSOLE_HISTORY, none)) {
+                console_history_add(input->line);
+                history_line = 0;
+            }
             console_handle_line();
             break;
         case '\t':
@@ -1357,10 +1193,6 @@ console_pkg_init(void)
     os_eventq_init(&avail_queue);
     os_mutex_init(&console_write_lock);
 
-#if MYNEWT_VAL(CONSOLE_HISTORY_SIZE) > 0
-    console_hist_init();
-#endif
-
 #if MYNEWT_VAL(CONSOLE_UART)
     rc = uart_console_init();
 #endif
diff --git a/sys/console/full/syscfg.yml b/sys/console/full/syscfg.yml
index a71f95f..0aebad7 100644
--- a/sys/console/full/syscfg.yml
+++ b/sys/console/full/syscfg.yml
@@ -41,11 +41,13 @@ syscfg.defs:
     CONSOLE_MAX_INPUT_LEN:
         description: 'Maximum input line length'
         value: 256
-    CONSOLE_HISTORY_SIZE:
+    CONSOLE_HISTORY:
         description: >
-            Number of lines to be stored in console history.
-            Set to "0" to disable console history.
-        value: 0
+            Select how console should handle command history.
+        value: none
+        choices:
+            - none  # no history support
+            - ram   # history kept in ram for current session only
     CONSOLE_MAX_PROMPT_LEN:
         description: 'Maximum number of characters for prompt'
         value: 16
@@ -124,3 +126,12 @@ syscfg.defs:
         description: >
             Sysinit stage for console functionality.
         value: 20
+
+    CONSOLE_HISTORY_SIZE:
+        description: >
+            This is no longer used.
+            Equivalent settings:
+                CONSOLE_HISTORY: ram
+                CONSOLE_HISTORY_RAM_HISTORY_SIZE: <n>   <- previous value of CONSOLE_HISTORY_SIZE
+        value: 0
+        defunct: 1


[mynewt-core] 01/06: sys/console: Add history provider api

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit aa42b4b90e27a04270f354603dd5d519351fcf69
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Wed Nov 13 15:28:27 2019 +0100

    sys/console: Add history provider api
    
    This adds set of functions to manipulate console history.
    Three functions prototypes are provided
    - console_history_add() to add new lines to history
    - console_history_find() to move around history (back/forth/search)
    - console_history_get() to get date from history
    
    This is base for different back-end implementations of history.
---
 sys/console/full/include/console/history.h | 87 ++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/sys/console/full/include/console/history.h b/sys/console/full/include/console/history.h
new file mode 100644
index 0000000..37f13a7
--- /dev/null
+++ b/sys/console/full/include/console/history.h
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef __SYS_CONSOLE_FULL_HISTORY_H__
+#define __SYS_CONSOLE_FULL_HISTORY_H__
+
+/**
+ * History search types for console_history_find()
+ */
+typedef enum history_find_type {
+    /* Find previous entry */
+    HFT_PREV                = 0,
+    /* Find next entry */
+    HFT_NEXT                = 1,
+    /* Find oldest entry */
+    HFT_LAST                = 2,
+    HFT_MATCH               = 4,
+    /* Find previous entry matching string pointed by arg */
+    HFT_MATCH_PREV          = HFT_MATCH | HFT_PREV,
+    /* Find next entry matching string pointed by arg */
+    HFT_MATCH_NEXT          = HFT_MATCH | HFT_NEXT,
+} history_find_type_t;
+
+typedef intptr_t history_handle_t;
+
+/**
+ * Function adds line to history.
+ *
+ * Implemented by history provider.
+ *
+ * @param line text to add to history
+ *
+ * @return handle to new entry added to history
+ *         SYS_EINVAL - line was empty or null
+ *         SYS_EALREADY - line was already at the end of history
+ */
+history_handle_t console_history_add(const char *line);
+
+/**
+ * Finds element in history.
+ *
+ * Implemented by history provider.
+ *
+ * @param start starting point for search 0 start from the beginning
+ * @param search_type search method
+ * @param arg for HFT_PREV, HFT_NEXT can be a pointer to int with
+ *            requested move size (default 1)
+ *            for HFT_MATCH_PREV, HFT_MATCH_NEXT points to the null
+ *            terminated string to match history line.
+ * @return handle to entry that matched or 0 if entry can't be found.
+ */
+history_handle_t console_history_find(history_handle_t start,
+                                      history_find_type_t search_type,
+                                      void *arg);
+
+/**
+ * Get data from history element found by console_history_find()
+ *
+ * Implemented by history provider.
+ *
+ * @param handle  handle to entry to get data from
+ * @param offset  offset of data in element to get data from
+ * @param buf     buffer to fill with data from element
+ * @param buf_size size of buf
+ *
+ * @return number of characters written to buf, or SYS_Exxx if
+ *                there is an error.
+ */
+int console_history_get(history_handle_t handle, size_t offset, char *buf,
+                        size_t buf_size);
+
+#endif /* __SYS_CONSOLE_FULL_HISTORY_H__ */


[mynewt-core] 06/06: sys/console: Clean up syscfg value names

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 8615b0d01d1cfbf808554fef252ae85f56098475
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Wed Nov 20 08:51:22 2019 +0100

    sys/console: Clean up syscfg value names
    
    Console history and prompt values names are more consistent now.
---
 sys/console/full/src/console.c | 28 ++++++++++++++--------------
 sys/console/full/syscfg.yml    |  8 ++++----
 2 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/sys/console/full/src/console.c b/sys/console/full/src/console.c
index 86224a1..e2a6e22 100644
--- a/sys/console/full/src/console.c
+++ b/sys/console/full/src/console.c
@@ -109,7 +109,7 @@ static bool terminal_size_requested;
 /* Maximum row as reported by terminal, 0 - terminal size is not known yet */
 static int max_row;
 /* Buffer for prompt */
-static char console_prompt[MYNEWT_VAL(CONSOLE_MAX_PROMPT_LEN)];
+static char console_prompt[MYNEWT_VAL(CONSOLE_PROMPT_MAX_LEN)];
 /* Length of prompt stored in console_prompt */
 static uint16_t prompt_len;
 /* Current history line, 0 no history line */
@@ -216,7 +216,7 @@ console_filter_out(int c)
      * line from prompt editor.
      */
     console_is_midline = c != '\n' && c != '\r';
-    if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT) && max_row > 0) {
+    if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY) && max_row > 0) {
         if (c == '\n') {
             console_is_midline = false;
             if (holding_lf) {
@@ -344,7 +344,7 @@ request_terminal_size(void)
 static void
 console_init_terminal(void)
 {
-    if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT) && !terminal_initialized) {
+    if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY) && !terminal_initialized) {
         console_write_str(CSI "!p"
                           CSI "1;999r"
                           CSI "999;1H\n\n"
@@ -367,7 +367,7 @@ console_switch_to_prompt(void)
 
     console_init_terminal();
     /* If terminal size is not known yet, try asking terminal first */
-    if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT) &&
+    if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY) &&
         max_row == 0 && !terminal_size_requested) {
         request_terminal_size();
     }
@@ -376,7 +376,7 @@ console_switch_to_prompt(void)
      * of the screen, save cursor position and set cursor at place
      * that is in sync with 'cur' variable.
      */
-    if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT) &&
+    if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY) &&
         !prompt_has_focus && max_row) {
         cursor_save();
         prompt_has_focus = true;
@@ -391,7 +391,7 @@ console_switch_to_prompt(void)
             console_write_str(CSI "0m");
             console_out_nolock(c);
             console_out_nolock('\b');
-            if (MYNEWT_VAL(CONSOLE_HIDE_CURSOR_IN_LOG_AREA)) {
+            if (MYNEWT_VAL(CONSOLE_PROMPT_HIDE_CURSOR_IN_LOG_AREA)) {
                 console_write_str(CSI "?25h");
             }
         }
@@ -413,7 +413,7 @@ console_switch_to_logs(void)
     }
 
     console_init_terminal();
-    if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT) && prompt_has_focus) {
+    if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY) && prompt_has_focus) {
         if (MYNEWT_VAL(CONSOLE_PROMPT_SOFT_CURSOR)) {
             console_write_str(CSI);
             console_write_str(MYNEWT_VAL(CONSOLE_PROMPT_SOFT_CURSOR_ATTR));
@@ -424,7 +424,7 @@ console_switch_to_logs(void)
                 c = ' ';
             }
             console_out_nolock(c);
-            if (MYNEWT_VAL(CONSOLE_HIDE_CURSOR_IN_LOG_AREA)) {
+            if (MYNEWT_VAL(CONSOLE_PROMPT_HIDE_CURSOR_IN_LOG_AREA)) {
                 console_write_str(CSI "?25l");
             }
             console_write_str(CSI "0m\b");
@@ -456,8 +456,8 @@ console_prompt_set(const char *prompt, const char *line)
 
     prompt_len = strlen(prompt);
 
-    /* If this assert fails increase value of CONSOLE_MAX_PROMPT_LEN */
-    assert(MYNEWT_VAL(CONSOLE_MAX_PROMPT_LEN) > prompt_len);
+    /* If this assert fails increase value of CONSOLE_PROMPT_MAX_LEN */
+    assert(MYNEWT_VAL(CONSOLE_PROMPT_MAX_LEN) > prompt_len);
 
     strcpy(console_prompt, prompt);
 
@@ -472,7 +472,7 @@ console_prompt_set(const char *prompt, const char *line)
     locked = console_lock(1000) == OS_OK;
 
     console_switch_to_prompt();
-    if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT) && prompt_has_focus) {
+    if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY) && prompt_has_focus) {
         console_write_str(CSI "999;1H");
         console_write_nolock(prompt, prompt_len);
         console_write_nolock(line, cur);
@@ -927,7 +927,7 @@ ansi_cmd:
         }
         break;
     case DSR_CPS:
-        if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT) && terminal_size_requested) {
+        if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY) && terminal_size_requested) {
             terminal_size_requested = false;
             max_row = ansi_val;
             console_cursor_set(max_row - 1, 1);
@@ -1139,7 +1139,7 @@ console_handle_char(uint8_t byte)
 
             prev_endl = byte;
             input->line[cur + trailing_chars] = '\0';
-            if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT) && prompt_has_focus) {
+            if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY) && prompt_has_focus) {
                 console_switch_to_logs();
                 /*
                  * Cursor is always in the middle of the line since new lines
@@ -1195,7 +1195,7 @@ console_handle_char(uint8_t byte)
             break;
         /* CTRL-L */
         case VT:
-            if (MYNEWT_VAL(CONSOLE_STICKY_PROMPT)) {
+            if (MYNEWT_VAL(CONSOLE_PROMPT_STICKY)) {
                 request_terminal_size();
             } else {
                 console_out_nolock(VT);
diff --git a/sys/console/full/syscfg.yml b/sys/console/full/syscfg.yml
index 65efbcb..34a039d 100644
--- a/sys/console/full/syscfg.yml
+++ b/sys/console/full/syscfg.yml
@@ -54,12 +54,12 @@ syscfg.defs:
             When set to 1 history is automatically searched for lines starting with already typed
             text.
         value: 0
-    CONSOLE_MAX_PROMPT_LEN:
+    CONSOLE_PROMPT_MAX_LEN:
         description: 'Maximum number of characters for prompt'
         value: 16
-    CONSOLE_STICKY_PROMPT:
+    CONSOLE_PROMPT_STICKY:
         description: >
-            If set to 1 prompt will be visble all the time at the
+            If set to 1 prompt will be visible all the time at the
             bottom of terminal if terminal was detected.
         value: 0
     CONSOLE_PROMPT_SOFT_CURSOR:
@@ -73,7 +73,7 @@ syscfg.defs:
             For black and white (minicom) use "7m" - inverse
             For color (putty) use "30;42m" - black/green
         value: '"7m"'
-    CONSOLE_HIDE_CURSOR_IN_LOG_AREA:
+    CONSOLE_PROMPT_HIDE_CURSOR_IN_LOG_AREA:
         description: >
             Hide cursor in log area.
         value: 1


[mynewt-core] 02/06: sys/console: Add in-RAM implementation of history

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit ecc5cfee49fd54039f458b8b382b61d5ea3ae92a
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Wed Nov 13 15:34:41 2019 +0100

    sys/console: Add in-RAM implementation of history
    
    Code is mostly copied from existing console.c file.
---
 sys/console/full/history_ram/pkg.yml           |  32 ++++
 sys/console/full/history_ram/src/history_ram.c | 234 +++++++++++++++++++++++++
 sys/console/full/history_ram/syscfg.yml        |  27 +++
 3 files changed, 293 insertions(+)

diff --git a/sys/console/full/history_ram/pkg.yml b/sys/console/full/history_ram/pkg.yml
new file mode 100644
index 0000000..870da71
--- /dev/null
+++ b/sys/console/full/history_ram/pkg.yml
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: sys/console/full/history_ram
+pkg.description: RAM only console history.
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/hw/hal"
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/sys/console/full"
+
+pkg.init:
+    console_history_ram_pkg_init: 'MYNEWT_VAL(CONSOLE_HISTORY_RAM_SYSINIT_STAGE)'
diff --git a/sys/console/full/history_ram/src/history_ram.c b/sys/console/full/history_ram/src/history_ram.c
new file mode 100644
index 0000000..e08fda0
--- /dev/null
+++ b/sys/console/full/history_ram/src/history_ram.c
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdint.h>
+#include <ctype.h>
+#include <os/mynewt.h>
+#include <console/history.h>
+
+#ifndef bssnz_t
+/* Just in case bsp.h does not define it, in this case console history will
+ * not be preserved across software resets
+ */
+#define bssnz_t
+#endif
+
+#define HISTORY_SIZE MYNEWT_VAL(CONSOLE_HISTORY_RAM_HISTORY_SIZE)
+
+bssnz_t static char console_hist_lines[HISTORY_SIZE][ MYNEWT_VAL(CONSOLE_MAX_INPUT_LEN) ];
+
+bssnz_t static struct console_hist {
+    uint32_t magic;
+    uint8_t head;
+    uint8_t count;
+    char *lines[HISTORY_SIZE + 1];
+} console_hist;
+
+static size_t
+trim_whitespace(const char *str, char *out, size_t out_size)
+{
+    const char *end;
+    size_t len;
+
+    if (out_size == 0) {
+        return 0;
+    }
+
+    /* Skip leading space */
+    while (isspace((unsigned char)*str)) {
+        str++;
+    }
+
+    if (*str == 0) { /* All spaces? */
+        *out = 0;
+        return 0;
+    }
+
+    /* Skip trailing space */
+    end = str + strlen(str) - 1;
+    while (isspace((unsigned char)*end)) {
+        end--;
+    }
+
+    end++;
+
+    /* Set output size to minimum of trimmed string length and buffer size minus 1 */
+    len = min(end - str, out_size - 1);
+
+    /* Copy trimmed string and add null terminator */
+    memcpy(out, str, len);
+    out[len] = 0;
+
+    return len;
+}
+
+static uint8_t
+ring_buf_next(uint8_t i)
+{
+    return (uint8_t) ((i + 1) % HISTORY_SIZE);
+}
+
+static uint8_t
+ring_buf_prev(uint8_t i)
+{
+    return i == 0 ? HISTORY_SIZE - 1 : i - 1;
+}
+
+static bool
+console_hist_is_full(void)
+{
+    return console_hist.count == HISTORY_SIZE;
+}
+
+static bool
+console_hist_move_to_head(char *line)
+{
+    struct console_hist *sh = &console_hist;
+    char *match = NULL;
+    uint8_t prev;
+    uint8_t curr;
+    uint8_t left;
+
+    left = sh->count;
+    curr = ring_buf_prev(sh->head);
+    while (left) {
+        if (strcmp(sh->lines[curr], line) == 0) {
+            match = sh->lines[curr];
+            break;
+        }
+        curr = ring_buf_next(curr);
+        left--;
+    }
+
+    if (!match) {
+        return false;
+    }
+
+    prev = curr;
+    curr = ring_buf_next(curr);
+    while (curr != sh->head) {
+        sh->lines[prev] = sh->lines[curr];
+        prev = curr;
+        curr = ring_buf_next(curr);
+    }
+
+    sh->lines[prev] = match;
+
+    return true;
+}
+
+history_handle_t
+console_history_add(const char *line)
+{
+    struct console_hist *sh = &console_hist;
+    char buf[MYNEWT_VAL(CONSOLE_MAX_INPUT_LEN)];
+    size_t len;
+
+    len = trim_whitespace(line, buf, sizeof(buf));
+    if (len == 0) {
+        return 0;
+    }
+
+    if (console_hist_move_to_head(buf)) {
+        return 1;
+    }
+
+    strcpy(sh->lines[sh->head], buf);
+    sh->head = ring_buf_next(sh->head);
+    if (!console_hist_is_full()) {
+        sh->count++;
+    }
+    return 1;
+}
+
+history_handle_t
+console_history_find(history_handle_t start, history_find_type_t search_type,
+                     void *arg)
+{
+    int num;
+
+    switch (search_type) {
+    case HFT_PREV:
+        num = start + (arg ? *(int *)arg : 1);
+        if (num > console_hist.count) {
+            return 0;
+        }
+        return num;
+    case HFT_NEXT:
+        num = start - (arg ? *(int *)arg : 1);
+        if (num <= 0) {
+            return 0;
+        }
+        return num;
+    default:
+        return 0;
+    }
+}
+
+int
+console_history_get(history_handle_t handle, size_t offset, char *buf,
+                    size_t buf_size)
+{
+    const char *line;
+    size_t line_len;
+    int num = (int)handle;
+
+    if (num > console_hist.count || num < 1) {
+        return SYS_EINVAL;
+    }
+
+    if (num <= console_hist.head) {
+        line = console_hist.lines[console_hist.head - num];
+    } else {
+        line = console_hist.lines[console_hist.head + HISTORY_SIZE - num];
+    }
+
+    line_len = strlen(line);
+    if (line_len <= offset) {
+        return 0;
+    }
+    line += offset;
+    line_len -= offset;
+
+    if (line_len > buf_size) {
+        line_len = buf_size;
+    }
+    memcpy(buf, line, line_len);
+
+    return line_len;
+}
+
+int
+console_history_ram_pkg_init(void)
+{
+    struct console_hist *sh = &console_hist;
+    int i;
+
+    if (sh->magic != 0xBABEFACE) {
+        memset(console_hist_lines, 0, sizeof(console_hist_lines));
+        memset(&console_hist, 0, sizeof(console_hist));
+
+        for (i = 0; i < HISTORY_SIZE; i++) {
+            sh->lines[i] = console_hist_lines[i];
+        }
+        sh->magic = 0xBABEFACE;
+    }
+
+    return 0;
+}
diff --git a/sys/console/full/history_ram/syscfg.yml b/sys/console/full/history_ram/syscfg.yml
new file mode 100644
index 0000000..5fdb86a
--- /dev/null
+++ b/sys/console/full/history_ram/syscfg.yml
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    CONSOLE_HISTORY_RAM_HISTORY_SIZE:
+        description: >
+            Number of lines to be stored in console history.
+        value: 10
+    CONSOLE_HISTORY_RAM_SYSINIT_STAGE:
+        description: >
+            Sysinit stage for the in ram console history.
+        value: 1000


[mynewt-core] 05/06: sys/console: Add autocomplete from history

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit c29bde707b59ba578b298d447635e7894a70ef16
Author: Jerzy Kasenberg <je...@codecoup.pl>
AuthorDate: Wed Nov 13 16:35:41 2019 +0100

    sys/console: Add autocomplete from history
    
    Historical entries will show up at prompt when
    typed characters match.
---
 sys/console/full/src/console.c | 140 +++++++++++++++++++++++++++++++++++------
 sys/console/full/syscfg.yml    |   5 ++
 2 files changed, 126 insertions(+), 19 deletions(-)

diff --git a/sys/console/full/src/console.c b/sys/console/full/src/console.c
index c296d69..86224a1 100644
--- a/sys/console/full/src/console.c
+++ b/sys/console/full/src/console.c
@@ -621,30 +621,83 @@ cursor_backward(unsigned int count)
     }
 }
 
+static bool trailing_selection;
+
+/*
+ * The following two weak functions are here in case linker does not allow
+ * to eliminate dead code (console_history_search()).
+ */
+history_handle_t __attribute__((weak))
+console_history_find(history_handle_t start, history_find_type_t search_type,
+                     void *arg)
+{
+    return (history_handle_t )0;
+}
+
+int __attribute__((weak))
+console_history_get(history_handle_t handle, size_t offset, char *buf,
+                    size_t buf_size)
+{
+    return 0;
+}
+
+static void
+console_history_search(char *line, history_find_type_t direction)
+{
+    line[cur] = '\0';
+    history_line = console_history_find(history_line, direction, line);
+    /* No more lines backward, do nothing */
+    cursor_clear_line();
+    if (!history_line) {
+        trailing_chars = 0;
+    } else {
+        trailing_chars = console_history_get(history_line, cur,line + cur,
+                                             MYNEWT_VAL(CONSOLE_MAX_INPUT_LEN) - cur);
+        console_write_str(CSI "7m");
+        console_write_nolock(line + cur, trailing_chars);
+        console_write_str(CSI "0m");
+        cursor_backward(trailing_chars);
+        trailing_selection = true;
+    }
+}
+
 static void
 insert_char(char *pos, char c)
 {
     char tmp;
-    int end = trailing_chars;
+    int end;
 
-    if (cur + end >= MYNEWT_VAL(CONSOLE_MAX_INPUT_LEN) - 1) {
+    if ((!MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH) || !trailing_selection) &&
+        cur + trailing_chars >= MYNEWT_VAL(CONSOLE_MAX_INPUT_LEN) - 1) {
         return;
     }
 
     if (echo) {
+        if (!MYNEWT_VAL_CHOICE(CONSOLE_HISTORY, none) &&
+            MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH) && trailing_selection) {
+            cursor_clear_line();
+            trailing_chars = 0;
+            trailing_selection = false;
+        }
         /* Echo back to console */
         console_out_nolock(c);
     }
 
     ++cur;
 
-    if (end == 0) {
+    if (trailing_chars == 0) {
         *pos = c;
-        return;
+        if (!MYNEWT_VAL_CHOICE(CONSOLE_HISTORY, none) &&
+            MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH) && cur > 1) {
+            history_line = 0;
+            console_history_search(pos - cur + 1, HFT_MATCH_PREV);
+            return;
+        }
     }
 
     tmp = *pos;
     *(pos++) = c;
+    end = trailing_chars;
 
     while (end-- > 0) {
         if (echo) {
@@ -661,11 +714,37 @@ insert_char(char *pos, char c)
     }
 }
 
+static void
+clear_selection(const char *line)
+{
+    if (MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH) && trailing_selection) {
+        trailing_selection = false;
+        console_write_str(CSI "0m");
+        console_write_nolock(line + cur, trailing_chars);
+        cursor_backward(trailing_chars);
+    }
+}
+
+static void
+delete_selection(char *pos)
+{
+    if (MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH) && trailing_selection) {
+        *pos = 0;
+        trailing_selection = false;
+        trailing_chars = 0;
+        cursor_clear_line();
+    }
+}
+
 /* Delete character at cursor position */
 static void
 del_char(char *pos)
 {
-    int left = trailing_chars;
+    int left;
+
+    delete_selection(pos);
+
+    left = trailing_chars;
 
     while (left-- > 1) {
         *pos = *(pos + 1);
@@ -721,13 +800,20 @@ console_history_move(char *line, history_find_type_t direction)
 static void
 console_handle_move(char *line, int direction)
 {
-    console_history_move(line, direction > 0 ? HFT_PREV : HFT_NEXT);
+    if (MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH) && trailing_selection) {
+        console_history_search(line, direction > 0 ? HFT_MATCH_PREV :
+                                     HFT_MATCH_NEXT);
+    } else {
+        console_history_move(line, direction > 0 ? HFT_PREV : HFT_NEXT);
+    }
 }
 #endif
 
 static void
-handle_home(void)
+handle_home(char *line)
 {
+    clear_selection(line);
+
     if (cur) {
         cursor_backward(cur);
         trailing_chars += cur;
@@ -744,8 +830,10 @@ handle_delete(char *line)
 }
 
 static void
-handle_end(void)
+handle_end(char *line)
 {
+    clear_selection(line);
+
     if (trailing_chars) {
         cursor_forward(trailing_chars);
         cur += trailing_chars;
@@ -804,6 +892,7 @@ ansi_cmd:
             break;
         }
 
+        clear_selection(line);
         trailing_chars += ansi_val;
         cur -= ansi_val;
         cursor_backward(ansi_val);
@@ -813,26 +902,27 @@ ansi_cmd:
             break;
         }
 
+        clear_selection(line);
         trailing_chars -= ansi_val;
         cur += ansi_val;
         cursor_forward(ansi_val);
         break;
     case ANSI_HOME:
-        handle_home();
+        handle_home(line);
         break;
     case ANSI_END:
-        handle_end();
+        handle_end(line);
         break;
     case '~':
         switch (ansi_val) {
         case 1:
-            handle_home();
+            handle_home(line);
             break;
         case 3:
             handle_delete(line);
             break;
         case 4:
-            handle_end();
+            handle_end(line);
             break;
         }
         break;
@@ -959,6 +1049,16 @@ console_append_char(char *line, uint8_t byte)
     return 1;
 }
 
+static void
+handle_backspace(char *line)
+{
+    if (cur > 0) {
+        cursor_backward(1);
+        cur--;
+        trailing_chars++;
+        del_char(&line[cur]);
+    }
+}
 
 int
 console_handle_char(uint8_t byte)
@@ -1020,12 +1120,7 @@ console_handle_char(uint8_t byte)
         switch (byte) {
         case DEL:
         case BS:
-            if (cur > 0) {
-                cursor_backward(1);
-                cur--;
-                trailing_chars++;
-                del_char(&input->line[cur]);
-            }
+            handle_backspace(input->line);
             break;
         case ESC:
             esc_state |= ESC_ESC;
@@ -1071,11 +1166,18 @@ console_handle_char(uint8_t byte)
             if (!MYNEWT_VAL_CHOICE(CONSOLE_HISTORY, none)) {
                 console_history_add(input->line);
                 history_line = 0;
+                if (MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH)) {
+                    trailing_selection = 0;
+                }
             }
             console_handle_line();
             break;
         case '\t':
-            if (completion && !trailing_chars) {
+            if (completion && (!trailing_chars ||
+                (MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH) && trailing_selection))) {
+                if (MYNEWT_VAL(CONSOLE_HISTORY_AUTO_SEARCH) && trailing_selection) {
+                    delete_selection(input->line + cur);
+                }
 #if MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE) == 0
                 console_blocking_mode();
 #endif
diff --git a/sys/console/full/syscfg.yml b/sys/console/full/syscfg.yml
index 7163884..65efbcb 100644
--- a/sys/console/full/syscfg.yml
+++ b/sys/console/full/syscfg.yml
@@ -49,6 +49,11 @@ syscfg.defs:
             - none  # no history support
             - ram   # history kept in ram for current session only
             - log   # history kept in log
+    CONSOLE_HISTORY_AUTO_SEARCH:
+        description: >
+            When set to 1 history is automatically searched for lines starting with already typed
+            text.
+        value: 0
     CONSOLE_MAX_PROMPT_LEN:
         description: 'Maximum number of characters for prompt'
         value: 16