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
45typedef 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
52static 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
488static 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 */
495static char *
496mycomplet_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
519int
520main(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