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