cmdline: big rework and clean of cmdline library
[libcmdline.git] / src / lib / cmdline_rdline.c
index 4352b94..7f34946 100644 (file)
 #include <string.h>
 #include <stdarg.h>
 #include <ctype.h>
+#include <unistd.h>
 
 #include "cmdline_cirbuf.h"
 #include "cmdline_rdline.h"
 #include "cmdline_parse.h"
 
-static void rdline_puts(struct rdline *rdl, const char *buf);
-static void rdline_miniprintf(struct rdline *rdl,
-                             const char *buf, unsigned int val);
-
 #ifndef NO_RDLINE_HISTORY
 static void rdline_remove_old_history_item(struct rdline *rdl);
 static void rdline_remove_first_history_item(struct rdline *rdl);
@@ -94,14 +91,17 @@ isblank2(char c)
 
 void
 rdline_init(struct rdline *rdl,
-                rdline_write_char_t *write_char,
-                rdline_validate_t *validate,
-                rdline_complete_t *complete)
+           int fd_in, int fd_out,
+           rdline_validate_t *validate,
+           rdline_complete_t *complete,
+           rdline_help_t *help)
 {
        memset(rdl, 0, sizeof(*rdl));
+       rdl->fd_in = fd_in;
+       rdl->fd_out = fd_out;
        rdl->validate = validate;
        rdl->complete = complete;
-       rdl->write_char = write_char;
+       rdl->help = help;
        rdl->status = RDLINE_INIT;
 #ifndef NO_RDLINE_HISTORY
        cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
@@ -111,18 +111,15 @@ rdline_init(struct rdline *rdl,
 void
 rdline_newline(struct rdline *rdl, const char *prompt)
 {
-       unsigned int i;
-
        vt100_init(&rdl->vt100);
        cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
        cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
 
+       /* if pointer is the same, don't copy it */
        if (prompt != rdl->prompt)
-               memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1);
-       rdl->prompt_size = strlen(prompt);
+               snprintf(rdl->prompt, sizeof(rdl->prompt), "%s", prompt);
 
-       for (i=0 ; i<rdl->prompt_size ; i++)
-               rdl->write_char(rdl, rdl->prompt[i]);
+       rdline_printf(rdl, "%s", rdl->prompt);
        rdl->status = RDLINE_RUNNING;
 
 #ifndef NO_RDLINE_HISTORY
@@ -159,8 +156,7 @@ rdline_get_buffer(struct rdline *rdl)
        len_r = CIRBUF_GET_LEN(&rdl->right);
        memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
 
-       rdl->left_buf[len_l + len_r] = '\n';
-       rdl->left_buf[len_l + len_r + 1] = '\0';
+       rdl->left_buf[len_l + len_r] = '\0';
        return rdl->left_buf;
 }
 
@@ -173,13 +169,13 @@ display_right_buffer(struct rdline *rdl, int force)
        if (!force && CIRBUF_IS_EMPTY(&rdl->right))
                return;
 
-       rdline_puts(rdl, vt100_clear_right);
+       rdline_printf(rdl, vt100_clear_right);
        CIRBUF_FOREACH(&rdl->right, i, tmp) {
-               rdl->write_char(rdl, tmp);
+               rdline_printf(rdl, "%c", tmp);
        }
        if (!CIRBUF_IS_EMPTY(&rdl->right))
-               rdline_miniprintf(rdl, vt100_multi_left,
-                                 CIRBUF_GET_LEN(&rdl->right));
+               rdline_printf(rdl, vt100_multi_left,
+                             CIRBUF_GET_LEN(&rdl->right));
 }
 
 void
@@ -188,17 +184,16 @@ rdline_redisplay(struct rdline *rdl)
        unsigned int i;
        char tmp;
 
-       rdline_puts(rdl, vt100_home);
-       for (i=0 ; i<rdl->prompt_size ; i++)
-               rdl->write_char(rdl, rdl->prompt[i]);
+       rdline_printf(rdl, vt100_home);
+       rdline_printf(rdl, "%s", rdl->prompt);
        CIRBUF_FOREACH(&rdl->left, i, tmp) {
-               rdl->write_char(rdl, tmp);
+               rdline_printf(rdl, "%c", tmp);
        }
        display_right_buffer(rdl, 1);
 }
 
