Bug 12909 fixed: Completion on (mt)list led to a crash
[scilab.git] / scilab / modules / console / src / c / cmdLine / autoCompletionCli.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 <string.h>
13 #include <wchar.h>
14 #include <wctype.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include "charEncoding.h"
19 #include "completion.h"
20 #include "autoCompletionCli.h"
21 #include "completeLine.h"
22 #include "cliDisplayManagement.h"
23 #include "freeArrayOfString.h"
24 #include "cliPrompt.h"
25 #include "MALLOC.h"
26 #include "completion.h"
27 #include "getPartLine.h"
28 #include "getCommonPart.h"
29 #include "localization.h"
30 #include "scilabmode.h"
31 #include "sciprint.h"
32 #include "HistoryManager.h"
33 #include "ConsoleRead.h"
34 #include "SetConsolePrompt.h"
35 #include "TermReadAndProcess.h"
36 #include "sciquit.h"
37 #include "getCommonPart.h"
38
39 static void doCompletion(char *wk_buf, unsigned int *cursor, unsigned int *cursor_max);
40
41 static char *getLineBeforeCaret(char *wk_buf, unsigned int *cursor);
42
43 static char *getLineAfterCaret(char *wk_buf, unsigned int *cursor, unsigned int *cursor_max);
44
45 static void backspace(int n);
46
47 static void erase_nchar(int n);
48
49 static void TermCompletionOnFiles(char **dictionaryFiles, int sizedictionaryFiles,
50                                   char *lineBeforeCaret, char *lineAfterCaret, char *filePattern, char *defaultPattern,
51                                   char *wk_buf, unsigned int *cursor, unsigned int *cursor_max);
52 static int CopyLineAtPrompt(char *wk_buf, char *line, unsigned int *cursor, unsigned int *cursor_max);
53
54 static void TermCompletionOnAll(char *lineBeforeCaret, char *lineAfterCaret, char *defaultPattern, char *wk_buf, unsigned int *cursor,
55                                 unsigned int *cursor_max);
56
57 static void displayCompletionDictionary(char **dictionary, int sizedictionary, char *namedictionary);
58
59 static char **concatenateStrings(int *sizearrayofstring, char *string1, char *string2, char *string3, char *string4, char *string5);
60
61 /* Autocompletion in NW/NWNI */
62 void autoCompletionInConsoleMode(wchar_t ** commandLine, unsigned int *cursorLocation)
63 {
64     char *multiByteString = NULL;
65
66     wchar_t *wideString = NULL;
67
68     int sizeToAlloc = 0;
69
70     unsigned int nbrCharInString;
71
72     multiByteString = wide_string_to_UTF8(*commandLine);
73     nbrCharInString = wcslen(*commandLine);
74     doCompletion(multiByteString, cursorLocation, &nbrCharInString);
75     wideString = to_wide_string(multiByteString);
76     /* Copy the new string in a buffer wich size is a multiple of 1024 */
77     sizeToAlloc = 1024 * (wcslen(wideString) / 1024 + 1);
78     FREE(*commandLine);
79     *commandLine = MALLOC(sizeof(**commandLine) * sizeToAlloc);
80     wcscpy(*commandLine, wideString);
81     FREE(wideString);
82     FREE(multiByteString);
83 }
84
85 static void doCompletion(char *wk_buf, unsigned int *cursor, unsigned int *cursor_max)
86 {
87     char *LineBeforeCaret = getLineBeforeCaret(wk_buf, cursor);
88
89     char *LineAfterCaret = getLineAfterCaret(wk_buf, cursor, cursor_max);
90
91     char *fileSearchedPattern = getFilePartLevel(LineBeforeCaret);
92
93     char *SearchedPattern = getPartLevel(LineBeforeCaret);
94
95     char **completionDictionaryFiles = NULL;
96
97     int sizecompletionDictionaryFiles = 0;
98
99     completionDictionaryFiles = completionOnFiles(fileSearchedPattern, &sizecompletionDictionaryFiles);
100     if (completionDictionaryFiles)
101     {
102         TermCompletionOnFiles(completionDictionaryFiles, sizecompletionDictionaryFiles,
103                               LineBeforeCaret, LineAfterCaret, fileSearchedPattern, SearchedPattern, wk_buf, cursor, cursor_max);
104
105         freeArrayOfString(completionDictionaryFiles, sizecompletionDictionaryFiles);
106     }
107     else
108     {
109         TermCompletionOnAll(LineBeforeCaret, LineAfterCaret, SearchedPattern, wk_buf, cursor, cursor_max);
110     }
111
112     if (LineBeforeCaret)
113     {
114         FREE(LineBeforeCaret);
115         LineBeforeCaret = NULL;
116     }
117     if (LineAfterCaret)
118     {
119         FREE(LineAfterCaret);
120         LineAfterCaret = NULL;
121     }
122     if (fileSearchedPattern)
123     {
124         FREE(fileSearchedPattern);
125         fileSearchedPattern = NULL;
126     }
127     if (SearchedPattern)
128     {
129         FREE(SearchedPattern);
130         SearchedPattern = NULL;
131     }
132 }
133
134 static char *getLineBeforeCaret(char *wk_buf, unsigned int *cursor)
135 {
136     char *line = NULL;
137
138     line = strdup(wk_buf);
139     line[*cursor] = '\0';
140
141     return line;
142 }
143
144 static char *getLineAfterCaret(char *wk_buf, unsigned int *cursor, unsigned int *cursor_max)
145 {
146     if (wk_buf)
147     {
148         if (*cursor != *cursor_max)
149         {
150             int len = *cursor_max - *cursor;
151
152             char aftercaret[WK_BUF_SIZE];
153
154             strcpy(aftercaret, &wk_buf[*cursor]);
155             aftercaret[len + 1] = '\0';
156             return strdup(aftercaret);
157         }
158     }
159     return strdup("");
160 }
161
162 static void backspace(int n)
163 {
164     if (n < 1)
165     {
166         return;
167     }
168     while (n--)
169 #ifdef TERMCAP
170         if (BC)
171         {
172             /* if control-H won-t work */
173             fputs(BC, stdout);
174         }
175         else
176         {
177             /* otherwise just use a normal control-H */
178             putchar('\010');
179         }
180 #else
181         putchar('\010');
182 #endif
183
184 }
185
186 static void erase_nchar(int n)
187 {
188     int i;                      /* fill field with blanks */
189
190     for (i = 0; i < n; i++)
191     {
192         putchar(' ');
193     }
194     backspace(n);               /* and back up over blanks just written */
195 }
196
197 static void TermCompletionOnFiles(char **dictionaryFiles, int sizedictionaryFiles,
198                                   char *lineBeforeCaret, char *lineAfterCaret, char *filePattern, char *defaultPattern,
199                                   char *wk_buf, unsigned int *cursor, unsigned int *cursor_max)
200 {
201     if (dictionaryFiles)
202     {
203         if (sizedictionaryFiles == 1)
204         {
205             char *new_line = completeLine(lineBeforeCaret, dictionaryFiles[0], filePattern, defaultPattern, TRUE, lineAfterCaret);
206
207             if (new_line)
208             {
209                 char buflinetmp[WK_BUF_SIZE + 1];
210
211                 strcpy(buflinetmp, new_line);
212                 FREE(new_line);
213
214                 backspace(*cursor);
215                 erase_nchar(*cursor_max);
216                 wk_buf[0] = '\0';
217                 *cursor = *cursor_max = 0;
218
219                 CopyLineAtPrompt(wk_buf, buflinetmp, cursor, cursor_max);
220                 return;
221             }
222         }
223         else
224         {
225             char *common = getCommonPart(dictionaryFiles, sizedictionaryFiles);
226
227             displayCompletionDictionary(dictionaryFiles, sizedictionaryFiles, gettext("File or Directory"));
228
229             printf("\n");
230
231             backspace(*cursor);
232             erase_nchar(*cursor_max);
233             wk_buf[0] = '\0';
234             *cursor = *cursor_max = 0;
235
236             printPrompt(WRITE_PROMPT);
237
238             if (defaultPattern[0] == 0)
239             {
240                 int lennewline = (int)strlen(lineBeforeCaret) + (int)strlen(lineAfterCaret);
241
242                 char *new_line = (char *)MALLOC(sizeof(char) * (lennewline + 1));
243
244                 if (new_line)
245                 {
246                     strcpy(new_line, lineBeforeCaret);
247                     strcat(new_line, lineAfterCaret);
248
249                     CopyLineAtPrompt(wk_buf, new_line, cursor, cursor_max);
250                     FREE(new_line);
251                     new_line = NULL;
252                 }
253             }
254             else if (common)
255             {
256                 char *new_line = completeLine(lineBeforeCaret, common, filePattern, defaultPattern, TRUE, lineAfterCaret);
257
258                 if (new_line)
259                 {
260                     char buflinetmp[WK_BUF_SIZE + 1];
261
262                     strcpy(buflinetmp, new_line);
263                     FREE(new_line);
264
265                     CopyLineAtPrompt(wk_buf, buflinetmp, cursor, cursor_max);
266                 }
267                 else
268                 {
269                     int lennewline = (int)strlen(lineBeforeCaret) + (int)strlen(lineAfterCaret);
270
271                     new_line = (char *)MALLOC(sizeof(char) * (lennewline + 1));
272
273                     if (new_line)
274                     {
275                         strcpy(new_line, lineBeforeCaret);
276                         strcat(new_line, lineAfterCaret);
277
278                         CopyLineAtPrompt(wk_buf, new_line, cursor, cursor_max);
279                         FREE(new_line);
280                         new_line = NULL;
281                     }
282                 }
283             }
284             if (common)
285             {
286                 FREE(common);
287                 common = NULL;
288             }
289         }
290     }
291 }
292
293 static int CopyLineAtPrompt(char *wk_buf, char *line, unsigned int *cursor, unsigned int *cursor_max)
294 {
295     if (line)
296     {
297         //** Copy line to current command buffer, usefull in completion case.
298         strcpy(wk_buf, line);
299         backspace(*cursor);     /* backspace to beginning of line */
300         printf("%s", wk_buf);   /* copy to screen */
301
302         *cursor = strlen(wk_buf);   /* cursor set at end of line */
303
304         /* erase extra characters left over if any */
305         erase_nchar(GET_MAX(0, (*cursor_max - *cursor)));
306         *cursor_max = *cursor;
307         return 1;
308     }
309     return 0;
310 }
311
312 static char **concatenateStrings(int *sizearrayofstring, char *string1, char *string2, char *string3, char *string4, char *string5)
313 {
314     int newsize = 0;
315
316     char **arrayOfString = NULL;
317
318     *sizearrayofstring = 0;
319
320     if (string1)
321     {
322         newsize++;
323     }
324     if (string2)
325     {
326         newsize++;
327     }
328     if (string3)
329     {
330         newsize++;
331     }
332     if (string4)
333     {
334         newsize++;
335     }
336     if (string5)
337     {
338         newsize++;
339     }
340
341     if (newsize > 0)
342     {
343         arrayOfString = (char **)MALLOC(sizeof(char *) * (newsize));
344         if (arrayOfString)
345         {
346             int i = 0;
347
348             if (string1)
349             {
350                 arrayOfString[i] = string1;
351                 i++;
352             }
353             if (string2)
354             {
355                 arrayOfString[i] = string2;
356                 i++;
357             }
358             if (string3)
359             {
360                 arrayOfString[i] = string3;
361                 i++;
362             }
363             if (string4)
364             {
365                 arrayOfString[i] = string4;
366                 i++;
367             }
368             if (string5)
369             {
370                 arrayOfString[i] = string5;
371                 i++;
372             }
373             *sizearrayofstring = i;
374         }
375         else
376         {
377             *sizearrayofstring = 0;
378         }
379     }
380     return arrayOfString;
381 }
382
383 static void TermCompletionOnAll(char *lineBeforeCaret, char *lineAfterCaret, char *defaultPattern, char *wk_buf, unsigned int *cursor,
384                                 unsigned int *cursor_max)
385 {
386     if (defaultPattern)
387     {
388         int numberWordFound = 0;
389
390         char **completionDictionaryFunctions = NULL;
391
392         int sizecompletionDictionaryFunctions = 0;
393
394         char **completionDictionaryCommandWords = NULL;
395
396         int sizecompletionDictionaryCommandWords = 0;
397
398         char **completionDictionaryMacros = NULL;
399
400         int sizecompletionDictionaryMacros = 0;
401
402         char **completionDictionaryVariables = NULL;
403
404         int sizecompletionDictionaryVariables = 0;
405
406         char **completionDictionaryHandleGraphicsProperties = NULL;
407
408         int sizecompletionDictionaryHandleGraphicsProperties = 0;
409
410         char **completionDictionaryFields = NULL;
411
412         int sizecompletionDictionaryFields = 0;
413
414         completionDictionaryFields = completionOnFields(lineBeforeCaret, defaultPattern, &sizecompletionDictionaryFields);
415
416         if (!completionDictionaryFields && strcmp(defaultPattern, ""))
417         {
418             completionDictionaryFunctions = completionOnFunctions(defaultPattern, &sizecompletionDictionaryFunctions);
419             completionDictionaryCommandWords = completionOnCommandWords(defaultPattern, &sizecompletionDictionaryCommandWords);
420             completionDictionaryMacros = completionOnMacros(defaultPattern, &sizecompletionDictionaryMacros);
421             completionDictionaryVariables = completionOnVariablesWithoutMacros(defaultPattern, &sizecompletionDictionaryVariables);
422             completionDictionaryHandleGraphicsProperties =
423                 completionOnHandleGraphicsProperties(defaultPattern, &sizecompletionDictionaryHandleGraphicsProperties);
424         }
425
426         numberWordFound = sizecompletionDictionaryFunctions + sizecompletionDictionaryCommandWords +
427                           sizecompletionDictionaryMacros + sizecompletionDictionaryVariables +
428                           sizecompletionDictionaryHandleGraphicsProperties + sizecompletionDictionaryFields;
429
430         if (numberWordFound > 0)
431         {
432             if (numberWordFound == 1)
433             {
434                 char **completionDictionary = NULL;
435
436                 char *new_line = NULL;
437
438                 if (completionDictionaryFields)
439                 {
440                     completionDictionary = completionDictionaryFields;
441                 }
442                 if (completionDictionaryFunctions)
443                 {
444                     completionDictionary = completionDictionaryFunctions;
445                 }
446                 if (completionDictionaryCommandWords)
447                 {
448                     completionDictionary = completionDictionaryCommandWords;
449                 }
450                 if (completionDictionaryMacros)
451                 {
452                     completionDictionary = completionDictionaryMacros;
453                 }
454                 if (completionDictionaryVariables)
455                 {
456                     completionDictionary = completionDictionaryVariables;
457                 }
458                 if (completionDictionaryHandleGraphicsProperties)
459                 {
460                     completionDictionary = completionDictionaryHandleGraphicsProperties;
461                 }
462
463                 new_line = completeLine(lineBeforeCaret, completionDictionary[0], NULL, defaultPattern, FALSE, lineAfterCaret);
464                 if (new_line)
465                 {
466                     char buflinetmp[WK_BUF_SIZE + 1];
467
468                     strcpy(buflinetmp, new_line);
469                     FREE(new_line);
470
471                     backspace(*cursor);
472                     erase_nchar(*cursor_max);
473                     wk_buf[0] = '\0';
474                     *cursor = *cursor_max = 0;
475
476                     CopyLineAtPrompt(wk_buf, buflinetmp, cursor, cursor_max);
477                 }
478
479                 freeArrayOfString(completionDictionary, 1);
480             }
481             else
482             {
483                 char *commonAll = NULL;
484
485                 if (completionDictionaryFields)
486                 {
487                     commonAll = getCommonPart(completionDictionaryFields, sizecompletionDictionaryFields);
488                     displayCompletionDictionary(completionDictionaryFields, sizecompletionDictionaryFields, (char *)_("Scilab Fields"));
489                     freeArrayOfString(completionDictionaryFields, sizecompletionDictionaryFields);
490                 }
491                 else
492                 {
493                     char *commonFunctions = getCommonPart(completionDictionaryFunctions, sizecompletionDictionaryFunctions);
494
495                     char *commonCommandWords = getCommonPart(completionDictionaryCommandWords, sizecompletionDictionaryCommandWords);
496
497                     char *commonMacros = getCommonPart(completionDictionaryMacros, sizecompletionDictionaryMacros);
498
499                     char *commonVariables = getCommonPart(completionDictionaryVariables, sizecompletionDictionaryVariables);
500
501                     char *commonHandleGraphicsProperties =
502                         getCommonPart(completionDictionaryHandleGraphicsProperties, sizecompletionDictionaryHandleGraphicsProperties);
503
504                     int sizecommonsDictionary = 0;
505
506                     char **commonsDictionary = concatenateStrings(&sizecommonsDictionary, commonFunctions,
507                                                commonMacros, commonCommandWords, commonVariables, commonHandleGraphicsProperties);
508
509                     if (sizecommonsDictionary > 0)
510                     {
511                         if (sizecommonsDictionary == 1)
512                         {
513                             commonAll = strdup(commonsDictionary[0]);
514                         }
515                         else
516                         {
517                             commonAll = getCommonPart(commonsDictionary, sizecommonsDictionary);
518                         }
519                         freeArrayOfString(commonsDictionary, sizecommonsDictionary);
520                     }
521
522                     displayCompletionDictionary(completionDictionaryFunctions, sizecompletionDictionaryFunctions, (char *)_("Scilab Function"));
523                     displayCompletionDictionary(completionDictionaryCommandWords, sizecompletionDictionaryCommandWords, (char *)_("Scilab Command"));
524                     displayCompletionDictionary(completionDictionaryMacros, sizecompletionDictionaryMacros, (char *)_("Scilab Macro"));
525                     displayCompletionDictionary(completionDictionaryVariables, sizecompletionDictionaryVariables, (char *)_("Scilab Variable"));
526                     displayCompletionDictionary(completionDictionaryHandleGraphicsProperties, sizecompletionDictionaryHandleGraphicsProperties,
527                                                 (char *)_("Graphics handle field"));
528                     freeArrayOfString(completionDictionaryFunctions, sizecompletionDictionaryFunctions);
529                     freeArrayOfString(completionDictionaryCommandWords, sizecompletionDictionaryCommandWords);
530                     freeArrayOfString(completionDictionaryMacros, sizecompletionDictionaryMacros);
531                     freeArrayOfString(completionDictionaryVariables, sizecompletionDictionaryVariables);
532                     freeArrayOfString(completionDictionaryHandleGraphicsProperties, sizecompletionDictionaryHandleGraphicsProperties);
533                 }
534
535                 printf("\n");
536
537                 backspace(*cursor);
538                 erase_nchar(*cursor_max);
539                 wk_buf[0] = '\0';
540                 *cursor = *cursor_max = 0;
541
542                 printPrompt(WRITE_PROMPT);
543
544                 if (commonAll)
545                 {
546                     char *new_line = NULL;
547
548                     new_line = completeLine(lineBeforeCaret, commonAll, NULL, defaultPattern, FALSE, lineAfterCaret);
549                     if (new_line)
550                     {
551                         char buflinetmp[WK_BUF_SIZE + 1];
552
553                         strcpy(buflinetmp, new_line);
554                         FREE(new_line);
555
556                         CopyLineAtPrompt(wk_buf, buflinetmp, cursor, cursor_max);
557                     }
558
559                     FREE(commonAll);
560                     commonAll = NULL;
561                 }
562             }
563         }
564     }
565 }
566
567 static void displayCompletionDictionary(char **dictionary, int sizedictionary, char *namedictionary)
568 {
569 #define MAX_LINE_SIZE 79        /* 80 - 1 the leading space */
570     if (dictionary)
571     {
572         int i = 0;
573
574         int lenCurrentLine = 0;
575
576         /* Set the category */
577         setCharDisplay(DISP_ITALIC);
578         printf("\n");
579         printf("%s", namedictionary);
580         printf("%s", ":");
581         printf("\n");
582         /* Reset to set back the default display. */
583         setCharDisplay(DISP_LAST_SET);
584
585         /* Set the list of completion */
586         setCharDisplay(DISP_RESET);
587         for (i = 0; i < sizedictionary; i++)
588         {
589             int newlenLine = lenCurrentLine + (int)strlen(dictionary[i]) + (int)strlen(" ");
590
591             if ((lenCurrentLine + newlenLine) > MAX_LINE_SIZE)
592             {
593                 printf("\n");
594                 lenCurrentLine = 0;
595             }
596             else
597             {
598                 lenCurrentLine = newlenLine;
599             }
600             printf("%s", dictionary[i]);
601             printf("%s", " ");
602         }
603         printf("\n");
604         /* Reset to set back the default display. */
605         setCharDisplay(DISP_LAST_SET);
606     }
607 }