7336031450e1c0ac4680621ed7c378709f73e9e7
[scilab.git] / scilab / modules / console / src / c / cmdLine / getKey.c
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2011 - DIGITEO - Karim Mamode
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  */
11
12 #include <wchar.h>
13 #include <string.h>
14 #include <wctype.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <curses.h>
19 #include <termios.h>
20 #include <term.h>
21 #include <string.h>
22 #include "cliHistory.h"
23 #include "termcapManagement.h"
24 #include "gotoFunctions.h"
25 #include "charactersManagement.h"
26 #include "initConsoleMode.h"
27 #include "cliPrompt.h"
28 #include "getKey.h"
29 #include "sci_malloc.h"
30 #include "HistoryManager.h"
31 #include "charEncoding.h"
32 #include "cliDisplayManagement.h"
33 #include "autoCompletionCli.h"
34 #include "tohome.h"
35 #include "localization.h"
36
37 /* Set new token in order to get string changement in history */
38 static void updateTokenInScilabHistory(wchar_t ** commandLine)
39 {
40     char *multiByteString = NULL;
41
42     multiByteString = wide_string_to_UTF8(*commandLine);
43     setSearchedTokenInScilabHistory(multiByteString);
44     FREE(multiByteString);
45 }
46
47 /*
48  * If last key was '1'
49  * it means this could be arrow key plus control key
50  */
51 static void caseCtrlAndArrowKey(wchar_t * commandLine, unsigned int *cursorLocation)
52 {
53     if (getwchar() == L';' && getwchar() == L'5')
54     {
55         switch (getwchar())
56         {
57             case L'C':
58                 nextWord(commandLine, cursorLocation);
59                 break;
60             case L'D':
61                 previousWord(commandLine, cursorLocation);
62                 break;
63         }
64     }
65     else
66     {
67         /*
68          * In case the condition returned false, it may be SHIFT or Alt and an arrow key
69          * So, the last character is taken to avoid an unintentional print of any other characters.
70          */
71         getwchar();
72     }
73 }
74
75 static void caseHomeOrEndKey(wchar_t * commandLine, unsigned int *cursorLocation)
76 {
77     switch (getwchar())
78     {
79         case L'H':
80             begLine(commandLine, cursorLocation);
81             break;
82         case L'F':
83             endLine(commandLine, cursorLocation);
84             break;
85     }
86 }
87
88 /*
89  * If second key was L'['
90  * It means this could be an arrow key, a delete key or home/end key.
91  */
92 static void caseDelOrArrowKey(wchar_t ** commandLine, unsigned int *cursorLocation)
93 {
94     int * cmd = *commandLine;
95     switch (getwchar())
96     {
97         case L'A':
98             previousCmd(commandLine, cursorLocation);
99             break;
100         case L'B':
101             nextCmd(commandLine, cursorLocation);
102             break;
103         case L'C':
104             gotoRight(*commandLine, cursorLocation);
105             break;
106         case L'D':
107             gotoLeft(*commandLine, cursorLocation);
108             break;
109         case L'1':
110             caseCtrlAndArrowKey(*commandLine, cursorLocation);
111             break;
112         case L'3':
113             if (getwchar() == L'~')
114             {
115                 rmChar(*commandLine, SCI_DELETE, cursorLocation);
116                 updateTokenInScilabHistory(commandLine);
117                 break;
118             }
119         //home or end key in some consoles
120         case L'H':
121             begLine(cmd, cursorLocation);
122             break;
123         case L'F':
124             endLine(cmd, cursorLocation);
125             break;
126     }
127 }
128
129 /*
130  * If last key was Meta...
131  */
132 static void caseMetaKey(wchar_t ** commandLine, unsigned int *cursorLocation)
133 {
134     switch (getwchar())
135     {
136         case L'f':
137         case L'F':
138             nextWord(*commandLine, cursorLocation);
139             break;
140         case L'b':
141         case L'B':
142             previousWord(*commandLine, cursorLocation);
143             break;
144         case L'[':
145             caseDelOrArrowKey(commandLine, cursorLocation);
146             break;
147         case L'O':
148             caseHomeOrEndKey(*commandLine, cursorLocation);
149             break;
150
151     }
152 }
153
154 static void setCBreak(bool cbk)
155 {
156     struct termios t;
157
158     tcgetattr(0, &t);
159     if (cbk)
160     {
161         t.c_cc[VMIN] = 1;       /* Wait 1 character before leaving getwchar */
162         t.c_cc[VTIME] = 0;      /* Do not wait any second before leaving getwchar */
163     }
164     else
165     {
166         t.c_cc[VMIN] = 0;
167         t.c_cc[VTIME] = 0;      /* Do not wait any second before leaving getwchar */
168     }
169     tcsetattr(0, 0, &t);
170 }
171
172 static void endCopyPast(wchar_t * commandLine)
173 {
174     int sizeOfCmd = 0;
175
176     sizeOfCmd = wcslen(commandLine);
177
178     if (sizeOfCmd == 0)
179     {
180         setTokenInteruptExecution(SEND_COMMAND);
181     }
182     else
183     {
184         if (commandLine[sizeOfCmd - 1] == L'\n')
185         {
186             setTokenInteruptExecution(SEND_COMMAND);
187         }
188         else
189         {
190             setTokenInteruptExecution(CONTINUE_COMMAND);
191         }
192     }
193 }
194
195 /* Reset command line if CTRL-C is pressed */
196 static void resetCommandLine(wchar_t ** commandLine, unsigned int *cursorLocation)
197 {
198     char *multiByteString = NULL;
199
200     /* Send the preivous edited line in the history */
201     multiByteString = wide_string_to_UTF8(*commandLine);
202     appendLineToScilabHistory(multiByteString);
203     FREE(multiByteString);
204     setSearchedTokenInScilabHistory(NULL);
205     FREE(*commandLine);
206     /* Reset command line and cursor position */
207     *cursorLocation = 0;
208     *commandLine = MALLOC(1024 * sizeof(**commandLine));
209     **commandLine = L'\0';
210     setTokenInteruptExecution(RESET_TOKEN);
211 }
212
213 /*
214  * Read keyboard a first time.
215  */
216 static void getKey(wchar_t ** commandLine, unsigned int *cursorLocation)
217 {
218     int key;
219
220     key = getwchar();
221
222         // Need to clear the stdin
223     if (key == WEOF && feof(stdin))
224     {
225         clearerr(stdin);
226     }
227
228     if (getTokenInteruptExecution() == DO_NOT_SEND_COMMAND)
229     {
230         resetCommandLine(commandLine, cursorLocation);
231     }
232
233     switch (key)
234     {
235         case CTRL_A:
236             begLine(*commandLine, cursorLocation);
237             break;
238         case CTRL_B:
239             gotoLeft(*commandLine, cursorLocation);
240             break;
241         case CTRL_D:
242             rmChar(*commandLine, SCI_DELETE, cursorLocation);
243             updateTokenInScilabHistory(commandLine);
244             exit(0);
245             break;
246         case CTRL_E:
247             endLine(*commandLine, cursorLocation);
248             break;
249         case CTRL_F:
250             gotoRight(*commandLine, cursorLocation);
251             break;
252         case CTRL_H:
253             rmChar(*commandLine, SCI_BACKSPACE, cursorLocation);
254             break;
255         case CTRL_K:
256             deleteFromCursToEndLine(*commandLine, cursorLocation);
257             updateTokenInScilabHistory(commandLine);
258             break;
259         case CTRL_L:
260             tohome();
261             printPrompt(WRITE_PROMPT);
262             printf("%ls", *commandLine);
263             break;
264         case CTRL_N:
265             nextCmd(commandLine, cursorLocation);
266             break;
267         case CTRL_P:
268             previousCmd(commandLine, cursorLocation);
269             break;
270         case CTRL_U:
271             deleteFromCursToBeginningLine(*commandLine, cursorLocation);
272             updateTokenInScilabHistory(commandLine);
273             break;
274         case CTRL_W:
275             deletePreviousWordFromCurs(*commandLine, cursorLocation);
276             updateTokenInScilabHistory(commandLine);
277             break;
278         case '\t':
279             autoCompletionInConsoleMode(commandLine, cursorLocation);
280             updateTokenInScilabHistory(commandLine);
281             break;
282         case ESCAPE:
283             caseMetaKey(commandLine, cursorLocation);
284             break;
285         case SCI_BACKSPACE:
286             rmChar(*commandLine, SCI_BACKSPACE, cursorLocation);
287             updateTokenInScilabHistory(commandLine);
288             break;
289         case WEOF:
290             setCBreak(1);
291             endCopyPast(*commandLine);
292             break;
293         default:
294             /* Different keys are not in different case when it add characters to the command line */
295             if (key == L'\n')
296             {
297                 setCBreak(0);
298                 setCharDisplay(DISP_FAINT);
299             }
300             addChar(commandLine, key, cursorLocation);
301
302             updateTokenInScilabHistory(commandLine);
303             break;
304     }
305 }
306
307 /* main command line function */
308 char *getCmdLine(void)
309 {
310     char *multiByteString = NULL;
311
312     unsigned int cursorLocation = 0;
313
314     static wchar_t *commandLine = NULL;
315
316     static int nextLineLocationInWideString = 0;
317
318     if (isatty(fileno(stdin)))
319     {
320         /* We are not in a pipe */
321         printPrompt(WRITE_PROMPT);
322         setCharDisplay(DISP_BRIGHT);
323     }
324     setTokenInteruptExecution(RESET_TOKEN);
325
326     if (commandLine == NULL || commandLine[nextLineLocationInWideString] == L'\0')
327     {
328         if (commandLine != NULL)
329         {
330             FREE(commandLine);
331         }
332         commandLine = MALLOC(1024 * sizeof(*commandLine));
333         *commandLine = L'\0';
334         nextLineLocationInWideString = 0;
335     }
336     else
337     {
338         setTokenInteruptExecution(SEND_MULTI_COMMAND);
339     }
340     setSearchedTokenInScilabHistory(NULL);
341
342     while (getTokenInteruptExecution() == CONTINUE_COMMAND)
343     {
344         getKey(&commandLine, &cursorLocation);
345     }
346
347     cursorLocation = nextLineLocationInWideString;
348     while (commandLine[cursorLocation] != L'\n' && commandLine[cursorLocation] != L'\0')
349     {
350         cursorLocation++;
351     }
352
353     commandLine[cursorLocation] = L'\0';
354
355     if (getTokenInteruptExecution() == SEND_MULTI_COMMAND)
356     {
357         printf("%ls\n", &commandLine[nextLineLocationInWideString]);
358     }
359
360     multiByteString = wide_string_to_UTF8(&commandLine[nextLineLocationInWideString]);
361
362     nextLineLocationInWideString = cursorLocation + 1;
363
364     appendLineToScilabHistory(multiByteString);
365
366     setSearchedTokenInScilabHistory(NULL);
367
368     setCharDisplay(DISP_RESET);
369
370     if (multiByteString && strlen(multiByteString) > 4096)
371     {
372         printf(_("Command is too long (more than %d characters long): could not send it to Scilab\n"), 4096);
373         FREE(multiByteString);
374         return NULL;
375     }
376
377     return multiByteString;
378 }
379
380 /* set the token for the command line */
381 int setTokenInteruptExecution(int token)
382 {
383     static int savedToken = RESET_TOKEN;
384
385     if (token != CHECK_TOKEN)
386     {
387         savedToken = token;
388     }
389     return savedToken;
390 }
391
392 /* get the token for the command line */
393 int getTokenInteruptExecution(void)
394 {
395     return setTokenInteruptExecution(CHECK_TOKEN);
396 }