-int
-rdline_char_in(struct rdline *rdl, char c)
+static int
+rdline_parse_char(struct rdline *rdl, char c)
 {
        unsigned int i;
        int cmd;
@@ -207,11 +202,6 @@ rdline_char_in(struct rdline *rdl, char c)
        char *buf;
 #endif
 
-       if (rdl->status == RDLINE_EXITED)
-               return RDLINE_RES_EXITED;
-       if (rdl->status != RDLINE_RUNNING)
-               return RDLINE_RES_NOT_RUNNING;
-
        cmd = vt100_parser(&rdl->vt100, c);
        if (cmd == VT100_NOT_COMPLETE)
                return RDLINE_RES_SUCCESS;
@@ -225,7 +215,7 @@ rdline_char_in(struct rdline *rdl, char c)
                        tmp = cirbuf_get_tail(&rdl->left);
                        cirbuf_del_tail(&rdl->left);
                        cirbuf_add_head(&rdl->right, tmp);
-                       rdline_puts(rdl, vt100_left_arr);
+                       rdline_printf(rdl, vt100_left_arr);
                        break;
 
                case CMDLINE_KEY_CTRL_F:
@@ -235,21 +225,21 @@ rdline_char_in(struct rdline *rdl, char c)
                        tmp = cirbuf_get_head(&rdl->right);
                        cirbuf_del_head(&rdl->right);
                        cirbuf_add_tail(&rdl->left, tmp);
-                       rdline_puts(rdl, vt100_right_arr);
+                       rdline_printf(rdl, vt100_right_arr);
                        break;
 
                case CMDLINE_KEY_WLEFT:
                        while (! CIRBUF_IS_EMPTY(&rdl->left) &&
                               (tmp = cirbuf_get_tail(&rdl->left)) &&
                               isblank2(tmp)) {
-                               rdline_puts(rdl, vt100_left_arr);
+                               rdline_printf(rdl, vt100_left_arr);
                                cirbuf_del_tail(&rdl->left);
                                cirbuf_add_head(&rdl->right, tmp);
                        }
                        while (! CIRBUF_IS_EMPTY(&rdl->left) &&
                               (tmp = cirbuf_get_tail(&rdl->left)) &&
                               !isblank2(tmp)) {
-                               rdline_puts(rdl, vt100_left_arr);
+                               rdline_printf(rdl, vt100_left_arr);
                                cirbuf_del_tail(&rdl->left);
                                cirbuf_add_head(&rdl->right, tmp);
                        }
@@ -259,14 +249,14 @@ rdline_char_in(struct rdline *rdl, char c)
                        while (! CIRBUF_IS_EMPTY(&rdl->right) &&
                               (tmp = cirbuf_get_head(&rdl->right)) &&
                               isblank2(tmp)) {
-                               rdline_puts(rdl, vt100_right_arr);
+                               rdline_printf(rdl, vt100_right_arr);
                                cirbuf_del_head(&rdl->right);
                                cirbuf_add_tail(&rdl->left, tmp);
                        }
                        while (! CIRBUF_IS_EMPTY(&rdl->right) &&
                               (tmp = cirbuf_get_head(&rdl->right)) &&
                               !isblank2(tmp)) {
-                               rdline_puts(rdl, vt100_right_arr);
+                               rdline_printf(rdl, vt100_right_arr);
                                cirbuf_del_head(&rdl->right);
                                cirbuf_add_tail(&rdl->left, tmp);
                        }
@@ -274,28 +264,32 @@ rdline_char_in(struct rdline *rdl, char c)
 
                case CMDLINE_KEY_BKSPACE:
                        if(!cirbuf_del_tail_safe(&rdl->left)) {
-                               rdline_puts(rdl, vt100_bs);
+                               rdline_printf(rdl, vt100_bs);
                                display_right_buffer(rdl, 1);
                        }
                        break;
 
                case CMDLINE_KEY_META_BKSPACE:
                case CMDLINE_KEY_CTRL_W:
-                       while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
-                               rdline_puts(rdl, vt100_bs);
+                       while (! CIRBUF_IS_EMPTY(&rdl->left) &&
+                              isblank2(cirbuf_get_tail(&rdl->left))) {
+                               rdline_printf(rdl, vt100_bs);
                                cirbuf_del_tail(&rdl->left);
                        }
-                       while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
-                               rdline_puts(rdl, vt100_bs);
+                       while (! CIRBUF_IS_EMPTY(&rdl->left) &&
+                              !isblank2(cirbuf_get_tail(&rdl->left))) {
+                               rdline_printf(rdl, vt100_bs);
                                cirbuf_del_tail(&rdl->left);
                        }
                        display_right_buffer(rdl, 1);
                        break;
 
                case CMDLINE_KEY_META_D:
-                       while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right)))
+                       while (! CIRBUF_IS_EMPTY(&rdl->right) &&
+                              isblank2(cirbuf_get_head(&rdl->right)))
                                cirbuf_del_head(&rdl->right);
