add emergency "pop"
[protos/xbee-avr.git] / 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_STOPPED;
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_STOPPED;
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                         rdline_puts_P(rdl, PSTR("\r\n"));
355 #ifdef CONFIG_MODULE_RDLINE_HISTORY
356                         if (rdl->history_cur_line != -1)
357                                 rdline_remove_first_history_item(rdl);
358 #endif
359
360                         if (rdl->validate)
361                                 rdl->validate(rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
362                         return 1;
363                         
364 #ifdef CONFIG_MODULE_RDLINE_HISTORY
365                 case KEY_UP_ARR:
366                         if (rdl->history_cur_line == 0) {
367                                 rdline_remove_first_history_item(rdl);
368                         }
369                         if (rdl->history_cur_line <= 0) {
370                                 rdline_add_history(rdl, rdline_get_buffer(rdl));
371                                 rdl->history_cur_line = 0;
372                         }
373                         
374                         buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
375                         if (!buf)
376                                 break;
377                         
378                         rdl->history_cur_line ++;
379                         vt100_init(&rdl->vt100);
380                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
381                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
382                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
383                         rdline_redisplay(rdl);
384                         break;
385
386                 case KEY_DOWN_ARR:
387                         if (rdl->history_cur_line - 1 < 0)
388                                 break;
389                         
390                         rdl->history_cur_line --;
391                         buf = rdline_get_history_item(rdl, rdl->history_cur_line);
392                         if (!buf)
393                                 break;
394                         vt100_init(&rdl->vt100);
395                         cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
396                         cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
397                         cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
398                         rdline_redisplay(rdl);
399
400                         break;
401 #endif /* CONFIG_MODULE_RDLINE_HISTORY */
402
403
404                 default:
405                         break;
406                 }
407
408                 return 0;
409         }
410         
411         if (! isprint(c))
412                 return 0;
413
414         /* standard chars */
415         if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
416                 return 0;
417                 
418         if (cirbuf_add_tail_safe(&rdl->left, c))
419                 return 0;
420
421         rdl->write_char(c);
422         display_right_buffer(rdl);
423
424         return 0;
425 }
426
427
428 /* HISTORY */
429
430 #ifdef CONFIG_MODULE_RDLINE_HISTORY
431 static void
432 rdline_remove_old_history_item(struct rdline * rdl)
433 {
434         char tmp;
435
436         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
437                 tmp = cirbuf_get_head(&rdl->history);
438                 cirbuf_del_head(&rdl->history);
439                 if (!tmp)
440                         break;
441         }
442 }
443
444 static void
445 rdline_remove_first_history_item(struct rdline * rdl)
446 {
447         char tmp;
448
449         if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
450                 return;
451         }
452         else {
453                 cirbuf_del_tail(&rdl->history);
454         }
455
456         while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
457                 tmp = cirbuf_get_tail(&rdl->history);
458                 if (!tmp)
459                         break;
460                 cirbuf_del_tail(&rdl->history);
461         }
462 }
463
464 static uint8_t
465 rdline_get_history_size(struct rdline * rdl)
466 {
467         uint8_t i, tmp, ret=0;
468
469         CIRBUF_FOREACH(&rdl->history, i, tmp) {
470                 if (tmp == 0)
471                         ret ++;
472         }
473
474         return ret;
475 }
476
477 char *
478 rdline_get_history_item(struct rdline * rdl, uint8_t idx)
479 {
480         uint8_t len, i, tmp;
481
482         len = rdline_get_history_size(rdl);
483         if ( idx >= len ) {
484                 return NULL;
485         }
486
487         cirbuf_align_left(&rdl->history);
488
489         CIRBUF_FOREACH(&rdl->history, i, tmp) {
490                 if ( idx == len - 1) {
491                         return rdl->history_buf + i;
492                 }
493                 if (tmp == 0)
494                         len --;
495         }
496
497         return NULL;
498 }
499
500 int8_t 
501 rdline_add_history(struct rdline * rdl, const char * buf)
502 {
503         cirbuf_uint len, i;
504
505         len = strlen(buf);
506         for (i=0; i<len ; i++) {
507                 if (buf[i] == '\n') {
508                         len = i;
509                         break;
510                 }
511         }
512
513         if ( len >= RDLINE_HISTORY_BUF_SIZE )
514                 return -1;
515
516         while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
517                 rdline_remove_old_history_item(rdl);
518         }
519
520         cirbuf_add_buf_tail(&rdl->history, buf, len);
521         cirbuf_add_tail(&rdl->history, 0);
522         
523         return 0;
524 }
525
526 void
527 rdline_clear_history(struct rdline * rdl)
528 {
529         cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
530 }
531
532 #else /* CONFIG_MODULE_RDLINE_HISTORY */
533
534 int8_t rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
535 void rdline_clear_history(struct rdline * rdl) {}
536 char * rdline_get_history_item(struct rdline * rdl, uint8_t i) {return NULL;}
537
538
539 #endif /* CONFIG_MODULE_RDLINE_HISTORY */
540
541
542 /* STATIC USEFUL FUNCS */
543
544 static void 
545 rdline_puts_P(struct rdline * rdl, const prog_char * buf)
546 {
547         char c;
548         while ( (c=pgm_read_byte(buf++)) != '\0' ) {
549                 rdl->write_char(c);
550         }
551 }
552
553 /* a very very basic printf with one arg and one format 'u' */
554 static void 
555 rdline_miniprintf_P(struct rdline * rdl, const prog_char * buf, uint8_t val)
556 {
557         char c, started=0, div=100;
558
559         while ( (c=pgm_read_byte(buf++)) ) {
560                 if (c=='%') {
561                         c = pgm_read_byte(buf++);
562
563                         if (c=='u') { /* val is never more than 255 */
564                                 while (div) {
565                                         c = val / div;
566                                         if (c || started) {
567                                                 rdl->write_char(c+'0');
568                                                 started = 1;
569                                         }
570                                         val %= div;
571                                         div /= 10;
572                                 }
573                         }
574                         else {
575                                 rdl->write_char('%');
576                                 rdl->write_char(c);
577                         }
578                 }
579                 else {
580                         rdl->write_char(c);
581                 }
582         }
583 }
584