]> rtime.felk.cvut.cz Git - sysless.git/blobdiff - libs4c/cmdproc/cmd_io_line.c
cmdproc: Make backspace work even in sterm
[sysless.git] / libs4c / cmdproc / cmd_io_line.c
index 6e85cdc2b9afcdd40cff094f1260120dc7584bd1..3bcca497d9695b31a52a1f27279f30f087a85cc0 100644 (file)
@@ -1,11 +1,11 @@
 /*******************************************************************
   Components for embedded applications builded for
-  laboratory and medical instruments firmware  
+  laboratory and medical instruments firmware
+
   cmd_proc.h - text line command processor
                designed for instruments control and setup
               over RS-232 line
+
   Copyright (C) 2001 by Pavel Pisa pisa@cmp.felk.cvut.cz
             (C) 2002 by PiKRON Ltd. http://www.pikron.com
             (C) 2007 by Michal Sojka <sojkam1@fel.cvut.cz>
 #include <cmd_proc.h>
 #include "cmd_proc_priv.h"
 #include <string.h>
+#include <ctype.h>
 
 /* cmd_io line editor */
 
-/** 
+/**
  * Adds new characters to an edit line buffer.
- * 
+ *
  * @param elb Edit line buffer.
  * @param ch character to add.
- * 
- * @return 1 in case of end of line, 0 otherwise.
+ *
+ * @return 1 in case of end of line, -1 if called at inaproprate time, 0 otherwise.
  */
 int cmd_ed_line_buf(ed_line_buf_t *elb, int ch)
 {
@@ -40,6 +41,11 @@ int cmd_ed_line_buf(ed_line_buf_t *elb, int ch)
   if(!lastch){
     elb->inbuf=0;               /* Start new line */
   }
+  if(ch == '\b' || ch == 127) {                /* backspace, del (sometimes backspace = 127) */
+    if (elb->inbuf > 0)
+      elb->inbuf--;
+    return 0;
+  }
   if((!(elb->flg&FL_ELB_NOCRLF))&&((ch=='\n')||(ch=='\r'))){
     if((lastch=='\n')&&(ch=='\r')) /* Empty line, ignore it. */
       return 0;
@@ -47,11 +53,11 @@ int cmd_ed_line_buf(ed_line_buf_t *elb, int ch)
     elb->buf[elb->inbuf]=0;
     return 1;
   }
-  if(elb->inbuf>=elb->alloc-1){ 
+  if(elb->inbuf>=elb->alloc-1){
     /* try to reallocate buffer len not implemented */
     return 0;
   }
-  elb->buf[elb->inbuf++]=ch;  
+  elb->buf[elb->inbuf++]=ch;
   return 0;
 }
 
@@ -65,11 +71,11 @@ int cmd_io_line_out(cmd_io_t *cmd_io)
 {
   cmd_io_t* io_stack=cmd_io->priv.ed_line.io_stack;
   ed_line_buf_t* ed_line_out=cmd_io->priv.ed_line.out;
-  
+
   if(!ed_line_out->inbuf) return 0;
   if(!io_stack)
     return -1;
-  
+
   if(!(ed_line_out->flg&FL_ELB_INSEND)){
     ed_line_out->flg|=FL_ELB_INSEND;
     ed_line_out->lastch=0;
@@ -85,43 +91,135 @@ int cmd_io_line_out(cmd_io_t *cmd_io)
   return 1;
 }
 
-/* process input */
+static void replace_history(ed_line_buf_t *el, cmd_io_t* io_stack)
+{
+  if(!el->lastch)
+    el->inbuf=0;               /* Make inbuf consistent */
+  while (el->inbuf) {          /* Delete previous input */
+    el->inbuf--;
+    while (cmd_io_putc(io_stack,'\b') < 0);
+  }
+  if (el->hist_idx >= 0) {
+    strncpy(el->buf, el->hist + el->hist_idx*el->alloc, el->alloc);
+    while (el->buf[el->inbuf]) {
+      el->lastch = el->buf[el->inbuf++];
+      while (cmd_io_putc(io_stack, el->lastch) < 0);
+    }
+  }
+  cmd_io_puts(io_stack, "\033[K"); /* Erase End of Line */
+}
+
+/**
+ * @return One when the character was consumed, zero otherwise.
+ */
+static int handle_history(ed_line_buf_t *el, char ch, cmd_io_t* io_stack)
+{
+  enum { NONE, UP, DOWN } dir = NONE;
+
+  if (!(el->hist && el->hist_size && el->hist_size >= 2*el->alloc))
+    return 0;
+
+  switch (ch) {
+  case 14: /* Ctrl-N */
+    dir = DOWN;
+    break;
+  case 16: /* Ctrl-P */
+    dir = UP;
+    break;
+  case '\033': /* ESC */
+    strcpy(el->esc, "\033");
+    return 1;
+  default:
+    if (!el->esc[0])
+      return 0;
+
+    char *p = el->esc+1;
+    while (*p && p < el->esc + sizeof(el->esc) - 2) p++;
+    if (!*p) {
+      p[0] = ch;
+      p[1] = 0;
+    }
+    if (isalpha(ch)) {
+      if (strcmp(el->esc, "\033[A") == 0) dir = UP;
+      else if (strcmp(el->esc, "\033[B") == 0) dir = DOWN;
+      el->esc[0] = 0;
+    }
+    if (dir == NONE)
+      return 1;
+  }
+
+  if (dir == DOWN) {
+    if (el->hist_idx >= 0)
+      el->hist_idx--;
+    else
+      return 1;
+  } else if (dir == UP) {
+    if ((el->hist_idx+2) * el->alloc <= el->hist_size &&
+       el->hist[(el->hist_idx+1) * el->alloc])
+      el->hist_idx++;
+    else
+      return 1;
+  }
+
+  replace_history(el, io_stack);
+  return 1;
+}
+
+/* process input & perform echo if requested */
 int cmd_io_line_in(cmd_io_t *cmd_io)
 {
   int ch;
   cmd_io_t* io_stack = cmd_io->priv.ed_line.io_stack;
-  ed_line_buf_t *ed_line_in = cmd_io->priv.ed_line.in;
+  ed_line_buf_t *el = cmd_io->priv.ed_line.in;
 
   if(!io_stack)
       return -1;
 
   while((ch=cmd_io_getc(io_stack))>=0){
 //    DPRINT("Added %c (%d)\n", ch, ch);
-    int eol = cmd_ed_line_buf(ed_line_in,ch);
+    if (handle_history(el, ch, io_stack) == 1)
+      continue;
+    int eol = cmd_ed_line_buf(el,ch);
+    if (eol == -1)
+      return -1;
     if(eol){
-      if(ed_line_in->flg&FL_ELB_ECHO){
+      if(el->flg&FL_ELB_ECHO){
         while(cmd_io_putc(io_stack,'\r')<0);
         while(cmd_io_putc(io_stack,'\n')<0);
       }
+      if (el->hist && el->hist_size) {
+       el->hist_idx = -1;
+       if ((el->hist[0] && strcmp(el->buf, el->hist) == 0) || /* The same command as before */
+           !el->buf[0])        /* Empty line */
+         return 1;             /* Don't add to the history */
+       memmove(el->hist + el->alloc, el->hist, el->hist_size - el->alloc);
+       strncpy(el->hist, el->buf, el->alloc);
+      }
       return 1;
     }
-    else 
-      if(ed_line_in->flg&FL_ELB_ECHO) {
+    else
+      if(el->flg&FL_ELB_ECHO) {
         while(cmd_io_putc(io_stack,ch)<0);
+        if (ch == 127)
+                 while(cmd_io_putc(io_stack,'\b')<0);
+               if (ch == '\b' || ch == 127)
+                 cmd_io_puts(io_stack, "\033[K"); /* Erase End of Line */
       }
   }
   return 0;
 }
 
-/* char *cmd_io_line_rdline(cmd_io_t *cmd_io, int mode) */
-/* { */
-/*   int ret; */
-/*   while((ret=cmd_rs232_line_in(cmd_io))==0) */
-/*     if(!mode) break; */
-/*   if(ret<=0) return NULL; */
-/*   return cmd_io->priv.ed_line.in->buf; */
-/* } */
+/* The possibly blocking read of one line, should be used only
+   when other options fails */
+char *cmd_io_line_rdline(cmd_io_t *cmd_io, int mode)
+{
+  int ret;
+  while((ret=cmd_io_line_in(cmd_io))==0)
+    if(!mode) break;
+  if(ret<=0) return NULL;
+  return cmd_io->priv.ed_line.in->buf;
+}
 
 /* Local Variables: */
 /* c-basic-offset: 2 */
-/* End */
+/* End: */