-                       while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right)))
+                       while (! CIRBUF_IS_EMPTY(&rdl->right) &&
+                              !isblank2(cirbuf_get_head(&rdl->right)))
                                cirbuf_del_head(&rdl->right);
                        display_right_buffer(rdl, 1);
                        break;
@@ -315,8 +309,8 @@ rdline_char_in(struct rdline *rdl, char c)
                case CMDLINE_KEY_CTRL_A:
                        if (CIRBUF_IS_EMPTY(&rdl->left))
                                break;
-                       rdline_miniprintf(rdl, vt100_multi_left,
-                                           CIRBUF_GET_LEN(&rdl->left));
+                       rdline_printf(rdl, vt100_multi_left,
+                                     CIRBUF_GET_LEN(&rdl->left));
                        while (! CIRBUF_IS_EMPTY(&rdl->left)) {
                                tmp = cirbuf_get_tail(&rdl->left);
                                cirbuf_del_tail(&rdl->left);
@@ -327,8 +321,8 @@ rdline_char_in(struct rdline *rdl, char c)
                case CMDLINE_KEY_CTRL_E:
                        if (CIRBUF_IS_EMPTY(&rdl->right))
                                break;
-                       rdline_miniprintf(rdl, vt100_multi_right,
-                                           CIRBUF_GET_LEN(&rdl->right));
+                       rdline_printf(rdl, vt100_multi_right,
+                                     CIRBUF_GET_LEN(&rdl->right));
                        while (! CIRBUF_IS_EMPTY(&rdl->right)) {
                                tmp = cirbuf_get_head(&rdl->right);
                                cirbuf_del_head(&rdl->right);
@@ -341,7 +335,7 @@ rdline_char_in(struct rdline *rdl, char c)
                        cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
                        rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
                        cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
-                       rdline_puts(rdl, vt100_clear_right);
+                       rdline_printf(rdl, vt100_clear_right);
                        break;
 
                case CMDLINE_KEY_CTRL_Y:
@@ -350,7 +344,7 @@ rdline_char_in(struct rdline *rdl, char c)
                              RDLINE_BUF_SIZE &&
                              i < rdl->kill_size) {
                                cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
-                               rdl->write_char(rdl, rdl->kill_buf[i]);
+                               rdline_printf(rdl, "%c", rdl->kill_buf[i]);
                                i++;
                        }
                        display_right_buffer(rdl, 0);
@@ -358,7 +352,7 @@ rdline_char_in(struct rdline *rdl, char c)
 #endif /* !NO_RDLINE_KILL_BUF */
 
                case CMDLINE_KEY_CTRL_C:
-                       rdline_puts(rdl, "\r\n");
+                       rdline_printf(rdl, "\r\n");
                        rdline_newline(rdl, rdl->prompt);
                        break;
 
@@ -366,66 +360,64 @@ rdline_char_in(struct rdline *rdl, char c)
                        rdline_redisplay(rdl);
                        break;
 
-               case CMDLINE_KEY_TAB:
-               case CMDLINE_KEY_HELP:
+               case CMDLINE_KEY_HELP: {
+                       if (rdl->help == NULL)
+                               break;
+
+                       cirbuf_align_left(&rdl->left);
+                       rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
+                       rdline_printf(rdl, "\r\n");
+                       rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
+                       rdline_redisplay(rdl);
+                       break;
+               }
+
+               case CMDLINE_KEY_TAB: {
+                       char tmp_buf[CMDLINE_MAX_TOKEN_SIZE];
+                       int ret; //, curline = 0;
+                       unsigned int tmp_size;
+
+                       if (rdl->complete == NULL)
+                               break;
+
                        cirbuf_align_left(&rdl->left);
                        rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
-                       if (rdl->complete) {
-                               char tmp_buf[CMDLINE_MAX_TOKEN_SIZE];
-                               int complete_state;
-                               int ret;
-                               unsigned int tmp_size;
-
-                               if (cmd == CMDLINE_KEY_TAB)
-                                       complete_state = 0;
-                               else
-                                       complete_state = -1;
-
-                               /* see in parse.h for help on complete() */
-                               ret = rdl->complete(rdl, rdl->left_buf,
-                                                   tmp_buf, sizeof(tmp_buf),
-                                                   &complete_state);
-                               /* no completion or error */
-                               if (ret <= 0) {
-                                       return RDLINE_RES_COMPLETE;
-                               }
-
-                               tmp_size = strlen(tmp_buf);
-                               /* add chars */
-                               if (ret == RDLINE_RES_COMPLETE) {
-                                       i=0;
-                                       while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
-                                             RDLINE_BUF_SIZE &&
-                                             i < tmp_size) {
-                                               cirbuf_add_tail(&rdl->left, tmp_buf[i]);
-                                               rdl->write_char(rdl, tmp_buf[i]);
-                                               i++;
-                                       }
-                                       display_right_buffer(rdl, 1);
-                                       return RDLINE_RES_COMPLETE; /* ?? */
-                               }
-
-                               /* choice */
-                               rdline_puts(rdl, "\r\n");
-                               while (ret) {
-                                       rdl->write_char(rdl, ' ');
-                                       for (i=0 ; tmp_buf[i] ; i++)
-                                               rdl->write_char(rdl, tmp_buf[i]);
-                                       rdline_puts(rdl, "\r\n");
-                                       ret = rdl->complete(rdl, rdl->left_buf,
-                                                           tmp_buf, sizeof(tmp_buf),
-                                                           &complete_state);
-                               }
 
+                       /* see in parse.h for help on complete() */
+                       ret = rdl->complete(rdl, rdl->left_buf,
+                                           tmp_buf, sizeof(tmp_buf));
+                       /* no completion or error */
+                       if (ret == CMDLINE_COMPLETE_NONE ||
+                           ret == CMDLINE_COMPLETE_MANY) {
+                               cirbuf_align_left(&rdl->left);
+                               rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
+                               rdline_printf(rdl, "\r\n");
+                               rdl->help(rdl, rdl->left_buf, rdline_write, rdl);
                                rdline_redisplay(rdl);
+                               break;
                        }
-                       return RDLINE_RES_COMPLETE;
+
+                       tmp_size = strlen(tmp_buf);
+                       /* add chars */
+                       i = 0;
+                       while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
+                             RDLINE_BUF_SIZE &&
+                             i < tmp_size) {
+                               cirbuf_add_tail(&rdl->left, tmp_buf[i]);
+                               rdline_printf(rdl, "%c", tmp_buf[i]);
+                               i++;
+                       }
+                       display_right_buffer(rdl, 1);
+                       break;
+               }
 
                case CMDLINE_KEY_RETURN:
                case CMDLINE_KEY_RETURN2:
