bb0169da0e8f8c9b73d1510249ab86e34d87bf98
[aversive.git] / modules / ihm / rdline / rdline.c
1 /*  
2  *  Copyright Droids Corporation (2007)
3  *  Olivier MATZ <zer0@droids-corp.org>
4  * 
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *  Revision : $Id: rdline.c,v 1.1.2.9 2009-02-27 21:41:31 zer0 Exp $
20  *
21  *
22  */
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <ctype.h>
29
30 #include <aversive/pgmspace.h>
31
32 #include <cirbuf.h>
33 #include "rdline.h"
34
35 static void rdline_puts_P(struct rdline * rdl, const prog_char * buf);
36 static void rdline_miniprintf_P(struct rdline * rdl, 
37                                 const prog_char * buf, uint8_t val);
38
39 #ifdef CONFIG_MODULE_RDLINE_HISTORY
40 static void rdline_remove_old_history_item(struct rdline * rdl);
41 static void rdline_remove_first_history_item(struct rdline * rdl);
42 static uint8_t rdline_get_history_size(struct rdline * rdl);
43 #endif /* CONFIG_MODULE_RDLINE_HISTORY */
44
45
46 void rdline_init(struct rdline *rdl, 
47                  rdline_write_char_t *write_char,
48                  rdline_validate_t *validate,
49                  rdline_complete_t *complete)
50 {
51         memset(rdl, 0, sizeof(*rdl));
52         rdl->validate = validate;
53         rdl->complete = complete;
54         rdl->write_char = write_char;
55         rdl->status = RDLINE_INIT;
56 #ifdef CONFIG_MODULE_RDLINE_HISTORY
57         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
58 #endif /* CONFIG_MODULE_RDLINE_HISTORY */
59 }
60
61 void
62 rdline_newline(struct rdline * rdl, const char * prompt)
63 {
64         uint8_t i;
65
66         vt100_init(&rdl->vt100);
67         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
68         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
69
70         if (prompt != rdl->prompt)
71                 memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1);
72         rdl->prompt_size = strlen(prompt);
73
74         for (i=0 ; i<rdl->prompt_size ; i++)
75                 rdl->write_char(rdl->prompt[i]);
76         rdl->status = RDLINE_RUNNING;
77
78 #ifdef CONFIG_MODULE_RDLINE_HISTORY 
79         rdl->history_cur_line = -1;
80 #endif /* CONFIG_MODULE_RDLINE_HISTORY */
81 }
82
83 void 
84 rdline_stop(struct rdline * rdl)
85 {
86         rdl->status = RDLINE_INIT;
87 }
88
89 void
90 rdline_restart(struct rdline * rdl)
91 {
92         rdl->status = RDLINE_RUNNING;
93 }
94
95 const char *
96 rdline_get_buffer(struct rdline * rdl)
97 {
98         uint8_t len_l, len_r;
99         cirbuf_align_left(&rdl->left);
100         cirbuf_align_left(&rdl->right);
101
102         len_l = CIRBUF_GET_LEN(&rdl->left);
103         len_r = CIRBUF_GET_LEN(&rdl->right);
104         memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
105
106         rdl->left_buf[len_l + len_r] = '\n';
107         rdl->left_buf[len_l + len_r + 1] = '\0';
108         return rdl->left_buf;
109 }
110
111 static void
112 display_right_buffer(struct rdline * rdl)
113 {
114         uint8_t i;
115         char tmp;
116
117         rdline_puts_P(rdl, PSTR(vt100_clear_right));
118         if (!CIRBUF_IS_EMPTY(&rdl->right)) {
119                 CIRBUF_FOREACH(&rdl->right, i, tmp) {
120                         rdl->write_char(tmp);
121                 }
122                 rdline_miniprintf_P(rdl, PSTR(vt100_multi_left), 
123                                     CIRBUF_GET_LEN(&rdl->right));
124         }
125 }
126
127 void rdline_redisplay(struct rdline * rdl)
128 {
129         uint8_t i;
130         char tmp;
131
132         rdline_puts_P(rdl, PSTR(vt100_home));
133         for (i=0 ; i<rdl->prompt_size ; i++)
134                 rdl->write_char(rdl->prompt[i]);
135         CIRBUF_FOREACH(&rdl->left, i, tmp) {
136                 rdl->write_char(tmp);
137         }
138         display_right_buffer(rdl);
139 }
140
141 int8_t
142 rdline_char_in(struct rdline * rdl, char c)
143 {
144         uint8_t i;
145         int8_t cmd;
146         char tmp;
147 #ifdef CONFIG_MODULE_RDLINE_HISTORY
148         char * buf;
149 #endif
150         
151         if (rdl->status != RDLINE_RUNNING)
152                 return -1;
153
154         cmd = vt100_parser(&rdl->vt100, c);
155         if (cmd == -2)
156                 return 0;
157
158         if (cmd >= 0) {
159                 switch (cmd) {
160                 case KEY_CTRL_B:
161                 case KEY_LEFT_ARR:
162                         if (CIRBUF_IS_EMPTY(&rdl->left))
163                                 break;
164                         tmp = cirbuf_get_tail(&rdl->left);
165                         cirbuf_del_tail(&rdl->left);
166                         cirbuf_add_head(&rdl->right, tmp);
167                         rdline_puts_P(rdl, PSTR(vt100_left_arr));
168                         break;
169
170                 case KEY_CTRL_F:
171                 case KEY_RIGHT_ARR:
172                         if (CIRBUF_IS_EMPTY(&rdl->right))
173                                 break;
174                         tmp = cirbuf_get_head(&rdl->right);
175                         cirbuf_del_head(&rdl->right);
176                         cirbuf_add_tail(&rdl->left, tmp);
177                         rdline_puts_P(rdl, PSTR(vt100_right_arr));
178                         break;
179
180                 case KEY_WLEFT:
181                         while (! CIRBUF_IS_EMPTY(&rdl->left) && 
182                                (tmp = cirbuf_get_tail(&rdl->left)) && 
183                                isblank(tmp)) {
184                                 rdline_puts_P(rdl, PSTR(vt100_left_arr));
185                                 cirbuf_del_tail(&rdl->left);
186                                 cirbuf_add_head(&rdl->right, tmp);
187                         }
188                         while (! CIRBUF_IS_EMPTY(&rdl->left) && 
189                                (tmp = cirbuf_get_tail(&rdl->left)) && 
190                                !isblank(tmp)) {
191                                 rdline_puts_P(rdl, PSTR(vt100_left_arr));
192                                 cirbuf_del_tail(&rdl->left);
193                                 cirbuf_add_head(&rdl->right, tmp);
194                         }                       
195                         break;
196
197                 case KEY_WRIGHT:
198                         while (! CIRBUF_IS_EMPTY(&rdl->right) && 
199                                (tmp = cirbuf_get_head(&rdl->right)) && 
200                                isblank(tmp)) {
201                                 rdline_puts_P(rdl, PSTR(vt100_right_arr));
202                                 cirbuf_del_head(&rdl->right);
203                                 cirbuf_add_tail(&rdl->left, tmp);
204                         }
205                         while (! CIRBUF_IS_EMPTY(&rdl->right) && 
206                                (tmp = cirbuf_get_head(&rdl->right)) && 
207                                !isblank(tmp)) {
208                                 rdline_puts_P(rdl, PSTR(vt100_right_arr));
209                                 cirbuf_del_head(&rdl->right);
210                                 cirbuf_add_tail(&rdl->left, tmp);
211                         }                       
212                         break;
213
214                 case KEY_BKSPACE:
215                         if(!cirbuf_del_tail_safe(&rdl->left)) {
216                                 rdline_puts_P(rdl, PSTR(vt100_bs));
217                                 display_right_buffer(rdl);
218                         }
219                         break;
220
221                 case KEY_META_BKSPACE:
222                         while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank(cirbuf_get_tail(&rdl->left))) {
223                                 rdline_puts_P(rdl, PSTR(vt100_bs));
224                                 cirbuf_del_tail(&rdl->left);
225                         }
226                         while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank(cirbuf_get_tail(&rdl->left))) {
227                                 rdline_puts_P(rdl, PSTR(vt100_bs));
228                                 cirbuf_del_tail(&rdl->left);
229                         }
230                         display_right_buffer(rdl);
231                         break;
232
233                 case KEY_SUPPR:
234                 case KEY_CTRL_D:
235                         if(!cirbuf_del_head_safe(&rdl->right)) {
236                                 display_right_buffer(rdl);
237                         }
238                         if (cmd == KEY_CTRL_D && 
239                             CIRBUF_IS_EMPTY(&rdl->left) &&
240                             CIRBUF_IS_EMPTY(&rdl->right)) {
241                                 return -2;
242                         }
243                         break;
244
245                 case KEY_CTRL_A:
246                         if (CIRBUF_IS_EMPTY(&rdl->left))
247                                 break;
248                         rdline_miniprintf_P(rdl, PSTR(vt100_multi_left), 
249                                             CIRBUF_GET_LEN(&rdl->left));
250                         while (! CIRBUF_IS_EMPTY(&rdl->left)) {
251                                 tmp = cirbuf_get_tail(&rdl->left);
252                                 cirbuf_del_tail(&rdl->left);
253                                 cirbuf_add_head(&rdl->right, tmp);
254                         }
255                         break;
256
257                 case KEY_CTRL_E:
258                         if (CIRBUF_IS_EMPTY(&rdl->right))
259                                 break;
260                         rdline_miniprintf_P(rdl, PSTR(vt100_multi_right), 
261                                             CIRBUF_GET_LEN(&rdl->right));
262                         while (! CIRBUF_IS_EMPTY(&rdl->right)) {
263                                 tmp = cirbuf_get_head(&rdl->right);
264                                 cirbuf_del_head(&rdl->right);
265                                 cirbuf_add_tail(&rdl->left, tmp);
266                         }
267                         break;
268
269 #ifdef CONFIG_MODULE_RDLINE_KILL_BUF
270                 case KEY_CTRL_K:
271                         cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
272                         rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
273                         cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
274                         rdline_puts_P(rdl, PSTR(vt100_clear_right));
275                         break;
276
277                 case KEY_CTRL_Y:
278                         i=0;
279                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
280                               RDLINE_BUF_SIZE && 
281                               i < rdl->kill_size) {
282                                 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
283                                 rdl->write_char(rdl->kill_buf[i]);
284                                 i++;
285                         }
286                         display_right_buffer(rdl);
287                         break;
288 #endif /* CONFIG_MODULE_RDLINE_KILL_BUF */
289
290                 case KEY_CTRL_C:
291                         rdline_puts_P(rdl, PSTR("\r\n"));
292                         rdline_newline(rdl, rdl->prompt);
293                         break;
294
295                 case KEY_CTRL_L:
296                         rdline_redisplay(rdl);
297                         break;
298
299                 case KEY_TAB:
300                 case KEY_HELP:
301                         cirbuf_align_left(&rdl->left);
302                         rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0'; 
303                         if (rdl->complete) {
304                                 char tmp_buf[127]; /* XXX */
305                                 int16_t complete_state;
306                                 int8_t ret;
307                                 int tmp_size;
308
309                                 if (cmd == KEY_TAB)
310                                         complete_state = 0;
311                                 else
312                                         complete_state = -1;
313
314                                 ret = rdl->complete(rdl->left_buf, tmp_buf, sizeof(tmp_buf), 
315                                                     &complete_state);
316                                 /* no completion or error */
317                                 if (ret <= 0) {
318                                         return 2;
319                                 }
320
321                                 tmp_size = strlen(tmp_buf);
322                                 /* add chars */
323                                 if (ret == 2) {
324                                         i=0;
325                                         while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
326                                               RDLINE_BUF_SIZE && 
327                                               i < tmp_size) {
328                                                 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
329                                                 rdl->write_char(tmp_buf[i]);
330                                                 i++;
331                                         }
332                                         display_right_buffer(rdl);
333                                         return 2; /* ?? */
334                                 }
335
336                                 /* choice */
337                                 rdline_puts_P(rdl, PSTR("\r\n"));
338                                 while (ret) {
339                                         rdl->write_char(' ');
340                                         for (i=0 ; tmp_buf[i] ; i++)
341                                                 rdl->write_char(tmp_buf[i]);
342                                         rdline_puts_P(rdl, PSTR("\r\n"));
343                                         ret = rdl->complete(rdl->left_buf, tmp_buf, 
344                                                             sizeof(tmp_buf), &complete_state);
345                                 }
346
347                                 rdline_redisplay(rdl);
348                         }
349                         return 2;
350
351                 case KEY_RETURN:
352                 case KEY_RETURN2:
353                         rdline_get_buffer(rdl);
354                         rdl->status = RDLINE_INIT;
355                         rdline_puts_P(rdl, PSTR("\r\n"));
356 #ifdef CONFIG_MODULE_RDLINE_HISTORY
357                         if (rdl->history_cur_line != -1)
358                                 rdline_remove_first_history_item(rdl);
359 #endif
360
361                         if (rdl->validate)
362                                 rdl->validate(rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
363                         return 1;
364                         
365 #ifdef CONFIG_MODULE_RDLINE_HISTORY
366                 case KEY_UP_ARR:
367                         if (rdl->history_cur_line == 0) {
368                                 rdline_remove_first_history_item(rdl);
369                         }
370                         if (rdl->history_cur_line <= 0) {
371                                 rdline_add_history(rdl, rdline_get_buffer(rdl));
372                                 rdl->history_cur_line = 0;
373                         }
374                         
375                         buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
376                         if (!buf)
377                                 break;
378                         
379                         rdl->history_cur_line ++;
380                         vt100_init(&rdl->vt100);
381                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
382                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
383                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
384                         rdline_redisplay(rdl);
385                         break;
386
387                 case KEY_DOWN_ARR:
388                         if (rdl->history_cur_line - 1 < 0)
389                                 break;
390                         
391                         rdl->history_cur_line --;
392                         buf = rdline_get_history_item(rdl, rdl->history_cur_line);
393                         if (!buf)
394                                 break;
395                         vt100_init(&rdl->vt100);
396                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
397                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
398                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
399                         rdline_redisplay(rdl);
400
401                         break;
402 #endif /* CONFIG_MODULE_RDLINE_HISTORY */
403
404
405                 default:
406                         break;
407                 }
408
409                 return 0;
410         }
411         
412         if (! isprint(c))
413                 return 0;
414
415         /* standard chars */
416         if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
417                 return 0;
418                 
419         if (cirbuf_add_tail_safe(&rdl->left, c))
420                 return 0;
421
422         rdl->write_char(c);
423         display_right_buffer(rdl);
424
425         return 0;
426 }
427
428
429 /* HISTORY */
430
431 #ifdef CONFIG_MODULE_RDLINE_HISTORY
432 static void
433 rdline_remove_old_history_item(struct rdline * rdl)
434 {
435         char tmp;
436
437         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
438                 tmp = cirbuf_get_head(&rdl->history);
439                 cirbuf_del_head(&rdl->history);
440                 if (!tmp)
441                         break;
442         }
443 }
444
445 static void
446 rdline_remove_first_history_item(struct rdline * rdl)
447 {
448         char tmp;
449
450         if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
451                 return;
452         }
453         else {
454                 cirbuf_del_tail(&rdl->history);
455         }
456
457         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
458                 tmp = cirbuf_get_tail(&rdl->history);
459                 if (!tmp)
460                         break;
461                 cirbuf_del_tail(&rdl->history);
462         }
463 }
464
465 static uint8_t
466 rdline_get_history_size(struct rdline * rdl)
467 {
468         uint8_t i, tmp, ret=0;
469
470         CIRBUF_FOREACH(&rdl->history, i, tmp) {
471                 if (tmp == 0)
472                         ret ++;
473         }
474
475         return ret;
476 }
477
478 char *
479 rdline_get_history_item(struct rdline * rdl, uint8_t idx)
480 {
481         uint8_t len, i, tmp;
482
483         len = rdline_get_history_size(rdl);
484         if ( idx >= len ) {
485                 return NULL;
486         }
487
488         cirbuf_align_left(&rdl->history);
489
490         CIRBUF_FOREACH(&rdl->history, i, tmp) {
491                 if ( idx == len - 1) {
492                         return rdl->history_buf + i;
493                 }
494                 if (tmp == 0)
495                         len --;
496         }
497
498         return NULL;
499 }
500
501 int8_t 
502 rdline_add_history(struct rdline * rdl, const char * buf)
503 {
504         cirbuf_uint len, i;
505
506         len = strlen(buf);
507         for (i=0; i<len ; i++) {
508                 if (buf[i] == '\n') {
509                         len = i;
510                         break;
511                 }
512         }
513
514         if ( len >= RDLINE_HISTORY_BUF_SIZE )
515                 return -1;
516
517         while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
518                 rdline_remove_old_history_item(rdl);
519         }
520
521         cirbuf_add_buf_tail(&rdl->history, buf, len);
522         cirbuf_add_tail(&rdl->history, 0);
523         
524         return 0;
525 }
526
527 void
528 rdline_clear_history(struct rdline * rdl)
529 {
530         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
531 }
532
533 #else /* CONFIG_MODULE_RDLINE_HISTORY */
534
535 int8_t rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
536 void rdline_clear_history(struct rdline * rdl) {}
537 char * rdline_get_history_item(struct rdline * rdl, uint8_t i) {return NULL;}
538
539
540 #endif /* CONFIG_MODULE_RDLINE_HISTORY */
541
542
543 /* STATIC USEFUL FUNCS */
544
545 static void 
546 rdline_puts_P(struct rdline * rdl, const prog_char * buf)
547 {
548         char c;
549         while ( (c=pgm_read_byte(buf++)) != '\0' ) {
550                 rdl->write_char(c);
551         }
552 }
553
554 /* a very very basic printf with one arg and one format 'u' */
555 static void 
556 rdline_miniprintf_P(struct rdline * rdl, const prog_char * buf, uint8_t val)
557 {
558         char c, started=0, div=100;
559
560         while ( (c=pgm_read_byte(buf++)) ) {
561                 if (c=='%') {
562                         c = pgm_read_byte(buf++);
563
564                         if (c=='u') { /* val is never more than 255 */
565                                 while (div) {
566                                         c = val / div;
567                                         if (c || started) {
568                                                 rdl->write_char(c+'0');
569                                                 started = 1;
570                                         }
571                                         val %= div;
572                                         div /= 10;
573                                 }
574                         }
575                         else {
576                                 rdl->write_char('%');
577                                 rdl->write_char(c);
578                         }
579                 }
580                 else {
581                         rdl->write_char(c);
582                 }
583         }
584 }
585