xref: /netbsd-src/lib/libedit/TEST/test_filecompletion.c (revision deb6f0161a9109e7de9b519dc8dfb9478668dcdd)
1 /*	$NetBSD: test_filecompletion.c,v 1.3 2018/05/04 16:39:15 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; /* 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",
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",
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",
72 		L"ls 'sq_ang<ular>test' "
73 	},
74 	{
75 		/* simple test for backslash */
76 		L"ls back",
77 		"back",
78 		"backslash\\test",
79 		L"ls backslash\\\\test "
80 	},
81 	{
82 		/* backslash inside single quotes */
83 		L"ls 'sback",
84 		"sback",
85 		"sbackslash\\test",
86 		L"ls 'sbackslash\\test' "
87 	},
88 	{
89 		/* backslash inside double quotes */
90 		L"ls \"dback",
91 		"dback",
92 		"dbackslash\\test",
93 		L"ls \"dbackslash\\\\test\" "
94 	},
95 	{
96 		/* test braces */
97 		L"ls br",
98 		"br",
99 		"braces{test}",
100 		L"ls braces\\{test\\} "
101 	},
102 	{
103 		/* test braces inside single quotes */
104 		L"ls 'sbr",
105 		"sbr",
106 		"sbraces{test}",
107 		L"ls 'sbraces{test}' "
108 	},
109 	{
110 		/* test braces inside double quotes */
111 		L"ls \"dbr",
112 		"dbr",
113 		"dbraces{test}",
114 		L"ls \"dbraces{test}\" "
115 	},
116 	{
117 		/* test dollar */
118 		L"ls doll",
119 		"doll",
120 		"doll$artest",
121 		L"ls doll\\$artest "
122 	},
123 	{
124 		/* test dollar inside single quotes */
125 		L"ls 'sdoll",
126 		"sdoll",
127 		"sdoll$artest",
128 		L"ls 'sdoll$artest' "
129 	},
130 	{
131 		/* test dollar inside double quotes */
132 		L"ls \"ddoll",
133 		"ddoll",
134 		"ddoll$artest",
135 		L"ls \"ddoll\\$artest\" "
136 	},
137 	{
138 		/* test equals */
139 		L"ls eq",
140 		"eq",
141 		"equals==test",
142 		L"ls equals\\=\\=test "
143 	},
144 	{
145 		/* test equals inside sinqle quotes */
146 		L"ls 'seq",
147 		"seq",
148 		"sequals==test",
149 		L"ls 'sequals==test' "
150 	},
151 	{
152 		/* test equals inside double quotes */
153 		L"ls \"deq",
154 		"deq",
155 		"dequals==test",
156 		L"ls \"dequals==test\" "
157 	},
158 	{
159 		/* test \n */
160 		L"ls new",
161 		"new",
162 		"new\\nline",
163 		L"ls new\\\\nline "
164 	},
165 	{
166 		/* test \n inside single quotes */
167 		L"ls 'snew",
168 		"snew",
169 		"snew\nline",
170 		L"ls 'snew\nline' "
171 	},
172 	{
173 		/* test \n inside double quotes */
174 		L"ls \"dnew",
175 		"dnew",
176 		"dnew\nline",
177 		L"ls \"dnew\nline\" "
178 	},
179 	{
180 		/* test single space */
181 		L"ls spac",
182 		"spac",
183 		"space test",
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",
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",
198 		L"ls \"d_space test\" "
199 	},
200 	{
201 		/* test multiple spaces */
202 		L"ls multi",
203 		"multi",
204 		"multi space  test",
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",
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",
219 		L"ls \"d_multi space  test\" "
220 	},
221 	{
222 		/* test double quotes */
223 		L"ls doub",
224 		"doub",
225 		"doub\"quotes",
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",
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",
240 		L"ls \"d_doub\\\"quotes\" "
241 	},
242 	{
243 		/* test multiple double quotes */
244 		L"ls mud",
245 		"mud",
246 		"mud\"qu\"otes\"",
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\"",
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\"",
261 		L"ls \"dmud\\\"qu\\\"otes\\\"\" "
262 	},
263 	{
264 		/* test one single quote */
265 		L"ls sing",
266 		"sing",
267 		"single'quote",
268 		L"ls single\\'quote "
269 	},
270 	{
271 		/* test one single quote inside single quote */
272 		L"ls 'ssing",
273 		"ssing",
274 		"ssingle'quote",
275 		L"ls 'ssingle'\\''quote' "
276 	},
277 	{
278 		/* test one single quote inside double quote */
279 		L"ls \"dsing",
280 		"dsing",
281 		"dsingle'quote",
282 		L"ls \"dsingle'quote\" "
283 	},
284 	{
285 		/* test multiple single quotes */
286 		L"ls mu_sing",
287 		"mu_sing",
288 		"mu_single''quotes''",
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''",
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''",
303 		L"ls \"dmu_single''quotes''\" "
304 	},
305 	{
306 		/* test parenthesis */
307 		L"ls paren",
308 		"paren",
309 		"paren(test)",
310 		L"ls paren\\(test\\) "
311 	},
312 	{
313 		/* test parenthesis inside single quote */
314 		L"ls 'sparen",
315 		"sparen",
316 		"sparen(test)",
317 		L"ls 'sparen(test)' "
318 	},
319 	{
320 		/* test parenthesis inside double quote */
321 		L"ls \"dparen",
322 		"dparen",
323 		"dparen(test)",
324 		L"ls \"dparen(test)\" "
325 	},
326 	{
327 		/* test pipe */
328 		L"ls pip",
329 		"pip",
330 		"pipe|test",
331 		L"ls pipe\\|test "
332 	},
333 	{
334 		/* test pipe inside single quote */
335 		L"ls 'spip",
336 		"spip",
337 		"spipe|test",
338 		L"ls 'spipe|test' ",
339 	},
340 	{
341 		/* test pipe inside double quote */
342 		L"ls \"dpip",
343 		"dpip",
344 		"dpipe|test",
345 		L"ls \"dpipe|test\" "
346 	},
347 	{
348 		/* test tab */
349 		L"ls ta",
350 		"ta",
351 		"tab\ttest",
352 		L"ls tab\\\ttest "
353 	},
354 	{
355 		/* test tab inside single quote */
356 		L"ls 'sta",
357 		"sta",
358 		"stab\ttest",
359 		L"ls 'stab\ttest' "
360 	},
361 	{
362 		/* test tab inside double quote */
363 		L"ls \"dta",
364 		"dta",
365 		"dtab\ttest",
366 		L"ls \"dtab\ttest\" "
367 	},
368 	{
369 		/* test back tick */
370 		L"ls tic",
371 		"tic",
372 		"tick`test`",
373 		L"ls tick\\`test\\` "
374 	},
375 	{
376 		/* test back tick inside single quote */
377 		L"ls 'stic",
378 		"stic",
379 		"stick`test`",
380 		L"ls 'stick`test`' "
381 	},
382 	{
383 		/* test back tick inside double quote */
384 		L"ls \"dtic",
385 		"dtic",
386 		"dtick`test`",
387 		L"ls \"dtick\\`test\\`\" "
388 	},
389 	{
390 		/* test for @ */
391 		L"ls at",
392 		"at",
393 		"atthe@rate",
394 		L"ls atthe\\@rate "
395 	},
396 	{
397 		/* test for @ inside single quote */
398 		L"ls 'sat",
399 		"sat",
400 		"satthe@rate",
401 		L"ls 'satthe@rate' "
402 	},
403 	{
404 		/* test for @ inside double quote */
405 		L"ls \"dat",
406 		"dat",
407 		"datthe@rate",
408 		L"ls \"datthe@rate\" "
409 	},
410 	{
411 		/* test ; */
412 		L"ls semi",
413 		"semi",
414 		"semi;colon;test",
415 		L"ls semi\\;colon\\;test "
416 	},
417 	{
418 		/* test ; inside single quote */
419 		L"ls 'ssemi",
420 		"ssemi",
421 		"ssemi;colon;test",
422 		L"ls 'ssemi;colon;test' "
423 	},
424 	{
425 		/* test ; inside double quote */
426 		L"ls \"dsemi",
427 		"dsemi",
428 		"dsemi;colon;test",
429 		L"ls \"dsemi;colon;test\" "
430 	},
431 	{
432 		/* test & */
433 		L"ls amp",
434 		"amp",
435 		"ampers&and",
436 		L"ls ampers\\&and "
437 	},
438 	{
439 		/* test & inside single quote */
440 		L"ls 'samp",
441 		"samp",
442 		"sampers&and",
443 		L"ls 'sampers&and' "
444 	},
445 	{
446 		/* test & inside double quote */
447 		L"ls \"damp",
448 		"damp",
449 		"dampers&and",
450 		L"ls \"dampers&and\" "
451 	},
452 	{
453 		/* test completion when cursor at \ */
454 		L"ls foo\\",
455 		"foo",
456 		"foo bar",
457 		L"ls foo\\ bar "
458 	},
459 	{
460 		/* test completion when cursor at single quote */
461 		L"ls foo'",
462 		"foo",
463 		"foo bar",
464 		L"ls foo\\ bar "
465 	},
466 	{
467 		/* test completion when cursor at double quote */
468 		L"ls foo\"",
469 		"foo",
470 		"foo bar",
471 		L"ls foo\\ bar "
472 	}
473 };
474 
475 static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{(";
476 
477 /*
478  * Custom completion function passed to fn_complete.
479  * The function returns hardcoded completion matches
480  * based on the test cases present in inputs[] (above)
481  */
482 static char *
483 mycomplet_func(const char *text, int index)
484 {
485 	static char *last_input = NULL;
486 	size_t i = 0;
487 	if (last_input && strcmp(last_input, text) == 0) {
488 		free(last_input);
489 		last_input = NULL;
490 		return NULL;
491 	}
492 	last_input = strdup(text);
493 
494 	for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
495 		if (strcmp(text, inputs[i].completion_function_input) == 0)
496 			return strdup(inputs[i].expanded_text);
497 	}
498 
499 	return NULL;
500 }
501 
502 int
503 main(int argc, char **argv)
504 {
505 	EditLine *el = el_init(argv[0], stdin, stdout, stderr);
506 	size_t i;
507 	size_t input_len;
508 	el_line_t line;
509 	wchar_t *buffer = malloc(64 * sizeof(*buffer));
510 	if (buffer == NULL)
511 		err(EXIT_FAILURE, "malloc failed");
512 
513 	for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
514 		memset(buffer, 0, 64 * sizeof(*buffer));
515 		input_len = wcslen(inputs[i].user_typed_text);
516 		wmemcpy(buffer, inputs[i].user_typed_text, input_len);
517 		buffer[input_len] = 0;
518 		line.buffer = buffer;
519 		line.cursor = line.buffer + input_len ;
520 		line.lastchar = line.cursor - 1;
521 		line.limit = line.buffer + 64 * sizeof(*buffer);
522 		el->el_line = line;
523 		fn_complete(el, mycomplet_func, NULL, break_chars, NULL, NULL, 10, NULL, NULL, NULL, NULL);
524 
525 		/*
526 		 * fn_complete would have expanded and escaped the input in el->el_line.buffer.
527 		 * We need to assert that it matches with the expected value in our test data
528 		 */
529 		printf("User input: %ls\t Expected output: %ls\t Generated output: %ls\n",
530 				inputs[i].user_typed_text, inputs[i].escaped_output, el->el_line.buffer);
531 		assert(wcscmp(el->el_line.buffer, inputs[i].escaped_output) == 0);
532 	}
533 	el_end(el);
534 	return 0;
535 
536 }
537