-                       rdline_get_buffer(rdl);
+                       cirbuf_align_left(&rdl->left);
+                       rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\n';
+                       rdl->left_buf[CIRBUF_GET_LEN(&rdl->left) + 1] = '\0';
                        rdl->status = RDLINE_INIT;
-                       rdline_puts(rdl, "\r\n");
+                       rdline_printf(rdl, "\r\n");
 #ifndef NO_RDLINE_HISTORY
                        if (rdl->history_cur_line != -1)
                                rdline_remove_first_history_item(rdl);
@@ -497,12 +489,56 @@ rdline_char_in(struct rdline *rdl, char c)
        if (cirbuf_add_tail_safe(&rdl->left, c))
                return RDLINE_RES_SUCCESS;
 
-       rdl->write_char(rdl, c);
+       rdline_printf(rdl, "%c", c);
        display_right_buffer(rdl, 0);
 
        return RDLINE_RES_SUCCESS;
 }
 
+int
+rdline_char_in(struct rdline *rdl, char c)
+{
+       int ret, same = 0;
+       const char *history, *buffer;
+
+       if (rdl->status == RDLINE_EXITED)
+               return RDLINE_RES_EXITED;
+       if (rdl->status != RDLINE_RUNNING)
+               return RDLINE_RES_NOT_RUNNING;
+
+       ret = rdline_parse_char(rdl, c);
+
+       /* add line to history */
+       if (ret == RDLINE_RES_VALIDATED) {
+               buffer = rdline_get_buffer(rdl);
+               history = rdline_get_history_item(rdl, 0);
+               if (history)
+                       same = !strcmp(buffer, history);
+
+               if (strlen(buffer) >= 1 && same == 0)
+                       rdline_add_history(rdl, buffer);
+       }
+
+       return ret;
+}
+
+int
+rdline(struct rdline *rdl, const char *prompt)
+{
+       char c;
+       int ret = RDLINE_RES_NOT_RUNNING;
+
+       rdline_newline(rdl, prompt);
+       while (1) {
+               if (read(rdl->fd_in, &c, 1) < 0)
+                       break;
+               ret = rdline_char_in(rdl, c);
+               if (ret != RDLINE_RES_SUCCESS)
+                       break;
+       }
+
+       return ret;
+}
 
 /* HISTORY */
 
