804dea3b56757eccd220d7f86d57fdaeb5bbc640
[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             break;
245         case CTRL_E:
246             endLine(*commandLine, cursorLocation);
247             break;
248         case CTRL_F:
249             gotoRight(*commandLine, cursorLocation);
250             break;
251         case CTRL_H:
252             rmChar(*commandLine, SCI_BACKSPACE, cursorLocation);
253             break;
254         case CTRL_K:
255             deleteFromCursToEndLine(*commandLine, cursorLocation);
256             updateTokenInScilabHistory(commandLine);
257             break;
258         case CTRL_L:
259             tohome();
260             printPrompt(WRITE_PROMPT);
261             printf("%ls", *commandLine);
262             break;
263         case CTRL_N:
264             nextCmd(commandLine, cursorLocation);
265             break;
266         case CTRL_P:
267             previousCmd(commandLine, cursorLocation);
268             break;
269         case CTRL_U:
270             deleteFromCursToBeginningLine(*commandLine, cursorLocation);
271             updateTokenInScilabHistory(commandLine);
272             break;
273         case CTRL_W:
274             deletePreviousWordFromCurs(*commandLine, cursorLocation);
275             updateTokenInScilabHistory(commandLine);
276             break;
277         case '\t':
278             autoCompletionInConsoleMode(commandLine, cursorLocation);
279             updateTokenInScilabHistory(commandLine);
280             break;
281         case ESCAPE:
282             caseMetaKey(commandLine, cursorLocation);
283             break;
284         case SCI_BACKSPACE:
285             rmChar(*commandLine, SCI_BACKSPACE, cursorLocation);
286             updateTokenInScilabHistory(commandLine);
287             break;
288         case WEOF:
289             setCBreak(1);
290             endCopyPast(*commandLine);
291             break;
292         default:
293             /* Different keys are not in different case when it add characters to the command line */
294             if (key == L'\n')
295             {
296                 setCBreak(0);
297                 setCharDisplay(DISP_FAINT);
298             }
299             addChar(commandLine, key, cursorLocation);
300
301             updateTokenInScilabHistory(commandLine);
302             break;
303     }
304 }
305
306 /* main command line function */
307 char *getCmdLine(void)
308 {
309     char *multiByteString = NULL;
310
311     unsigned int cursorLocation = 0;
312
313     static wchar_t *commandLine = NULL;
314
315     static int nextLineLocationInWideString = 0;
316
317     if (isatty(fileno(stdin)))
318     {
319         /* We are not in a pipe */
320         printPrompt(WRITE_PROMPT);
321         setCharDisplay(DISP_BRIGHT);
322     }
323     setTokenInteruptExecution(RESET_TOKEN);
324
325     if (commandLine == NULL || commandLine[nextLineLocationInWideString] == L'\0')
326     {
327         if (commandLine != NULL)
328         {
329             FREE(commandLine);
330         }
331         commandLine = MALLOC(1024 * sizeof(*commandLine));
332         *commandLine = L'\0';
333         nextLineLocationInWideString = 0;
334     }
335     else
336     {
337         setTokenInteruptExecution(SEND_MULTI_COMMAND);
338     }
339     setSearchedTokenInScilabHistory(NULL);
340
341     while (getTokenInteruptExecution() == CONTINUE_COMMAND)
342     {
343         getKey(&commandLine, &cursorLocation);
344     }
345
346     cursorLocation = nextLineLocationInWideString;
347     while (commandLine[cursorLocation] != L'\n' && commandLine[cursorLocation] != L'\0')
348     {
349         cursorLocation++;
350     }
351
352     commandLine[cursorLocation] = L'\0';
353
354     if (getTokenInteruptExecution() == SEND_MULTI_COMMAND)
355     {
356         printf("%ls\n", &commandLine[nextLineLocationInWideString]);
357     }
358
359     multiByteString = wide_string_to_UTF8(&commandLine[nextLineLocationInWideString]);
360
361     nextLineLocationInWideString = cursorLocation + 1;
362
363     appendLineToScilabHistory(multiByteString);
364
365     setSearchedTokenInScilabHistory(NULL);
366
367     setCharDisplay(DISP_RESET);
368
369     if (multiByteString && strlen(multiByteString) > 4096)
370     {
371         printf(_("Command is too long (more than %d characters long): could not send it to Scilab\n"), 4096);
372         FREE(multiByteString);
373         return NULL;
374     }
375
376     return multiByteString;
377 }
378
379 /* set the token for the command line */
380 int setTokenInteruptExecution(int token)
381 {
382     static int savedToken = RESET_TOKEN;
383
384     if (token != CHECK_TOKEN)
385     {
386         savedToken = token;
387     }
388     return savedToken;
389 }
390
391 /* get the token for the command line */
392 int getTokenInteruptExecution(void)
393 {
394     return setTokenInteruptExecution(CHECK_TOKEN);
395 }