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