00001
00072 #include "readline.h"
00073
00074 #include <cfg/compiler.h>
00075 #include <cfg/debug.h>
00076
00077 #include <stdio.h>
00078 #include <drv/ser.h>
00079
00080
00082 #define DEBUG_UNIT_TEST 0
00083
00085 #define DEBUG_DUMP_HISTORY 0
00086
00087
00089 enum RL_KEYS {
00090 SPECIAL_KEYS = 0x1000,
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101 KEY_UP_ARROW,
00102 KEY_DOWN_ARROW,
00103 KEY_LEFT_ARROW,
00104 KEY_RIGHT_ARROW,
00105 KEY_PAUSE,
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122 KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
00123 KEY_INS, KEY_HOME, KEY_PGUP, KEY_DEL, KEY_END, KEY_PGDN,
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136 KEY_F6, KEY_F7, KEY_F8, KEY_F9,
00137 KEY_F10, KEY_F11, KEY_F12,
00138 };
00139
00143 #define IS_WORD_SEPARATOR(c) ((c) == ' ' || (c) == '\0')
00144
00146 INLINE void rl_puts(const struct RLContext* ctx, const char* txt)
00147 {
00148 if (!ctx->put)
00149 return;
00150
00151 while (*txt)
00152 ctx->put(*txt++, ctx->put_param);
00153 }
00154
00156 INLINE void rl_putc(const struct RLContext* ctx, char ch)
00157 {
00158 if (ctx->put)
00159 ctx->put(ch, ctx->put_param);
00160 }
00161
00166 static bool rl_getc(const struct RLContext* ctx, int* ch)
00167 {
00168 int c = ctx->get(ctx->get_param);
00169
00170 if (c == EOF)
00171 {
00172 if (ctx->clear)
00173 ctx->clear(ctx->clear_param);
00174
00175 return false;
00176 }
00177
00178 if (c == 0x1B)
00179 {
00180
00181
00182 if (ctx->get(ctx->get_param) != 0x5B)
00183 return rl_getc(ctx, ch);
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201 c = ctx->get(ctx->get_param);
00202 switch (c)
00203 {
00204 case 0x41: c = KEY_UP_ARROW; break;
00205 case 0x42: c = KEY_DOWN_ARROW; break;
00206 case 0x43: c = KEY_RIGHT_ARROW; break;
00207 case 0x44: c = KEY_LEFT_ARROW; break;
00208 case 0x50: c = KEY_PAUSE; break;
00209 case 0x5B:
00210 c = ctx->get(ctx->get_param);
00211 switch (c)
00212 {
00213 case 0x41: c = KEY_F1; break;
00214 case 0x42: c = KEY_F2; break;
00215 case 0x43: c = KEY_F3; break;
00216 case 0x44: c = KEY_F4; break;
00217 case 0x45: c = KEY_F5; break;
00218 default: return rl_getc(ctx, ch);
00219 }
00220 break;
00221 default: return rl_getc(ctx, ch);
00222 }
00223 }
00224
00225 *ch = c;
00226 return true;
00227 }
00228
00229 INLINE void beep(struct RLContext* ctx)
00230 {
00231 rl_putc(ctx, '\a');
00232 }
00233
00234 static bool pop_history(struct RLContext* ctx, int total_len)
00235 {
00236
00237 int len = strlen(ctx->real_history+1)+1;
00238
00239
00240 ASSERT(ctx->real_history[0] == '\0');
00241
00242
00243 if (len == total_len)
00244 return false;
00245
00246
00247 memmove(ctx->real_history, ctx->real_history+len, HISTORY_SIZE-len);
00248
00249
00250 ctx->history -= len;
00251
00252 return true;
00253 }
00254
00256 INLINE bool is_history_begin(struct RLContext* ctx, int i)
00257 { return ctx->history + i == ctx->real_history; }
00258
00260 INLINE bool is_history_end(struct RLContext* ctx, int i)
00261 { return ctx->history + i == ctx->real_history + HISTORY_SIZE; }
00262
00264 INLINE bool is_history_past_end(struct RLContext* ctx, int i)
00265 { return ctx->history + i >= ctx->real_history + HISTORY_SIZE; }
00266
00274 static bool insert_chars(struct RLContext* ctx, size_t *curpos, const char* ch, int num_chars)
00275 {
00276 ASSERT(!is_history_past_end(ctx, *curpos));
00277
00278 while (is_history_past_end(ctx, *curpos+num_chars+1))
00279 {
00280 if (!pop_history(ctx, *curpos))
00281 return false;
00282 }
00283
00284 while (num_chars--)
00285 ctx->history[++(*curpos)] = *ch++;
00286
00287 ASSERT(!is_history_past_end(ctx, *curpos + 1));
00288 ctx->history[*curpos+1] = '\0';
00289 return true;
00290 }
00291
00293 static bool insert_char(struct RLContext* ctx, size_t *curpos, char ch)
00294 {
00295 return insert_chars(ctx, curpos, &ch, 1);
00296 }
00297
00298 #if DEBUG_DUMP_HISTORY
00300 static void dump_history(struct RLContext* ctx)
00301 {
00302 int k;
00303 char buf[8];
00304 ASSERT(ctx->real_history[0] == '\0');
00305 rl_puts(ctx, "History dump:");
00306 rl_puts(ctx, "\r\n");
00307 for (k = 1;
00308 ctx->real_history + k != ctx->history + ctx->history_pos + 1;
00309 k += strlen(&ctx->real_history[k]) + 1)
00310 {
00311 rl_puts(ctx, &ctx->real_history[k]);
00312 rl_puts(ctx, "\r\n");
00313 }
00314
00315 sprintf(buf, "%d\r\n", ctx->history_pos + (ctx->history - ctx->real_history));
00316 rl_puts(ctx, buf);
00317 }
00318 #endif
00319
00321 static bool complete_word(struct RLContext *ctx, size_t *curpos)
00322 {
00323 const char* completed_word;
00324 size_t wstart;
00325
00326
00327
00328 wstart = *curpos;
00329 if (IS_WORD_SEPARATOR(ctx->history[wstart]))
00330 {
00331 beep(ctx);
00332 return false;
00333 }
00334
00335
00336 do
00337 --wstart;
00338 while (!IS_WORD_SEPARATOR(ctx->history[wstart]));
00339
00340
00341 completed_word = ctx->match(ctx->match_param, ctx->history + wstart + 1, *curpos - wstart);
00342 if (!completed_word)
00343 return false;
00344
00345
00346 while (*curpos != wstart)
00347 {
00348 rl_putc(ctx, '\b');
00349 --*curpos;
00350 }
00351
00352
00353 insert_chars(ctx, curpos, completed_word, strlen(completed_word));
00354 rl_puts(ctx, completed_word);
00355 insert_char(ctx, curpos, ' ');
00356 rl_putc(ctx, ' ');
00357
00358 return true;
00359 }
00360
00361 void rl_refresh(struct RLContext* ctx)
00362 {
00363 rl_puts(ctx, "\r\n");
00364 if (ctx->prompt)
00365 rl_puts(ctx, ctx->prompt);
00366 rl_puts(ctx, ctx->history + ctx->history_pos + 1);
00367 }
00368
00369 const char* rl_readline(struct RLContext* ctx)
00370 {
00371 size_t i = ctx->history_pos;
00372
00373 if (ctx->prompt)
00374 rl_puts(ctx, ctx->prompt);
00375
00376 insert_chars(ctx, &i, NULL, 0);
00377
00378 while (1)
00379 {
00380 char ch;
00381 int c;
00382
00383 ASSERT(ctx->history - ctx->real_history + ctx->line_pos < HISTORY_SIZE);
00384
00385 if (!rl_getc(ctx, &c))
00386 return NULL;
00387
00388
00389 if (c > SPECIAL_KEYS)
00390 continue;
00391
00392 if (c == '\t')
00393 {
00394
00395 if (!ctx->match)
00396 return false;
00397
00398 complete_word(ctx, &ctx->line_pos);
00399 continue;
00400 }
00401
00402 if (c == '\r' || c == '\n')
00403 {
00404 if (ctx->prompt)
00405 rl_puts(ctx, ctx->prompt);
00406
00407
00408 insert_chars(ctx, &ctx->line_pos, NULL, 0);
00409 rl_puts(ctx, "\r\n");
00410 break;
00411 }
00412
00413
00414
00415 if (c == '\b')
00416 {
00417 if (ctx->history[ctx->line_pos] != '\0')
00418 {
00419 --ctx->line_pos;
00420 rl_puts(ctx, "\b \b");
00421 }
00422 continue;
00423 }
00424
00425
00426 ch = (char)c;
00427 ASSERT2(ch == c, "a special key was not properly handled");
00428 if (insert_chars(ctx, &ctx->line_pos, &ch, 1))
00429 {
00430 rl_putc(ctx, ch);
00431 }
00432 else
00433 {
00434 beep(ctx);
00435 }
00436 }
00437
00438 ctx->history_pos = ctx->line_pos + 1;
00439 while (ctx->history[ctx->line_pos] != '\0')
00440 --ctx->line_pos;
00441
00442
00443 if (ctx->line_pos == ctx->history_pos - 1)
00444 ctx->history_pos -= 1;
00445
00446 #if DEBUG_DUMP_HISTORY
00447 dump_history(ctx);
00448 #endif
00449
00450
00451
00452 return &ctx->history[ctx->line_pos + 1];
00453 }
00454
00455
00456 #if DEBUG_UNIT_TEST
00457
00459 void rl_test(void);
00460
00461 #if HISTORY_SIZE != 32
00462 #error This test needs HISTORY_SIZE to be set at 32
00463 #endif
00464
00465 static struct RLContext test_ctx;
00466
00467 static char* test_getc_ptr;
00468 static int test_getc(void* data)
00469 {
00470 return *test_getc_ptr++;
00471 }
00472
00477 static bool do_test(char* input_buffer, char* expected_history)
00478 {
00479 rl_init_ctx(&test_ctx);
00480 rl_sethook_get(&test_ctx, test_getc, NULL);
00481
00482 test_getc_ptr = input_buffer;
00483 while (*test_getc_ptr)
00484 rl_readline(&test_ctx);
00485
00486 if (memcmp(test_ctx.real_history, expected_history, HISTORY_SIZE) != 0)
00487 {
00488 ASSERT2(0, "history compare failed");
00489 return false;
00490 }
00491
00492 return true;
00493 }
00494
00495 void rl_test(void)
00496 {
00497 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";
00498 char test1_hist[HISTORY_SIZE] = "\0l\0m\0n\0o\0p\0q\0r\0s\0t\0u\0v\0w\0x\0y\0z";
00499
00500 if (!do_test(test1_in, test1_hist))
00501 return;
00502
00503 kprintf("rl_test successful\n");
00504 }
00505
00506 #endif
00507