]> rtime.felk.cvut.cz Git - sysless.git/blob - libs4c/cmdproc/cmd_io_line.c
cmdproc: Add history handling
[sysless.git] / libs4c / cmdproc / cmd_io_line.c
1 /*******************************************************************
2   Components for embedded applications builded for
3   laboratory and medical instruments firmware
4
5   cmd_proc.h - text line command processor
6                designed for instruments control and setup
7                over RS-232 line
8
9   Copyright (C) 2001 by Pavel Pisa pisa@cmp.felk.cvut.cz
10             (C) 2002 by PiKRON Ltd. http://www.pikron.com
11             (C) 2007 by Michal Sojka <sojkam1@fel.cvut.cz>
12
13   This file can be used and copied according to next
14   license alternatives
15    - MPL - Mozilla Public License
16    - GPL - GNU Public License
17    - other license provided by project originators
18  *******************************************************************/
19
20 #include <cmd_proc.h>
21 #include "cmd_proc_priv.h"
22 #include <string.h>
23 #include <ctype.h>
24
25 /* cmd_io line editor */
26
27 /**
28  * Adds new characters to an edit line buffer.
29  *
30  * @param elb Edit line buffer.
31  * @param ch character to add.
32  *
33  * @return 1 in case of end of line, -1 if called at inaproprate time, 0 otherwise.
34  */
35 int cmd_ed_line_buf(ed_line_buf_t *elb, int ch)
36 {
37   int lastch=elb->lastch;
38   if (elb->flg&FL_ELB_INSEND)
39     return -1;
40   elb->lastch=ch;
41   if(!lastch){
42     elb->inbuf=0;               /* Start new line */
43   }
44   if(ch == '\b') {              /* backspace */
45     if (elb->inbuf > 0)
46       elb->inbuf--;
47     return 0;
48   }
49   if((!(elb->flg&FL_ELB_NOCRLF))&&((ch=='\n')||(ch=='\r'))){
50     if((lastch=='\n')&&(ch=='\r')) /* Empty line, ignore it. */
51       return 0;
52     elb->lastch=0;               /* End the string */
53     elb->buf[elb->inbuf]=0;
54     return 1;
55   }
56   if(elb->inbuf>=elb->alloc-1){
57     /* try to reallocate buffer len not implemented */
58     return 0;
59   }
60   elb->buf[elb->inbuf++]=ch;
61   return 0;
62 }
63
64 int cmd_io_line_putc(cmd_io_t *cmd_io,int ch)
65 {
66   return cmd_ed_line_buf(cmd_io->priv.ed_line.out,ch);
67 }
68
69 /* Process pending output */
70 int cmd_io_line_out(cmd_io_t *cmd_io)
71 {
72   cmd_io_t* io_stack=cmd_io->priv.ed_line.io_stack;
73   ed_line_buf_t* ed_line_out=cmd_io->priv.ed_line.out;
74
75   if(!ed_line_out->inbuf) return 0;
76   if(!io_stack)
77     return -1;
78
79   if(!(ed_line_out->flg&FL_ELB_INSEND)){
80     ed_line_out->flg|=FL_ELB_INSEND;
81     ed_line_out->lastch=0;
82   }
83   while(cmd_io_putc(io_stack, ed_line_out->buf[ed_line_out->lastch])>=0){
84     if(++ed_line_out->lastch >= ed_line_out->inbuf){
85       ed_line_out->lastch=0;
86       ed_line_out->inbuf=0;
87       ed_line_out->flg&=~FL_ELB_INSEND;
88       return 0;
89     }
90   }
91   return 1;
92 }
93
94 static void replace_history(ed_line_buf_t *el, cmd_io_t* io_stack)
95 {
96   if(!el->lastch)
97     el->inbuf=0;                /* Make inbuf consistent */
98   while (el->inbuf) {           /* Delete previous input */
99     el->inbuf--;
100     while (cmd_io_putc(io_stack,'\b') < 0);
101   }
102   if (el->hist_idx >= 0) {
103     strncpy(el->buf, el->hist + el->hist_idx*el->alloc, el->alloc);
104     while (el->buf[el->inbuf]) {
105       el->lastch = el->buf[el->inbuf++];
106       while (cmd_io_putc(io_stack, el->lastch) < 0);
107     }
108   }
109   cmd_io_puts(io_stack, "\033[K"); /* Erase End of Line */
110 }
111
112 /**
113  * @return One when the character was consumed, zero otherwise.
114  */
115 static int handle_history(ed_line_buf_t *el, char ch, cmd_io_t* io_stack)
116 {
117   enum { NONE, UP, DOWN } dir = NONE;
118
119   if (!(el->hist && el->hist_size && el->hist_size >= 2*el->alloc))
120     return 0;
121
122   switch (ch) {
123   case 14: /* Ctrl-N */
124     dir = DOWN;
125     break;
126   case 16: /* Ctrl-P */
127     dir = UP;
128     break;
129   case '\033': /* ESC */
130     strcpy(el->esc, "\033");
131     return 1;
132   default:
133     if (!el->esc[0])
134       return 0;
135
136     char *p = el->esc+1;
137     while (*p && p < el->esc + sizeof(el->esc) - 2) p++;
138     if (!*p) {
139       p[0] = ch;
140       p[1] = 0;
141     }
142     if (isalpha(ch)) {
143       if (strcmp(el->esc, "\033[A") == 0) dir = UP;
144       else if (strcmp(el->esc, "\033[B") == 0) dir = DOWN;
145       el->esc[0] = 0;
146     }
147     if (dir == NONE)
148       return 1;
149   }
150
151   if (dir == DOWN) {
152     if (el->hist_idx >= 0)
153       el->hist_idx--;
154     else
155       return 1;
156   } else if (dir == UP) {
157     if ((el->hist_idx+2) * el->alloc <= el->hist_size &&
158         el->hist[(el->hist_idx+1) * el->alloc])
159       el->hist_idx++;
160     else
161       return 1;
162   }
163
164   replace_history(el, io_stack);
165   return 1;
166 }
167
168 /* process input & perform echo if requested */
169 int cmd_io_line_in(cmd_io_t *cmd_io)
170 {
171   int ch;
172   cmd_io_t* io_stack = cmd_io->priv.ed_line.io_stack;
173   ed_line_buf_t *el = cmd_io->priv.ed_line.in;
174
175   if(!io_stack)
176       return -1;
177
178   while((ch=cmd_io_getc(io_stack))>=0){
179 //    DPRINT("Added %c (%d)\n", ch, ch);
180     if (handle_history(el, ch, io_stack) == 1)
181       continue;
182     int eol = cmd_ed_line_buf(el,ch);
183     if (eol == -1)
184       return -1;
185     if(eol){
186       if(el->flg&FL_ELB_ECHO){
187         while(cmd_io_putc(io_stack,'\r')<0);
188         while(cmd_io_putc(io_stack,'\n')<0);
189       }
190       if (el->hist && el->hist_size) {
191         el->hist_idx = -1;
192         if ((el->hist[0] && strcmp(el->buf, el->hist) == 0) || /* The same command as before */
193             !el->buf[0])        /* Empty line */
194           return 1;             /* Don't add to the history */
195         memmove(el->hist + el->alloc, el->hist, el->hist_size - el->alloc);
196         strncpy(el->hist, el->buf, el->alloc);
197       }
198       return 1;
199     }
200     else
201       if(el->flg&FL_ELB_ECHO) {
202         while(cmd_io_putc(io_stack,ch)<0);
203         if (ch == '\b')
204           cmd_io_puts(io_stack, "\033[K"); /* Erase End of Line */
205       }
206   }
207   return 0;
208 }
209
210 /* The possibly blocking read of one line, should be used only
211    when other options fails */
212 char *cmd_io_line_rdline(cmd_io_t *cmd_io, int mode)
213 {
214   int ret;
215   while((ret=cmd_io_line_in(cmd_io))==0)
216     if(!mode) break;
217   if(ret<=0) return NULL;
218   return cmd_io->priv.ed_line.in->buf;
219 }
220
221 /* Local Variables: */
222 /* c-basic-offset: 2 */
223 /* End: */