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