Added regedit unit test, a couple minor changes to regedit.
[wine] / win32 / editline.c
1 /*
2  * line edition function for Win32 console
3  *
4  * Copyright 2001 Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wincon.h"
29 #include "wine/unicode.h"
30 #include "winnls.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(console);
34
35 /* console.c */
36 extern int CONSOLE_GetHistory(int idx, WCHAR* buf, int buf_len);
37 extern BOOL CONSOLE_AppendHistory(const WCHAR *p);
38 extern unsigned int CONSOLE_GetNumHistoryEntries(void);
39
40 struct WCEL_Context;
41
42 typedef struct
43 {
44     WCHAR                       val;            /* vk or unicode char */
45     void                        (*func)(struct WCEL_Context* ctx);
46 } KeyEntry;
47
48 typedef struct
49 {
50     DWORD                       keyState;       /* keyState (from INPUT_RECORD) to match */
51     BOOL                        chkChar;        /* check vk or char */
52     KeyEntry*                   entries;        /* array of entries */
53 } KeyMap;
54
55 typedef struct WCEL_Context {
56     WCHAR*                      line;           /* the line being edited */
57     size_t                      alloc;          /* number of WCHAR in line */
58     unsigned                    len;            /* number of chars in line */
59     unsigned                    ofs;            /* offset for cursor in current line */
60     WCHAR*                      yanked;         /* yanked line */
61     unsigned                    mark;           /* marked point (emacs mode only) */
62     CONSOLE_SCREEN_BUFFER_INFO  csbi;           /* current state (initial cursor, window size, attribute) */
63     HANDLE                      hConIn;
64     HANDLE                      hConOut;
65     unsigned                    done : 1,       /* to 1 when we're done with editing */
66                                 error : 1;      /* to 1 when an error occurred in the editing */
67     unsigned                    histSize;
68     unsigned                    histPos;
69     WCHAR*                      histCurr;
70 } WCEL_Context;
71
72 #if 0
73 static void WCEL_Dump(WCEL_Context* ctx, const char* pfx)
74 {
75     MESSAGE("%s: [line=%s[alloc=%u] ofs=%u len=%u start=(%d,%d) mask=%c%c\n"
76             "\t\thist=(size=%u pos=%u curr=%s)\n",
77             pfx, debugstr_w(ctx->line), ctx->alloc, ctx->ofs, ctx->len,
78             ctx->csbi.dwCursorPosition.X, ctx->csbi.dwCursorPosition.Y,
79             ctx->done ? 'D' : 'd', ctx->error ? 'E' : 'e',
80             ctx->histSize, ctx->histPos, debugstr_w(ctx->histCurr));
81 }
82 #endif
83
84 /* ====================================================================
85  *
86  * Console helper functions
87  *
88  * ====================================================================*/
89
90 static BOOL WCEL_Get(WCEL_Context* ctx, INPUT_RECORD* ir)
91 {
92     DWORD               retv;
93
94     for (;;)
95     {
96         /* data available ? */
97         if (ReadConsoleInputW(ctx->hConIn, ir, 1, &retv) && retv == 1)
98             return TRUE;
99         /* then wait... */
100         switch (WaitForSingleObject(ctx->hConIn, INFINITE))
101         {
102         case WAIT_OBJECT_0:
103             break;
104         default:
105             /* we have checked that hConIn was a console handle (could be sb) */
106             ERR("Shouldn't happen\n");
107             /* fall thru */
108         case WAIT_ABANDONED:
109         case WAIT_TIMEOUT:
110             ctx->error = 1;
111             ERR("hmm bad situation\n");
112             return FALSE;
113         }
114     }
115 }
116
117 static inline void WCEL_Beep(WCEL_Context* ctx)
118 {
119     Beep(400, 300);
120 }
121
122 static inline COORD WCEL_GetCoord(WCEL_Context* ctx, int ofs)
123 {
124     COORD c;
125     c.X = ctx->csbi.dwCursorPosition.X + ofs;
126     c.Y = ctx->csbi.dwCursorPosition.Y;
127     return c;
128 }
129
130 static inline void WCEL_GetRect(WCEL_Context* ctx, LPSMALL_RECT sr, int beg, int end)
131 {
132     sr->Left = ctx->csbi.dwCursorPosition.X + beg;
133     sr->Top = ctx->csbi.dwCursorPosition.Y;
134     sr->Right = ctx->csbi.dwCursorPosition.X + end;
135     sr->Bottom = ctx->csbi.dwCursorPosition.Y;
136 }
137
138 /* ====================================================================
139  *
140  * context manipulation functions
141  *
142  * ====================================================================*/
143
144 static BOOL WCEL_Grow(WCEL_Context* ctx, size_t len)
145 {
146     if (ctx->csbi.dwCursorPosition.X + ctx->len + len >= ctx->csbi.dwSize.X)
147     {
148         FIXME("Current implementation doesn't allow edition to spray across several lines\n");
149         return FALSE;
150     }
151
152     if (ctx->len + len >= ctx->alloc)
153     {
154         WCHAR*  newline;
155         newline = HeapReAlloc(GetProcessHeap(), 0, ctx->line, sizeof(WCHAR) * (ctx->alloc + 32));
156         if (!newline) return FALSE;
157         ctx->line = newline;
158         ctx->alloc += 32;
159     }
160     return TRUE;
161 }
162
163 static void WCEL_DeleteString(WCEL_Context* ctx, int beg, int end)
164 {
165     SMALL_RECT  scl, clp;
166     CHAR_INFO   ci;
167
168     if (end < ctx->len)
169         memmove(&ctx->line[beg], &ctx->line[end], (ctx->len - end) * sizeof(WCHAR));
170     /* make the source rect bigger than the actual rect to that the part outside the clip
171      * rect (before the scroll) will get redrawn after the scroll
172      */
173     WCEL_GetRect(ctx, &scl, end, ctx->len + end - beg);
174     WCEL_GetRect(ctx, &clp, beg, ctx->len);
175
176     ci.Char.UnicodeChar = ' ';
177     ci.Attributes = ctx->csbi.wAttributes;
178     ScrollConsoleScreenBufferW(ctx->hConOut, &scl, &clp, WCEL_GetCoord(ctx, beg), &ci);
179
180     ctx->len -= end - beg;
181     ctx->line[ctx->len] = 0;
182 }
183
184 static void WCEL_InsertString(WCEL_Context* ctx, const WCHAR* str)
185 {
186     size_t      len = lstrlenW(str);
187
188     if (!len || !WCEL_Grow(ctx, len)) return;
189     if (ctx->len > ctx->ofs)
190         memmove(&ctx->line[ctx->ofs + len], &ctx->line[ctx->ofs], (ctx->len - ctx->ofs) * sizeof(WCHAR));
191     memcpy(&ctx->line[ctx->ofs], str, len * sizeof(WCHAR));
192     ctx->len += len;
193     ctx->line[ctx->len] = 0;
194     SetConsoleCursorPosition(ctx->hConOut, WCEL_GetCoord(ctx, ctx->ofs));
195     WriteConsoleW(ctx->hConOut, &ctx->line[ctx->ofs], ctx->len - ctx->ofs, NULL, NULL);
196     ctx->ofs += len;
197 }
198
199 static void WCEL_InsertChar(WCEL_Context* ctx, WCHAR c)
200 {
201     WCHAR       buffer[2];
202
203     /* do not insert 0..31 control characters */
204     if (c < ' ' && c != '\t') return;
205
206     buffer[0] = c;
207     buffer[1] = 0;
208     WCEL_InsertString(ctx, buffer);
209 }
210
211 static void WCEL_SaveYank(WCEL_Context* ctx, int beg, int end)
212 {
213     int len = end - beg;
214     ctx->yanked = HeapReAlloc(GetProcessHeap(), 0, ctx->yanked, (len + 1) * sizeof(WCHAR));
215     if (!ctx->yanked) return;
216     memcpy(ctx->yanked, &ctx->line[beg], len * sizeof(WCHAR));
217     ctx->yanked[len] = 0;
218 }
219
220 /* FIXME NTDLL doesn't export iswalnum, and I don't want to link in msvcrt when most
221  * of the data lay in unicode lib
222  */
223 static inline BOOL WCEL_iswalnum(WCHAR wc)
224 {
225     return get_char_typeW(wc) & (C1_ALPHA|C1_DIGIT|C1_LOWER|C1_UPPER);
226 }
227
228 static int WCEL_GetLeftWordTransition(WCEL_Context* ctx, int ofs)
229 {
230     ofs--;
231     while (ofs >= 0 && !WCEL_iswalnum(ctx->line[ofs])) ofs--;
232     while (ofs >= 0 && WCEL_iswalnum(ctx->line[ofs])) ofs--;
233     if (ofs >= 0) ofs++;
234     return max(ofs, 0);
235 }
236
237 static int WCEL_GetRightWordTransition(WCEL_Context* ctx, int ofs)
238 {
239     ofs++;
240     while (ofs <= ctx->len && WCEL_iswalnum(ctx->line[ofs])) ofs++;
241     while (ofs <= ctx->len && !WCEL_iswalnum(ctx->line[ofs])) ofs++;
242     return min(ofs, ctx->len);
243 }
244
245 static WCHAR* WCEL_GetHistory(WCEL_Context* ctx, int idx)
246 {
247     WCHAR*      ptr;
248
249     if (idx == ctx->histSize - 1)
250     {
251         ptr = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(ctx->histCurr) + 1) * sizeof(WCHAR));
252         lstrcpyW(ptr, ctx->histCurr);
253     }
254     else
255     {
256         int     len = CONSOLE_GetHistory(idx, NULL, 0);
257
258         if ((ptr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
259         {
260             CONSOLE_GetHistory(idx, ptr, len);
261         }
262     }
263     return ptr;
264 }
265
266 static void     WCEL_HistoryInit(WCEL_Context* ctx)
267 {
268     ctx->histPos = CONSOLE_GetNumHistoryEntries();
269     ctx->histSize = ctx->histPos + 1;
270     ctx->histCurr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR));
271 }
272
273 static void    WCEL_MoveToHist(WCEL_Context* ctx, int idx)
274 {
275     WCHAR*      data = WCEL_GetHistory(ctx, idx);
276     int         len = lstrlenW(data) + 1;
277
278     /* save current line edition for recall when needed (FIXME seems broken to me) */
279     if (ctx->histPos == ctx->histSize - 1)
280     {
281         if (ctx->histCurr) HeapFree(GetProcessHeap(), 0, ctx->histCurr);
282         ctx->histCurr = HeapAlloc(GetProcessHeap(), 0, (ctx->len + 1) * sizeof(WCHAR));
283         memcpy(ctx->histCurr, ctx->line, (ctx->len + 1) * sizeof(WCHAR));
284     }
285     /* need to clean also the screen if new string is shorter than old one */
286     WCEL_DeleteString(ctx, 0, ctx->len);
287     ctx->ofs = 0;
288     /* insert new string */
289     if (WCEL_Grow(ctx, len))
290     {
291         WCEL_InsertString(ctx, data);
292         HeapFree(GetProcessHeap(), 0, data);
293         ctx->histPos = idx;
294     }
295 }
296
297 static void    WCEL_FindPrevInHist(WCEL_Context* ctx)
298 {
299     int startPos = ctx->histPos;
300     WCHAR*      data;
301     int         len, oldofs;
302
303     if (ctx->histPos && ctx->histPos == ctx->histSize) {
304         startPos--;
305         ctx->histPos--;
306     }
307
308     do {
309        data = WCEL_GetHistory(ctx, ctx->histPos);
310
311        if (ctx->histPos) ctx->histPos--;
312        else ctx->histPos = (ctx->histSize-1);
313
314        len = lstrlenW(data) + 1;
315        if ((len >= ctx->ofs) &&
316            (memcmp(ctx->line, data, ctx->ofs * sizeof(WCHAR)) == 0)) {
317
318            /* need to clean also the screen if new string is shorter than old one */
319            WCEL_DeleteString(ctx, 0, ctx->len);
320
321            if (WCEL_Grow(ctx, len))
322            {
323               oldofs = ctx->ofs;
324               ctx->ofs = 0;
325               WCEL_InsertString(ctx, data);
326               ctx->ofs = oldofs;
327               SetConsoleCursorPosition(ctx->hConOut, WCEL_GetCoord(ctx, ctx->ofs));
328               HeapFree(GetProcessHeap(), 0, data);
329               return;
330            }
331        }
332     } while (ctx->histPos != startPos);
333
334     return;
335 }
336
337 /* ====================================================================
338  *
339  * basic edition functions
340  *
341  * ====================================================================*/
342
343 static void WCEL_Done(WCEL_Context* ctx)
344 {
345     if (!WCEL_Grow(ctx, 1)) return;
346     ctx->line[ctx->len++] = '\n';
347     ctx->line[ctx->len] = 0;
348     WriteConsoleA(ctx->hConOut, "\n", 1, NULL, NULL);
349     ctx->done = 1;
350 }
351
352 static void WCEL_MoveLeft(WCEL_Context* ctx)
353 {
354     if (ctx->ofs > 0) ctx->ofs--;
355 }
356
357 static void WCEL_MoveRight(WCEL_Context* ctx)
358 {
359     if (ctx->ofs < ctx->len) ctx->ofs++;
360 }
361
362 static void WCEL_MoveToLeftWord(WCEL_Context* ctx)
363 {
364     int new_ofs = WCEL_GetLeftWordTransition(ctx, ctx->ofs);
365     if (new_ofs != ctx->ofs) ctx->ofs = new_ofs;
366 }
367
368 static void WCEL_MoveToRightWord(WCEL_Context* ctx)
369 {
370     int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs);
371     if (new_ofs != ctx->ofs) ctx->ofs = new_ofs;
372 }
373
374 static void WCEL_MoveToBeg(WCEL_Context* ctx)
375 {
376     ctx->ofs = 0;
377 }
378
379 static void WCEL_MoveToEnd(WCEL_Context* ctx)
380 {
381     ctx->ofs = ctx->len;
382 }
383
384 static void WCEL_SetMark(WCEL_Context* ctx)
385 {
386     ctx->mark = ctx->ofs;
387 }
388
389 static void WCEL_ExchangeMark(WCEL_Context* ctx)
390 {
391     unsigned tmp;
392
393     if (ctx->mark > ctx->len) return;
394     tmp = ctx->ofs;
395     ctx->ofs = ctx->mark;
396     ctx->mark = tmp;
397 }
398
399 static void WCEL_CopyMarkedZone(WCEL_Context* ctx)
400 {
401     unsigned beg, end;
402
403     if (ctx->mark > ctx->len || ctx->mark == ctx->ofs) return;
404     if (ctx->mark > ctx->ofs)
405     {
406         beg = ctx->ofs;         end = ctx->mark;
407     }
408     else
409     {
410         beg = ctx->mark;        end = ctx->ofs;
411     }
412     WCEL_SaveYank(ctx, beg, end);
413 }
414
415 static void WCEL_TransposeChar(WCEL_Context* ctx)
416 {
417     WCHAR       c;
418
419     if (!ctx->ofs || ctx->ofs == ctx->len) return;
420
421     c = ctx->line[ctx->ofs];
422     ctx->line[ctx->ofs] = ctx->line[ctx->ofs - 1];
423     ctx->line[ctx->ofs - 1] = c;
424
425     WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs - 1], 2, WCEL_GetCoord(ctx, ctx->ofs - 1), NULL);
426     ctx->ofs++;
427 }
428
429 static void WCEL_TransposeWords(WCEL_Context* ctx)
430 {
431     FIXME("NIY\n");
432 }
433
434 static void WCEL_LowerCaseWord(WCEL_Context* ctx)
435 {
436     int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs);
437     if (new_ofs != ctx->ofs)
438     {
439         int     i;
440         for (i = ctx->ofs; i <= new_ofs; i++)
441             ctx->line[i] = tolowerW(ctx->line[i]);
442         WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1,
443                                      WCEL_GetCoord(ctx, ctx->ofs), NULL);
444         ctx->ofs = new_ofs;
445     }
446 }
447
448 static void WCEL_UpperCaseWord(WCEL_Context* ctx)
449 {
450     int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs);
451     if (new_ofs != ctx->ofs)
452     {
453         int     i;
454         for (i = ctx->ofs; i <= new_ofs; i++)
455             ctx->line[i] = toupperW(ctx->line[i]);
456         WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1,
457                                      WCEL_GetCoord(ctx, ctx->ofs), NULL);
458         ctx->ofs = new_ofs;
459     }
460 }
461
462 static void WCEL_CapitalizeWord(WCEL_Context* ctx)
463 {
464     int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs);
465     if (new_ofs != ctx->ofs)
466     {
467         int     i;
468
469         ctx->line[ctx->ofs] = toupperW(ctx->line[ctx->ofs]);
470         for (i = ctx->ofs + 1; i <= new_ofs; i++)
471             ctx->line[i] = tolowerW(ctx->line[i]);
472         WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1,
473                                      WCEL_GetCoord(ctx, ctx->ofs), NULL);
474         ctx->ofs = new_ofs;
475     }
476 }
477
478 static void WCEL_Yank(WCEL_Context* ctx)
479 {
480     WCEL_InsertString(ctx, ctx->yanked);
481     HeapFree(GetProcessHeap(), 0, ctx->yanked);
482     ctx->yanked = NULL;
483 }
484
485 static void WCEL_KillToEndOfLine(WCEL_Context* ctx)
486 {
487     WCEL_SaveYank(ctx, ctx->ofs, ctx->len);
488     WCEL_DeleteString(ctx, ctx->ofs, ctx->len);
489 }
490
491 static void WCEL_KillMarkedZone(WCEL_Context* ctx)
492 {
493     unsigned beg, end;
494
495     if (ctx->mark > ctx->len || ctx->mark == ctx->ofs) return;
496     if (ctx->mark > ctx->ofs)
497     {
498         beg = ctx->ofs;         end = ctx->mark;
499     }
500     else
501     {
502         beg = ctx->mark;        end = ctx->ofs;
503     }
504     WCEL_SaveYank(ctx, beg, end);
505     WCEL_DeleteString(ctx, beg, end);
506     ctx->ofs = beg;
507 }
508
509 static void WCEL_DeletePrevChar(WCEL_Context* ctx)
510 {
511     if (ctx->ofs)
512     {
513         WCEL_DeleteString(ctx, ctx->ofs - 1, ctx->ofs);
514         ctx->ofs--;
515     }
516 }
517
518 static void WCEL_DeleteCurrChar(WCEL_Context* ctx)
519 {
520     if (ctx->ofs < ctx->len)
521         WCEL_DeleteString(ctx, ctx->ofs, ctx->ofs + 1);
522 }
523
524 static void WCEL_DeleteLeftWord(WCEL_Context* ctx)
525 {
526     int new_ofs = WCEL_GetLeftWordTransition(ctx, ctx->ofs);
527     if (new_ofs != ctx->ofs)
528     {
529         WCEL_DeleteString(ctx, new_ofs, ctx->ofs);
530         ctx->ofs = new_ofs;
531     }
532 }
533
534 static void WCEL_DeleteRightWord(WCEL_Context* ctx)
535 {
536     int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs);
537     if (new_ofs != ctx->ofs)
538     {
539         WCEL_DeleteString(ctx, ctx->ofs, new_ofs);
540     }
541 }
542
543 static void WCEL_MoveToPrevHist(WCEL_Context* ctx)
544 {
545     if (ctx->histPos) WCEL_MoveToHist(ctx, ctx->histPos - 1);
546 }
547
548 static void WCEL_MoveToNextHist(WCEL_Context* ctx)
549 {
550     if (ctx->histPos < ctx->histSize - 1) WCEL_MoveToHist(ctx, ctx->histPos + 1);
551 }
552
553 static void WCEL_MoveToFirstHist(WCEL_Context* ctx)
554 {
555     if (ctx->histPos != 0) WCEL_MoveToHist(ctx, 0);
556 }
557
558 static void WCEL_MoveToLastHist(WCEL_Context* ctx)
559 {
560     if (ctx->histPos != ctx->histSize - 1) WCEL_MoveToHist(ctx, ctx->histSize - 1);
561 }
562
563 /* ====================================================================
564  *
565  *              Key Maps
566  *
567  * ====================================================================*/
568
569 #define CTRL(x) ((x) - '@')
570 static KeyEntry StdKeyMap[] =
571 {
572     {/*BACK*/0x08,      WCEL_DeletePrevChar     },
573     {/*RETURN*/0x0d,    WCEL_Done               },
574     {/*DEL*/127,        WCEL_DeleteCurrChar     },
575     {   0,              NULL                    }
576 };
577
578 static KeyEntry Win32ExtraStdKeyMap[] =
579 {
580     {/*VK_F8*/   0x77,  WCEL_FindPrevInHist     },
581     {   0,              NULL                    }
582 };
583
584
585 static  KeyEntry EmacsKeyMapCtrl[] =
586 {
587     {   CTRL('@'),      WCEL_SetMark            },
588     {   CTRL('A'),      WCEL_MoveToBeg          },
589     {   CTRL('B'),      WCEL_MoveLeft           },
590     /* C */
591     {   CTRL('D'),      WCEL_DeleteCurrChar     },
592     {   CTRL('E'),      WCEL_MoveToEnd          },
593     {   CTRL('F'),      WCEL_MoveRight          },
594     {   CTRL('G'),      WCEL_Beep               },
595     {   CTRL('H'),      WCEL_DeletePrevChar     },
596     /* I: meaningless (or tab ???) */
597     {   CTRL('J'),      WCEL_Done               },
598     {   CTRL('K'),      WCEL_KillToEndOfLine    },
599     /* L: [NIY] redraw the whole stuff */
600     {   CTRL('M'),      WCEL_Done               },
601     {   CTRL('N'),      WCEL_MoveToNextHist     },
602     /* O; insert line... meaningless */
603     {   CTRL('P'),      WCEL_MoveToPrevHist     },
604     /* Q: [NIY] quoting... */
605     /* R: [NIY] search backwards... */
606     /* S: [NIY] search forwards... */
607     {   CTRL('T'),      WCEL_TransposeChar      },
608     /* U: [NIY] set repeat count... */
609     /* V: paragraph down... meaningless */
610     {   CTRL('W'),      WCEL_KillMarkedZone     },
611     {   CTRL('X'),      WCEL_ExchangeMark       },
612     {   CTRL('Y'),      WCEL_Yank               },
613     /* Z: meaningless */
614     {   0,              NULL                    }
615 };
616
617 static KeyEntry EmacsKeyMapAlt[] =
618 {
619     {/*DEL*/127,        WCEL_DeleteLeftWord     },
620     {   '<',            WCEL_MoveToFirstHist    },
621     {   '>',            WCEL_MoveToLastHist     },
622     {   '?',            WCEL_Beep               },
623     {   'b',            WCEL_MoveToLeftWord     },
624     {   'c',            WCEL_CapitalizeWord     },
625     {   'd',            WCEL_DeleteRightWord    },
626     {   'f',            WCEL_MoveToRightWord    },
627     {   'l',            WCEL_LowerCaseWord      },
628     {   't',            WCEL_TransposeWords     },
629     {   'u',            WCEL_UpperCaseWord      },
630     {   'w',            WCEL_CopyMarkedZone     },
631     {   0,              NULL                    }
632 };
633
634 static KeyEntry EmacsKeyMapExtended[] =
635 {
636     {/*RETURN*/  0x0d,  WCEL_Done },
637     {/*VK_PRIOR*/0x21,  WCEL_MoveToPrevHist     },
638     {/*VK_NEXT*/ 0x22,  WCEL_MoveToNextHist     },
639     {/*VK_END*/  0x23,  WCEL_MoveToEnd          },
640     {/*VK_HOME*/ 0x24,  WCEL_MoveToBeg          },
641     {/*VK_RIGHT*/0x27,  WCEL_MoveRight          },
642     {/*VK_LEFT*/ 0x25,  WCEL_MoveLeft           },
643     {/*VK_DEL*/  0x2e,  WCEL_DeleteCurrChar     },
644     {   0,              NULL                    }
645 };
646
647 static KeyMap   EmacsKeyMap[] =
648 {
649     {0x00000000, 1, StdKeyMap},
650     {0x00000001, 1, EmacsKeyMapAlt},    /* left  alt  */
651     {0x00000002, 1, EmacsKeyMapAlt},    /* right alt  */
652     {0x00000004, 1, EmacsKeyMapCtrl},   /* left  ctrl */
653     {0x00000008, 1, EmacsKeyMapCtrl},   /* right ctrl */
654     {0x00000100, 0, EmacsKeyMapExtended},
655     {0,          0, 0}
656 };
657
658 static  KeyEntry Win32KeyMapExtended[] =
659 {
660     {/*VK_LEFT*/ 0x25,  WCEL_MoveLeft           },
661     {/*VK_RIGHT*/0x27,  WCEL_MoveRight          },
662     {/*VK_HOME*/ 0x24,  WCEL_MoveToBeg          },
663     {/*VK_END*/  0x23,  WCEL_MoveToEnd          },
664     {/*VK_UP*/   0x26,  WCEL_MoveToPrevHist     },
665     {/*VK_DOWN*/ 0x28,  WCEL_MoveToNextHist     },
666     {/*VK_DEL*/  0x2e,  WCEL_DeleteCurrChar     },
667     {   0,              NULL                    }
668 };
669
670 static  KeyEntry Win32KeyMapCtrlExtended[] =
671 {
672     {/*VK_LEFT*/ 0x25,  WCEL_MoveToLeftWord     },
673     {/*VK_RIGHT*/0x27,  WCEL_MoveToRightWord    },
674     {/*VK_END*/  0x23,  WCEL_KillToEndOfLine    },
675     {   0,              NULL                    }
676 };
677
678 KeyMap  Win32KeyMap[] =
679 {
680     {0x00000000, 1, StdKeyMap},
681     {0x00000000, 0, Win32ExtraStdKeyMap},
682     {0x00000100, 0, Win32KeyMapExtended},
683     {0x00000104, 0, Win32KeyMapCtrlExtended},
684     {0x00000108, 0, Win32KeyMapCtrlExtended},
685     {0,          0, 0}
686 };
687 #undef CTRL
688
689 /* ====================================================================
690  *
691  *              Read line master function
692  *
693  * ====================================================================*/
694
695 WCHAR* CONSOLE_Readline(HANDLE hConsoleIn, int use_emacs)
696 {
697     WCEL_Context        ctx;
698     INPUT_RECORD        ir;
699     KeyMap*             km;
700     KeyEntry*           ke;
701     unsigned            ofs;
702     void                (*func)(struct WCEL_Context* ctx);
703     DWORD               ks;
704
705     memset(&ctx, 0, sizeof(ctx));
706     ctx.hConIn = hConsoleIn;
707     WCEL_HistoryInit(&ctx);
708     if ((ctx.hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL,
709                                     OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE ||
710         !GetConsoleScreenBufferInfo(ctx.hConOut, &ctx.csbi))
711         return NULL;
712     if (!WCEL_Grow(&ctx, 1))
713     {
714         CloseHandle(ctx.hConOut);
715         return NULL;
716     }
717     ctx.line[0] = 0;
718
719 /* EPP     WCEL_Dump(&ctx, "init"); */
720
721     while (!ctx.done && !ctx.error && WCEL_Get(&ctx, &ir))
722     {
723         if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown) continue;
724         TRACE("key%s repeatCount=%u, keyCode=%02x scanCode=%02x char=%02x keyState=%08lx\n",
725               ir.Event.KeyEvent.bKeyDown ? "Down" : "Up  ", ir.Event.KeyEvent.wRepeatCount,
726               ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode,
727               ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState);
728
729 /* EPP  WCEL_Dump(&ctx, "before func"); */
730         ofs = ctx.ofs;
731         /* mask out some bits which don't interest us */
732         ks = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON);
733
734         func = NULL;
735         for (km = (use_emacs) ? EmacsKeyMap : Win32KeyMap; km->entries != NULL; km++)
736         {
737             if (km->keyState != ks)
738                 continue;
739             if (km->chkChar)
740             {
741                 for (ke = &km->entries[0]; ke->func != 0; ke++)
742                     if (ke->val == ir.Event.KeyEvent.uChar.UnicodeChar) break;
743             }
744             else
745             {
746                 for (ke = &km->entries[0]; ke->func != 0; ke++)
747                     if (ke->val == ir.Event.KeyEvent.wVirtualKeyCode) break;
748
749             }
750             if (ke->func)
751             {
752                 func = ke->func;
753                 break;
754             }
755         }
756
757         if (func)
758             (func)(&ctx);
759         else if (!(ir.Event.KeyEvent.dwControlKeyState & (ENHANCED_KEY|LEFT_ALT_PRESSED)))
760             WCEL_InsertChar(&ctx, ir.Event.KeyEvent.uChar.UnicodeChar);
761         else TRACE("Dropped event\n");
762
763 /* EPP  WCEL_Dump(&ctx, "after func"); */
764         if (ctx.ofs != ofs)
765             SetConsoleCursorPosition(ctx.hConOut, WCEL_GetCoord(&ctx, ctx.ofs));
766     }
767     if (ctx.error)
768     {
769         HeapFree(GetProcessHeap(), 0, ctx.line);
770         ctx.line = NULL;
771     }
772     if (ctx.line)
773         CONSOLE_AppendHistory(ctx.line);
774
775     CloseHandle(ctx.hConOut);
776     if (ctx.histCurr) HeapFree(GetProcessHeap(), 0, ctx.histCurr);
777     return ctx.line;
778 }
779