1 /*        $NetBSD: test_filecompletion.c,v 1.5 2019/09/08 05:50:58 abhinav Exp $          */
2 
3 /*-
4  * Copyright (c) 2017 Abhinav Upadhyay <abhinav@NetBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "config.h"
33 
34 #include <assert.h>
35 #include <err.h>
36 #include <stdio.h>
37 #include <histedit.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <wchar.h>
41 
42 #include "filecomplete.h"
43 #include "el.h"
44 
45 typedef struct {
46           const wchar_t *user_typed_text; /* The actual text typed by the user on the terminal */
47           const char *completion_function_input ; /*the text received by fn_filename_completion_function */
48           const char *expanded_text[2]; /* the value to which completion_function_input should be expanded */
49           const wchar_t *escaped_output; /* expected escaped value of expanded_text */
50 } test_input;
51 
52 static test_input inputs[] = {
53           {
54                     /* simple test for escaping angular brackets */
55                     L"ls ang",
56                     "ang",
57                     {"ang<ular>test", NULL},
58                     L"ls ang\\<ular\\>test "
59           },
60           {
61                     /* test angular bracket inside double quotes: ls "dq_ang */
62                     L"ls \"dq_ang",
63                     "dq_ang",
64                     {"dq_ang<ular>test", NULL},
65                     L"ls \"dq_ang<ular>test\""
66           },
67           {
68                     /* test angular bracket inside singlq quotes: ls "sq_ang */
69                     L"ls 'sq_ang",
70                     "sq_ang",
71                     {"sq_ang<ular>test", NULL},
72                     L"ls 'sq_ang<ular>test'"
73           },
74           {
75                     /* simple test for backslash */
76                     L"ls back",
77                     "back",
78                     {"backslash\\test", NULL},
79                     L"ls backslash\\\\test "
80           },
81           {
82                     /* backslash inside single quotes */
83                     L"ls 'sback",
84                     "sback",
85                     {"sbackslash\\test", NULL},
86                     L"ls 'sbackslash\\test'"
87           },
88           {
89                     /* backslash inside double quotes */
90                     L"ls \"dback",
91                     "dback",
92                     {"dbackslash\\test", NULL},
93                     L"ls \"dbackslash\\\\test\""
94           },
95           {
96                     /* test braces */
97                     L"ls br",
98                     "br",
99                     {"braces{test}", NULL},
100                     L"ls braces\\{test\\} "
101           },
102           {
103                     /* test braces inside single quotes */
104                     L"ls 'sbr",
105                     "sbr",
106                     {"sbraces{test}", NULL},
107                     L"ls 'sbraces{test}'"
108           },
109           {
110                     /* test braces inside double quotes */
111                     L"ls \"dbr",
112                     "dbr",
113                     {"dbraces{test}", NULL},
114                     L"ls \"dbraces{test}\""
115           },
116           {
117                     /* test dollar */
118                     L"ls doll",
119                     "doll",
120                     {"doll$artest", NULL},
121                     L"ls doll\\$artest "
122           },
123           {
124                     /* test dollar inside single quotes */
125                     L"ls 'sdoll",
126                     "sdoll",
127                     {"sdoll$artest", NULL},
128                     L"ls 'sdoll$artest'"
129           },
130           {
131                     /* test dollar inside double quotes */
132                     L"ls \"ddoll",
133                     "ddoll",
134                     {"ddoll$artest", NULL},
135                     L"ls \"ddoll\\$artest\""
136           },
137           {
138                     /* test equals */
139                     L"ls eq",
140                     "eq",
141                     {"equals==test", NULL},
142                     L"ls equals\\=\\=test "
143           },
144           {
145                     /* test equals inside sinqle quotes */
146                     L"ls 'seq",
147                     "seq",
148                     {"sequals==test", NULL},
149                     L"ls 'sequals==test'"
150           },
151           {
152                     /* test equals inside double quotes */
153                     L"ls \"deq",
154                     "deq",
155                     {"dequals==test", NULL},
156                     L"ls \"dequals==test\""
157           },
158           {
159                     /* test \n */
160                     L"ls new",
161                     "new",
162                     {"new\\nline", NULL},
163                     L"ls new\\\\nline "
164           },
165           {
166                     /* test \n inside single quotes */
167                     L"ls 'snew",
168                     "snew",
169                     {"snew\nline", NULL},
170                     L"ls 'snew\nline'"
171           },
172           {
173                     /* test \n inside double quotes */
174                     L"ls \"dnew",
175                     "dnew",
176                     {"dnew\nline", NULL},
177                     L"ls \"dnew\nline\""
178           },
179           {
180                     /* test single space */
181                     L"ls spac",
182                     "spac",
183                     {"space test", NULL},
184                     L"ls space\\ test "
185           },
186           {
187                     /* test single space inside singlq quotes */
188                     L"ls 's_spac",
189                     "s_spac",
190                     {"s_space test", NULL},
191                     L"ls 's_space test'"
192           },
193           {
194                     /* test single space inside double quotes */
195                     L"ls \"d_spac",
196                     "d_spac",
197                     {"d_space test", NULL},
198                     L"ls \"d_space test\""
199           },
200           {
201                     /* test multiple spaces */
202                     L"ls multi",
203                     "multi",
204                     {"multi space  test", NULL},
205                     L"ls multi\\ space\\ \\ test "
206           },
207           {
208                     /* test multiple spaces inside single quotes */
209                     L"ls 's_multi",
210                     "s_multi",
211                     {"s_multi space  test", NULL},
212                     L"ls 's_multi space  test'"
213           },
214           {
215                     /* test multiple spaces inside double quotes */
216                     L"ls \"d_multi",
217                     "d_multi",
218                     {"d_multi space  test", NULL},
219                     L"ls \"d_multi space  test\""
220           },
221           {
222                     /* test double quotes */
223                     L"ls doub",
224                     "doub",
225                     {"doub\"quotes", NULL},
226                     L"ls doub\\\"quotes "
227           },
228           {
229                     /* test double quotes inside single quotes */
230                     L"ls 's_doub",
231                     "s_doub",
232                     {"s_doub\"quotes", NULL},
233                     L"ls 's_doub\"quotes'"
234           },
235           {
236                     /* test double quotes inside double quotes */
237                     L"ls \"d_doub",
238                     "d_doub",
239                     {"d_doub\"quotes", NULL},
240                     L"ls \"d_doub\\\"quotes\""
241           },
242           {
243                     /* test multiple double quotes */
244                     L"ls mud",
245                     "mud",
246                     {"mud\"qu\"otes\"", NULL},
247                     L"ls mud\\\"qu\\\"otes\\\" "
248           },
249           {
250                     /* test multiple double quotes inside single quotes */
251                     L"ls 'smud",
252                     "smud",
253                     {"smud\"qu\"otes\"", NULL},
254                     L"ls 'smud\"qu\"otes\"'"
255           },
256           {
257                     /* test multiple double quotes inside double quotes */
258                     L"ls \"dmud",
259                     "dmud",
260                     {"dmud\"qu\"otes\"", NULL},
261                     L"ls \"dmud\\\"qu\\\"otes\\\"\""
262           },
263           {
264                     /* test one single quote */
265                     L"ls sing",
266                     "sing",
267                     {"single'quote", NULL},
268                     L"ls single\\'quote "
269           },
270           {
271                     /* test one single quote inside single quote */
272                     L"ls 'ssing",
273                     "ssing",
274                     {"ssingle'quote", NULL},
275                     L"ls 'ssingle'\\''quote'"
276           },
277           {
278                     /* test one single quote inside double quote */
279                     L"ls \"dsing",
280                     "dsing",
281                     {"dsingle'quote", NULL},
282                     L"ls \"dsingle'quote\""
283           },
284           {
285                     /* test multiple single quotes */
286                     L"ls mu_sing",
287                     "mu_sing",
288                     {"mu_single''quotes''", NULL},
289                     L"ls mu_single\\'\\'quotes\\'\\' "
290           },
291           {
292                     /* test multiple single quotes inside single quote */
293                     L"ls 'smu_sing",
294                     "smu_sing",
295                     {"smu_single''quotes''", NULL},
296                     L"ls 'smu_single'\\'''\\''quotes'\\\'''\\'''"
297           },
298           {
299                     /* test multiple single quotes inside double quote */
300                     L"ls \"dmu_sing",
301                     "dmu_sing",
302                     {"dmu_single''quotes''", NULL},
303                     L"ls \"dmu_single''quotes''\""
304           },
305           {
306                     /* test parenthesis */
307                     L"ls paren",
308                     "paren",
309                     {"paren(test)", NULL},
310                     L"ls paren\\(test\\) "
311           },
312           {
313                     /* test parenthesis inside single quote */
314                     L"ls 'sparen",
315                     "sparen",
316                     {"sparen(test)", NULL},
317                     L"ls 'sparen(test)'"
318           },
319           {
320                     /* test parenthesis inside double quote */
321                     L"ls \"dparen",
322                     "dparen",
323                     {"dparen(test)", NULL},
324                     L"ls \"dparen(test)\""
325           },
326           {
327                     /* test pipe */
328                     L"ls pip",
329                     "pip",
330                     {"pipe|test", NULL},
331                     L"ls pipe\\|test "
332           },
333           {
334                     /* test pipe inside single quote */
335                     L"ls 'spip",
336                     "spip",
337                     {"spipe|test", NULL},
338                     L"ls 'spipe|test'",
339           },
340           {
341                     /* test pipe inside double quote */
342                     L"ls \"dpip",
343                     "dpip",
344                     {"dpipe|test", NULL},
345                     L"ls \"dpipe|test\""
346           },
347           {
348                     /* test tab */
349                     L"ls ta",
350                     "ta",
351                     {"tab\ttest", NULL},
352                     L"ls tab\\\ttest "
353           },
354           {
355                     /* test tab inside single quote */
356                     L"ls 'sta",
357                     "sta",
358                     {"stab\ttest", NULL},
359                     L"ls 'stab\ttest'"
360           },
361           {
362                     /* test tab inside double quote */
363                     L"ls \"dta",
364                     "dta",
365                     {"dtab\ttest", NULL},
366                     L"ls \"dtab\ttest\""
367           },
368           {
369                     /* test back tick */
370                     L"ls tic",
371                     "tic",
372                     {"tick`test`", NULL},
373                     L"ls tick\\`test\\` "
374           },
375           {
376                     /* test back tick inside single quote */
377                     L"ls 'stic",
378                     "stic",
379                     {"stick`test`", NULL},
380                     L"ls 'stick`test`'"
381           },
382           {
383                     /* test back tick inside double quote */
384                     L"ls \"dtic",
385                     "dtic",
386                     {"dtick`test`", NULL},
387                     L"ls \"dtick\\`test\\`\""
388           },
389           {
390                     /* test for @ */
391                     L"ls at",
392                     "at",
393                     {"atthe@rate", NULL},
394                     L"ls atthe\\@rate "
395           },
396           {
397                     /* test for @ inside single quote */
398                     L"ls 'sat",
399                     "sat",
400                     {"satthe@rate", NULL},
401                     L"ls 'satthe@rate'"
402           },
403           {
404                     /* test for @ inside double quote */
405                     L"ls \"dat",
406                     "dat",
407                     {"datthe@rate", NULL},
408                     L"ls \"datthe@rate\""
409           },
410           {
411                     /* test ; */
412                     L"ls semi",
413                     "semi",
414                     {"semi;colon;test", NULL},
415                     L"ls semi\\;colon\\;test "
416           },
417           {
418                     /* test ; inside single quote */
419                     L"ls 'ssemi",
420                     "ssemi",
421                     {"ssemi;colon;test", NULL},
422                     L"ls 'ssemi;colon;test'"
423           },
424           {
425                     /* test ; inside double quote */
426                     L"ls \"dsemi",
427                     "dsemi",
428                     {"dsemi;colon;test", NULL},
429                     L"ls \"dsemi;colon;test\""
430           },
431           {
432                     /* test & */
433                     L"ls amp",
434                     "amp",
435                     {"ampers&and", NULL},
436                     L"ls ampers\\&and "
437           },
438           {
439                     /* test & inside single quote */
440                     L"ls 'samp",
441                     "samp",
442                     {"sampers&and", NULL},
443                     L"ls 'sampers&and'"
444           },
445           {
446                     /* test & inside double quote */
447                     L"ls \"damp",
448                     "damp",
449                     {"dampers&and", NULL},
450                     L"ls \"dampers&and\""
451           },
452           {
453                     /* test completion when cursor at \ */
454                     L"ls foo\\",
455                     "foo",
456                     {"foo bar", NULL},
457                     L"ls foo\\ bar "
458           },
459           {
460                     /* test completion when cursor at single quote */
461                     L"ls foo'",
462                     "foo'",
463                     {"foo bar", NULL},
464                     L"ls foo\\ bar "
465           },
466           {
467                     /* test completion when cursor at double quote */
468                     L"ls foo\"",
469                     "foo\"",
470                     {"foo bar", NULL},
471                     L"ls foo\\ bar "
472           },
473           {
474                     /* test multiple completion matches */
475                     L"ls fo",
476                     "fo",
477                     {"foo bar", "foo baz"},
478                     L"ls foo\\ ba"
479           },
480           {
481                     L"ls ba",
482                     "ba",
483                     {"bar <bar>", "bar <baz>"},
484                     L"ls bar\\ \\<ba"
485           }
486 };
487 
488 static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{(";
489 
490 /*
491  * Custom completion function passed to fn_complet, NULLe.
492  * The function returns hardcoded completion matches
493  * based on the test cases present in inputs[] (above)
494  */
495 static char *
mycomplet_func(const char * text,int index)496 mycomplet_func(const char *text, int index)
497 {
498           static int last_index = 0;
499           size_t i = 0;
500           if (last_index == 2) {
501                     last_index = 0;
502                     return NULL;
503           }
504 
505           for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
506                     if (strcmp(text, inputs[i].completion_function_input) == 0) {
507                               if (inputs[i].expanded_text[last_index] != NULL)
508                                         return strdup(inputs[i].expanded_text[last_index++]);
509                               else {
510                                         last_index = 0;
511                                         return NULL;
512                               }
513                     }
514           }
515 
516           return NULL;
517 }
518 
519 int
main(int argc,char ** argv)520 main(int argc, char **argv)
521 {
522           EditLine *el = el_init(argv[0], stdin, stdout, stderr);
523           size_t i;
524           size_t input_len;
525           el_line_t line;
526           wchar_t *buffer = malloc(64 * sizeof(*buffer));
527           if (buffer == NULL)
528                     err(EXIT_FAILURE, "malloc failed");
529 
530           for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
531                     memset(buffer, 0, 64 * sizeof(*buffer));
532                     input_len = wcslen(inputs[i].user_typed_text);
533                     wmemcpy(buffer, inputs[i].user_typed_text, input_len);
534                     buffer[input_len] = 0;
535                     line.buffer = buffer;
536                     line.cursor = line.buffer + input_len ;
537                     line.lastchar = line.cursor - 1;
538                     line.limit = line.buffer + 64 * sizeof(*buffer);
539                     el->el_line = line;
540                     fn_complete(el, mycomplet_func, NULL, break_chars, NULL, NULL, 10, NULL, NULL, NULL, NULL);
541 
542                     /*
543                      * fn_complete would have expanded and escaped the input in el->el_line.buffer.
544                      * We need to assert that it matches with the expected value in our test data
545                      */
546                     printf("User input: %ls\t Expected output: %ls\t Generated output: %ls\n",
547                                         inputs[i].user_typed_text, inputs[i].escaped_output, el->el_line.buffer);
548                     assert(wcscmp(el->el_line.buffer, inputs[i].escaped_output) == 0);
549           }
550           el_end(el);
551           return 0;
552 
553 }
554