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