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