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