Fix some valgrind issues
[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
168     if (sizeOfCmd == 0)
169     {
170         setTokenInteruptExecution(SEND_COMMAND);
171     }
172     else
173     {
174         if (commandLine[sizeOfCmd - 1] == L'\n')
175         {
176             setTokenInteruptExecution(SEND_COMMAND);
177         }
178         else
179         {
180             setTokenInteruptExecution(CONTINUE_COMMAND);
181         }
182     }
183 }
184
185 /* Reset command line if CTRL-C is pressed */
186 static void resetCommandLine(wchar_t ** commandLine, unsigned int *cursorLocation)
187 {
188     char *multiByteString = NULL;
189
190     /* Send the preivous edited line in the history */
191     multiByteString = wide_string_to_UTF8(*commandLine);
192     appendLineToScilabHistory(multiByteString);
193     FREE(multiByteString);
194     setSearchedTokenInScilabHistory(NULL);
195     FREE(*commandLine);
196     /* Reset command line and cursor position */
197     *cursorLocation = 0;
198     *commandLine = MALLOC(1024 * sizeof(**commandLine));
199     **commandLine = L'\0';
200     setTokenInteruptExecution(RESET_TOKEN);
201 }
202
203 /*
204  * Read keyboard a first time.
205  */
206 static void getKey(wchar_t ** commandLine, unsigned int *cursorLocation)
207 {
208     int key;
209
210     key = getwchar();
211
212     // Need to clear the stdin
213     if (key == WEOF && feof(stdin))
214     {
215         clearerr(stdin);
216     }
217
218     if (getTokenInteruptExecution() == DO_NOT_SEND_COMMAND)
219     {
220         resetCommandLine(commandLine, cursorLocation);
221     }
222
223     switch (key)
224     {
225         case CTRL_A:
226             begLine(*commandLine, cursorLocation);
227             break;
228         case CTRL_B:
229             gotoLeft(*commandLine, cursorLocation);
230             break;
231         case CTRL_D:
232             rmChar(*commandLine, SCI_DELETE, cursorLocation);
233             updateTokenInScilabHistory(commandLine);
234             break;
235         case CTRL_E:
236             endLine(*commandLine, cursorLocation);
237             break;
238         case CTRL_F:
239             gotoRight(*commandLine, cursorLocation);
240             break;
241         case CTRL_H:
242             rmChar(*commandLine, SCI_BACKSPACE, cursorLocation);
243             break;
244         case CTRL_K:
245             deleteFromCursToEndLine(*commandLine, cursorLocation);
246             updateTokenInScilabHistory(commandLine);
247             break;
248         case CTRL_L:
249             tohome();
250             printPrompt(WRITE_PROMPT);
251             printf("%ls", *commandLine);
252             break;
253         case CTRL_N:
254             nextCmd(commandLine, cursorLocation);
255             break;
256         case CTRL_P:
257             previousCmd(commandLine, cursorLocation);
258             break;
259         case CTRL_U:
260             deleteFromCursToBeginningLine(*commandLine, cursorLocation);
261             updateTokenInScilabHistory(commandLine);
262             break;
263         case CTRL_W:
264             deletePreviousWordFromCurs(*commandLine, cursorLocation);
265             updateTokenInScilabHistory(commandLine);
266             break;
267         case '\t':
268             autoCompletionInConsoleMode(commandLine, cursorLocation);
269             updateTokenInScilabHistory(commandLine);
270             break;
271         case ESCAPE:
272             caseMetaKey(commandLine, cursorLocation);
273             break;
274         case SCI_BACKSPACE:
275             rmChar(*commandLine, SCI_BACKSPACE, cursorLocation);
276             updateTokenInScilabHistory(commandLine);
277             break;
278         case WEOF:
279             setCBreak(1);
280             endCopyPast(*commandLine);
281             break;
282         default:
283             /* Different keys are not in different case when it add characters to the command line */
284             if (key == L'\n')
285             {
286                 setCBreak(0);
287                 setCharDisplay(DISP_FAINT);
288             }
289             addChar(commandLine, key, cursorLocation);
290
291             updateTokenInScilabHistory(commandLine);
292             break;
293     }
294 }
295
296 /* main command line function */
297 char *getCmdLine(void)
298 {
299     char *multiByteString = NULL;
300
301     unsigned int cursorLocation = 0;
302
303     static wchar_t *commandLine = NULL;
304
305     static int nextLineLocationInWideString = 0;
306
307     if (isatty(fileno(stdin)))
308     {
309         /* We are not in a pipe */
310         printPrompt(WRITE_PROMPT);
311         setCharDisplay(DISP_BRIGHT);
312     }
313     setTokenInteruptExecution(RESET_TOKEN);
314
315     if (commandLine == NULL || commandLine[nextLineLocationInWideString] == L'\0')
316     {
317         if (commandLine != NULL)
318         {
319             FREE(commandLine);
320         }
321         commandLine = MALLOC(1024 * sizeof(*commandLine));
322         *commandLine = L'\0';
323         nextLineLocationInWideString = 0;
324     }
325     else
326     {
327         setTokenInteruptExecution(SEND_MULTI_COMMAND);
328     }
329     setSearchedTokenInScilabHistory(NULL);
330
331     while (getTokenInteruptExecution() == CONTINUE_COMMAND)
332     {
333         getKey(&commandLine, &cursorLocation);
334     }
335
336     cursorLocation = nextLineLocationInWideString;
337     while (commandLine[cursorLocation] != L'\n' && commandLine[cursorLocation] != L'\0')
338     {
339         cursorLocation++;
340     }
341
342     commandLine[cursorLocation] = L'\0';
343
344     if (getTokenInteruptExecution() == SEND_MULTI_COMMAND)
345     {
346         printf("%ls\n", &commandLine[nextLineLocationInWideString]);
347     }
348
349     multiByteString = wide_string_to_UTF8(&commandLine[nextLineLocationInWideString]);
350     FREE(commandLine);
351
352     nextLineLocationInWideString = cursorLocation + 1;
353
354     appendLineToScilabHistory(multiByteString);
355
356     setSearchedTokenInScilabHistory(NULL);
357
358     setCharDisplay(DISP_RESET);
359
360     if (multiByteString && strlen(multiByteString) > 4096)
361     {
362         printf(_("Command is too long (more than %d characters long): could not send it to Scilab\n"), 4096);
363         FREE(multiByteString);
364         return NULL;
365     }
366
367     return multiByteString;
368 }
369
370 /* set the token for the command line */
371 int setTokenInteruptExecution(int token)
372 {
373     static int savedToken = RESET_TOKEN;
374
375     if (token != CHECK_TOKEN)
376     {
377         savedToken = token;
378     }
379     return savedToken;
380 }
381
382 /* get the token for the command line */
383 int getTokenInteruptExecution(void)
384 {
385     return setTokenInteruptExecution(CHECK_TOKEN);
386 }