@@ -579,20 +615,13 @@ rdline_get_history_item(struct rdline * rdl, unsigned int idx)
 int
 rdline_add_history(struct rdline * rdl, const char * buf)
 {
-       unsigned int len, i;
+       unsigned int len;
 
        len = strlen(buf);
-       for (i=0; i<len ; i++) {
-               if (buf[i] == '\n') {
-                       len = i;
-                       break;
-               }
-       }
-
-       if ( len >= RDLINE_HISTORY_BUF_SIZE )
+       if (len >= RDLINE_HISTORY_BUF_SIZE)
                return -1;
 
-       while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
+       while (len >= CIRBUF_GET_FREELEN(&rdl->history)) {
                rdline_remove_old_history_item(rdl);
        }
 
@@ -610,52 +639,68 @@ rdline_clear_history(struct rdline * rdl)
 
 #else /* !NO_RDLINE_HISTORY */
 
-int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
-void rdline_clear_history(struct rdline * rdl) {}
-char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;}
+int rdline_add_history(struct rdline * rdl, const char * buf)
+{
+       return -1;
+}
 
+void rdline_clear_history(struct rdline * rdl)
+{
+       return;
+}
 
-#endif /* !NO_RDLINE_HISTORY */
+char * rdline_get_history_item(struct rdline * rdl, unsigned int i)
+{
+       return NULL;
+}
 
 
-/* STATIC USEFUL FUNCS */
+#endif /* !NO_RDLINE_HISTORY */
 
-static void
-rdline_puts(struct rdline * rdl, const char * buf)
+
+ssize_t
+rdline_write(const struct rdline *rdl, void *buf, size_t count)
 {
-       char c;
-       while ( (c = *(buf++)) != '\0' ) {
-               rdl->write_char(rdl, c);
-       }
+       return write(rdl->fd_out, buf, count);
 }
 
-/* a very very basic printf with one arg and one format 'u' */
-static void
-rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
+int
+rdline_vprintf(const struct rdline *rdl, const char *fmt, va_list ap)
 {
-       char c, started=0, div=100;
+       int ret;
+#ifndef _GNU_SOURCE
+       char *buf;
+#endif
 
-       while ( (c=*(buf++)) ) {
-               if (c != '%') {
-                       rdl->write_char(rdl, c);
-                       continue;
-               }
-               c = *(buf++);
-               if (c != 'u') {
-                       rdl->write_char(rdl, '%');
-                       rdl->write_char(rdl, c);
-                       continue;
-               }
-               /* val is never more than 255 */
-               while (div) {
-                       c = (char)(val / div);
-                       if (c || started) {
-                               rdl->write_char(rdl, (char)(c+'0'));
-                               started = 1;
-                       }
-                       val %= div;
-                       div /= 10;
-               }
-       }
+       if (rdl->fd_out < 0)
+               return -1;
+
+#ifdef _GNU_SOURCE
+       ret = vdprintf(rdl->fd_out, fmt, ap);
+#else
+       buf = malloc(BUFSIZ);
+       if (buf == NULL)
+               return -1;
+
+       ret = vsnprintf(buf, BUFSIZ, fmt, ap);
+
+       if (ret > 0)
+               write(rdl->fd_out, buf, (ret >= BUFSIZ) ? BUFSIZ : ret);
+       free(buf);
+#endif
+
+       return ret;
 }
 
+int
+rdline_printf(const struct rdline *rdl, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = rdline_vprintf(rdl, fmt, ap);
+       va_end(ap);
+
+       return ret;
+}