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