4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
30 * Copyright 2004 Giovanni Bajo
31 * All Rights Reserved.
34 * \brief Line editing support with history
36 * Rationale for basic implementation choices:
38 * \li The history is implemented storing consecutive ASCIIZ strings within an array of memory. When
39 * the history is full, the first (oldest) line is cancelled and the whole buffer is memmoved to
40 * overwrite it and make room. while this is is obviously not the fastest algorithm (which would
41 * require the use of a circular buffer) it is surely good enough for this module, which does not
42 * aim at fast performances (line editing does not require to be blazingly fast).
44 * \li The first character in the history is always \c \\0, and it is used as a guard. By 'wasting' it
45 * in this way, the code actually gets much simpler in that we remove many checks when moving
46 * backward (\c i>0 and similar).
48 * \li While editing, the current index points to the position of the buffer which contains the
49 * last character typed in (exactly like a stack pointer). This also allows to simplify calculations
50 * and to make easier using the last byte of history.
52 * \li While editing, the current line is always kept null-terminated. This is important because
53 * if the user press ENTER, we must have room to add a \c \\0 to terminate the line. If the line
54 * is as long as the whole history buffer, there would not be space for it. By always keeping the
55 * \c \\0 at the end, we properly ensure this without making index checks harder.
57 * \li When removing a line from the history (see \c pop_history()), instead of updating all the
58 * indices we have around, we move backward the pointer to the history we use. This way, we don't
59 * have to update anything. This means that we keep two pointers to the history: \c real_history
60 * always points to the physical start, while \c history is the adjusted pointer (that is
61 * dereference to read/write to it).
63 * \todo Use up/down to move through history The history line will be copied to the current line,
64 * making sure there is room for it.
68 * \author Giovanni Bajo <rasky@develer.com>
74 #include <cfg/compiler.h>
75 #include <cfg/debug.h>
80 /// Enable compilation of the unit test code
81 #define DEBUG_UNIT_TEST 0
83 /// Enable dump of the history after each line
84 #define DEBUG_DUMP_HISTORY 0
87 /** Special keys (escape sequences converted to a single code) */
89 SPECIAL_KEYS = 0x1000,
94 * UpArrow: 0x1B 0x5B 0X41
95 * DownArrow: 0x1B 0x5B 0X42
96 * RightArrow: 0x1B 0x5B 0x43
97 * LeftArrow: 0x1b 0x5B 0x44
98 * Beak(Pause): 0x1b 0x5B 0x50
109 * F1: 0x1b 0x5B 0x5B 0x41
110 * F2: 0x1b 0x5B 0x5B 0x42
111 * F3: 0x1b 0x5B 0x5B 0x43
112 * F4: 0x1b 0x5B 0x5B 0x44
113 * F5: 0x1b 0x5B 0x5B 0x45
114 * Ins: 0x1b 0x5B 0x32 0x7E
115 * Home: 0x1b 0x5B 0x31 0x7E
116 * PgUp: 0x1b 0x5B 0x35 0x7E
117 * Del: 0x1b 0x5B 0x33 0x7E
118 * End: 0x1b 0x5B 0x34 0x7E
119 * PgDn: 0x1b 0x5B 0x36 0x7E
121 KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
122 KEY_INS, KEY_HOME, KEY_PGUP, KEY_DEL, KEY_END, KEY_PGDN,
127 * F6: 0x1b 0x5B 0x31 0x37 0x7E
128 * F7: 0x1b 0x5B 0x31 0x38 0x7E
129 * F8: 0x1b 0x5B 0x31 0x39 0x7E
130 * F9: 0x1b 0x5B 0x32 0x30 0x7E
131 * F10: 0x1b 0x5B 0x32 0x31 0x7E
132 * F11: 0x1b 0x5B 0x32 0x33 0x7E
133 * F12: 0x1b 0x5B 0x32 0x34 0x7E
135 KEY_F6, KEY_F7, KEY_F8, KEY_F9,
136 KEY_F10, KEY_F11, KEY_F12,
139 /** Check if \a c is a separator between words.
140 * \note Parameter \a c is evaluated multiple times
142 #define IS_WORD_SEPARATOR(c) ((c) == ' ' || (c) == '\0')
144 /// Write the string \a txt to the IO output (without any kind of termination)
145 INLINE void rl_puts(const struct RLContext* ctx, const char* txt)
151 ctx->put(*txt++, ctx->put_param);
154 /// Write character \a ch to the IO output.
155 INLINE void rl_putc(const struct RLContext* ctx, char ch)
158 ctx->put(ch, ctx->put_param);
161 /** Read a character from the IO into \a ch. This function also takes
162 * care of converting the ANSI escape sequences into one of the codes
163 * defined in \c RL_KEYS.
165 static bool rl_getc(const struct RLContext* ctx, int* ch)
167 int c = ctx->get(ctx->get_param);
173 // Unknown ESC sequence. Ignore it and read
174 // return next character.
175 if (ctx->get(ctx->get_param) != 0x5B)
176 return rl_getc(ctx, ch);
179 * Home: 0x1b 0x5B 0x31 0x7E
180 * F6: 0x1b 0x5B 0x31 0x37 0x7E
181 * F7: 0x1b 0x5B 0x31 0x38 0x7E
182 * F8: 0x1b 0x5B 0x31 0x39 0x7E
183 * Ins: 0x1b 0x5B 0x32 0x7E
184 * F9: 0x1b 0x5B 0x32 0x30 0x7E
185 * F10: 0x1b 0x5B 0x32 0x31 0x7E
186 * F11: 0x1b 0x5B 0x32 0x33 0x7E
187 * F12: 0x1b 0x5B 0x32 0x34 0x7E
188 * Del: 0x1b 0x5B 0x33 0x7E
189 * End: 0x1b 0x5B 0x34 0x7E
190 * PgUp: 0x1b 0x5B 0x35 0x7E
191 * PgDn: 0x1b 0x5B 0x36 0x7E
194 c = ctx->get(ctx->get_param);
197 case 0x41: c = KEY_UP_ARROW; break;
198 case 0x42: c = KEY_DOWN_ARROW; break;
199 case 0x43: c = KEY_RIGHT_ARROW; break;
200 case 0x44: c = KEY_LEFT_ARROW; break;
201 case 0x50: c = KEY_PAUSE; break;
203 c = ctx->get(ctx->get_param);
206 case 0x41: c = KEY_F1; break;
207 case 0x42: c = KEY_F2; break;
208 case 0x43: c = KEY_F3; break;
209 case 0x44: c = KEY_F4; break;
210 case 0x45: c = KEY_F5; break;
211 default: return rl_getc(ctx, ch);
214 default: return rl_getc(ctx, ch);
222 INLINE void beep(struct RLContext* ctx)
227 static bool pop_history(struct RLContext* ctx, int total_len)
229 // Compute the length of the first command (including terminator).
230 int len = strlen(ctx->real_history+1)+1;
232 // (the first byte of the history should always be 0)
233 ASSERT(ctx->real_history[0] == '\0');
235 // If it is the only one in the history, do nothing
236 if (len == total_len)
239 // Overwrite the first command with the second one
240 memmove(ctx->real_history, ctx->real_history+len, HISTORY_SIZE-len);
242 // Move back the ctx->buffer pointer so that all the indices are still valid
248 /// Check if index \a i points to the begin of the history.
249 INLINE bool is_history_begin(struct RLContext* ctx, int i)
250 { return ctx->history + i == ctx->real_history; }
252 /// Check if index \a i points to the (exclusive) end of history
253 INLINE bool is_history_end(struct RLContext* ctx, int i)
254 { return ctx->history + i == ctx->real_history + HISTORY_SIZE; }
256 /// Check if index \a i points to the (exclusive) end of history, or somewhere past the end.
257 INLINE bool is_history_past_end(struct RLContext* ctx, int i)
258 { return ctx->history + i >= ctx->real_history + HISTORY_SIZE; }
260 /** Insert \a num_chars characters from \a ch into the history buffer at the
261 * position indicated by \a curpos. If needed, remove old history to make room.
262 * Returns true if everything was successful, false if there was no room to
263 * add the characters.
264 * \note \a num_chars can be 0, in which case we just make sure the line is
265 * correctly zero-terminated (ASCIIZ format).
267 static bool insert_chars(struct RLContext* ctx, size_t *curpos, const char* ch, int num_chars)
269 ASSERT(!is_history_past_end(ctx, *curpos));
271 while (is_history_past_end(ctx, *curpos+num_chars+1))
273 if (!pop_history(ctx, *curpos))
278 ctx->history[++(*curpos)] = *ch++;
280 ASSERT(!is_history_past_end(ctx, *curpos + 1));
281 ctx->history[*curpos+1] = '\0';
285 /// Insert a single character \a ch into the buffer (with the same semantic of \c insert_chars())
286 static bool insert_char(struct RLContext* ctx, size_t *curpos, char ch)
288 return insert_chars(ctx, curpos, &ch, 1);
291 #if DEBUG_DUMP_HISTORY
292 /// Dump the internal history of a context (used only for debug purposes)
293 static void dump_history(struct RLContext* ctx)
297 ASSERT(ctx->real_history[0] == '\0');
298 rl_puts(ctx, "History dump:");
299 rl_puts(ctx, "\r\n");
301 ctx->real_history + k != ctx->history + ctx->history_pos + 1;
302 k += strlen(&ctx->real_history[k]) + 1)
304 rl_puts(ctx, &ctx->real_history[k]);
305 rl_puts(ctx, "\r\n");
308 sprintf(buf, "%d\r\n", ctx->history_pos + (ctx->history - ctx->real_history));
311 #endif /* DEBUG_DUMP_HISTORY */
313 /// Complete the current word. Return false if no unambiguous completion was found
314 static bool complete_word(struct RLContext *ctx, size_t *curpos)
316 const char* completed_word;
319 // If the current character is a separator,
320 // there is nothing to complete
322 if (IS_WORD_SEPARATOR(ctx->history[wstart]))
328 // Find the separator before the current word
331 while (!IS_WORD_SEPARATOR(ctx->history[wstart]));
333 // Complete the word through the hook
334 completed_word = ctx->match(ctx->match_param, ctx->history + wstart + 1, *curpos - wstart);
338 // Move back the terminal cursor to the separator
339 while (*curpos != wstart)
345 // Insert the completed command
346 insert_chars(ctx, curpos, completed_word, strlen(completed_word));
347 rl_puts(ctx, completed_word);
348 insert_char(ctx, curpos, ' ');
354 void rl_refresh(struct RLContext* ctx)
356 rl_puts(ctx, "\r\n");
358 rl_puts(ctx, ctx->prompt);
359 rl_puts(ctx, ctx->history + ctx->history_pos + 1);
362 const char* rl_readline(struct RLContext* ctx)
364 size_t i = ctx->history_pos;
367 rl_puts(ctx, ctx->prompt);
369 insert_chars(ctx, &i, NULL, 0);
376 ASSERT(ctx->history - ctx->real_history + i < HISTORY_SIZE);
378 if (!rl_getc(ctx, &c))
381 // Just ignore special keys for now
382 if (c > SPECIAL_KEYS)
387 // Ask the match hook if available
391 complete_word(ctx, &i);
395 if (c == '\r' || c == '\n')
397 rl_puts(ctx, "\r\n");
401 // Backspace cancels a character, or it is ignored if at
402 // the start of the line
405 if (ctx->history[i] != '\0')
408 rl_puts(ctx, "\b \b");
413 // Add a character to the buffer, if possible
415 ASSERT2(ch == c, "a special key was not properly handled");
416 if (insert_chars(ctx, &i, &ch, 1))
422 ctx->history_pos = i+1;
423 while (ctx->history[i] != '\0')
426 // Do not store empty lines in the history
427 if (i == ctx->history_pos - 1)
428 ctx->history_pos -= 1;
430 #if DEBUG_DUMP_HISTORY
434 // Since the current pointer now points to the separator, we need
435 // to return the first character
436 return &ctx->history[i+1];
442 /** Perform the unit test for the readline library */
445 #if HISTORY_SIZE != 32
446 #error This test needs HISTORY_SIZE to be set at 32
449 static struct RLContext test_ctx;
451 static char* test_getc_ptr;
452 static int test_getc(void* data)
454 return *test_getc_ptr++;
457 /** Perform a readline test. The function pipes the characters from \a input_buffer
458 * through the I/O to \c rl_readline(). After the whole string is sent, \c do_test()
459 * checks if the current history within the context match \a expected_history.
461 static bool do_test(char* input_buffer, char* expected_history)
463 rl_init_ctx(&test_ctx);
464 rl_sethook_get(&test_ctx, test_getc, NULL);
466 test_getc_ptr = input_buffer;
467 while (*test_getc_ptr)
468 rl_readline(&test_ctx);
470 if (memcmp(test_ctx.real_history, expected_history, HISTORY_SIZE) != 0)
472 ASSERT2(0, "history compare failed");
481 char* test1_in = "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np\nq\nr\ns\nt\nu\nv\nw\nx\ny\nz\n";
482 char test1_hist[HISTORY_SIZE] = "\0l\0m\0n\0o\0p\0q\0r\0s\0t\0u\0v\0w\0x\0y\0z";
484 if (!do_test(test1_in, test1_hist))
487 kprintf("rl_test successful\n");
490 #endif /* DEBUG_UNIT_TEST */