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