554 lines
11 KiB
C
554 lines
11 KiB
C
|
/* $NetBSD: test_filecompletion.c,v 1.5 2019/09/08 05:50:58 abhinav Exp $ */
|
||
|
|
||
|
/*-
|
||
|
* Copyright (c) 2017 Abhinav Upadhyay <abhinav@NetBSD.org>
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
*
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in
|
||
|
* the documentation and/or other materials provided with the
|
||
|
* distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||
|
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
|
* SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <err.h>
|
||
|
#include <stdio.h>
|
||
|
#include <histedit.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <wchar.h>
|
||
|
|
||
|
#include "filecomplete.h"
|
||
|
#include "el.h"
|
||
|
|
||
|
typedef struct {
|
||
|
const wchar_t *user_typed_text; /* The actual text typed by the user on the terminal */
|
||
|
const char *completion_function_input ; /*the text received by fn_filename_completion_function */
|
||
|
const char *expanded_text[2]; /* the value to which completion_function_input should be expanded */
|
||
|
const wchar_t *escaped_output; /* expected escaped value of expanded_text */
|
||
|
} test_input;
|
||
|
|
||
|
static test_input inputs[] = {
|
||
|
{
|
||
|
/* simple test for escaping angular brackets */
|
||
|
L"ls ang",
|
||
|
"ang",
|
||
|
{"ang<ular>test", NULL},
|
||
|
L"ls ang\\<ular\\>test "
|
||
|
},
|
||
|
{
|
||
|
/* test angular bracket inside double quotes: ls "dq_ang */
|
||
|
L"ls \"dq_ang",
|
||
|
"dq_ang",
|
||
|
{"dq_ang<ular>test", NULL},
|
||
|
L"ls \"dq_ang<ular>test\""
|
||
|
},
|
||
|
{
|
||
|
/* test angular bracket inside singlq quotes: ls "sq_ang */
|
||
|
L"ls 'sq_ang",
|
||
|
"sq_ang",
|
||
|
{"sq_ang<ular>test", NULL},
|
||
|
L"ls 'sq_ang<ular>test'"
|
||
|
},
|
||
|
{
|
||
|
/* simple test for backslash */
|
||
|
L"ls back",
|
||
|
"back",
|
||
|
{"backslash\\test", NULL},
|
||
|
L"ls backslash\\\\test "
|
||
|
},
|
||
|
{
|
||
|
/* backslash inside single quotes */
|
||
|
L"ls 'sback",
|
||
|
"sback",
|
||
|
{"sbackslash\\test", NULL},
|
||
|
L"ls 'sbackslash\\test'"
|
||
|
},
|
||
|
{
|
||
|
/* backslash inside double quotes */
|
||
|
L"ls \"dback",
|
||
|
"dback",
|
||
|
{"dbackslash\\test", NULL},
|
||
|
L"ls \"dbackslash\\\\test\""
|
||
|
},
|
||
|
{
|
||
|
/* test braces */
|
||
|
L"ls br",
|
||
|
"br",
|
||
|
{"braces{test}", NULL},
|
||
|
L"ls braces\\{test\\} "
|
||
|
},
|
||
|
{
|
||
|
/* test braces inside single quotes */
|
||
|
L"ls 'sbr",
|
||
|
"sbr",
|
||
|
{"sbraces{test}", NULL},
|
||
|
L"ls 'sbraces{test}'"
|
||
|
},
|
||
|
{
|
||
|
/* test braces inside double quotes */
|
||
|
L"ls \"dbr",
|
||
|
"dbr",
|
||
|
{"dbraces{test}", NULL},
|
||
|
L"ls \"dbraces{test}\""
|
||
|
},
|
||
|
{
|
||
|
/* test dollar */
|
||
|
L"ls doll",
|
||
|
"doll",
|
||
|
{"doll$artest", NULL},
|
||
|
L"ls doll\\$artest "
|
||
|
},
|
||
|
{
|
||
|
/* test dollar inside single quotes */
|
||
|
L"ls 'sdoll",
|
||
|
"sdoll",
|
||
|
{"sdoll$artest", NULL},
|
||
|
L"ls 'sdoll$artest'"
|
||
|
},
|
||
|
{
|
||
|
/* test dollar inside double quotes */
|
||
|
L"ls \"ddoll",
|
||
|
"ddoll",
|
||
|
{"ddoll$artest", NULL},
|
||
|
L"ls \"ddoll\\$artest\""
|
||
|
},
|
||
|
{
|
||
|
/* test equals */
|
||
|
L"ls eq",
|
||
|
"eq",
|
||
|
{"equals==test", NULL},
|
||
|
L"ls equals\\=\\=test "
|
||
|
},
|
||
|
{
|
||
|
/* test equals inside sinqle quotes */
|
||
|
L"ls 'seq",
|
||
|
"seq",
|
||
|
{"sequals==test", NULL},
|
||
|
L"ls 'sequals==test'"
|
||
|
},
|
||
|
{
|
||
|
/* test equals inside double quotes */
|
||
|
L"ls \"deq",
|
||
|
"deq",
|
||
|
{"dequals==test", NULL},
|
||
|
L"ls \"dequals==test\""
|
||
|
},
|
||
|
{
|
||
|
/* test \n */
|
||
|
L"ls new",
|
||
|
"new",
|
||
|
{"new\\nline", NULL},
|
||
|
L"ls new\\\\nline "
|
||
|
},
|
||
|
{
|
||
|
/* test \n inside single quotes */
|
||
|
L"ls 'snew",
|
||
|
"snew",
|
||
|
{"snew\nline", NULL},
|
||
|
L"ls 'snew\nline'"
|
||
|
},
|
||
|
{
|
||
|
/* test \n inside double quotes */
|
||
|
L"ls \"dnew",
|
||
|
"dnew",
|
||
|
{"dnew\nline", NULL},
|
||
|
L"ls \"dnew\nline\""
|
||
|
},
|
||
|
{
|
||
|
/* test single space */
|
||
|
L"ls spac",
|
||
|
"spac",
|
||
|
{"space test", NULL},
|
||
|
L"ls space\\ test "
|
||
|
},
|
||
|
{
|
||
|
/* test single space inside singlq quotes */
|
||
|
L"ls 's_spac",
|
||
|
"s_spac",
|
||
|
{"s_space test", NULL},
|
||
|
L"ls 's_space test'"
|
||
|
},
|
||
|
{
|
||
|
/* test single space inside double quotes */
|
||
|
L"ls \"d_spac",
|
||
|
"d_spac",
|
||
|
{"d_space test", NULL},
|
||
|
L"ls \"d_space test\""
|
||
|
},
|
||
|
{
|
||
|
/* test multiple spaces */
|
||
|
L"ls multi",
|
||
|
"multi",
|
||
|
{"multi space test", NULL},
|
||
|
L"ls multi\\ space\\ \\ test "
|
||
|
},
|
||
|
{
|
||
|
/* test multiple spaces inside single quotes */
|
||
|
L"ls 's_multi",
|
||
|
"s_multi",
|
||
|
{"s_multi space test", NULL},
|
||
|
L"ls 's_multi space test'"
|
||
|
},
|
||
|
{
|
||
|
/* test multiple spaces inside double quotes */
|
||
|
L"ls \"d_multi",
|
||
|
"d_multi",
|
||
|
{"d_multi space test", NULL},
|
||
|
L"ls \"d_multi space test\""
|
||
|
},
|
||
|
{
|
||
|
/* test double quotes */
|
||
|
L"ls doub",
|
||
|
"doub",
|
||
|
{"doub\"quotes", NULL},
|
||
|
L"ls doub\\\"quotes "
|
||
|
},
|
||
|
{
|
||
|
/* test double quotes inside single quotes */
|
||
|
L"ls 's_doub",
|
||
|
"s_doub",
|
||
|
{"s_doub\"quotes", NULL},
|
||
|
L"ls 's_doub\"quotes'"
|
||
|
},
|
||
|
{
|
||
|
/* test double quotes inside double quotes */
|
||
|
L"ls \"d_doub",
|
||
|
"d_doub",
|
||
|
{"d_doub\"quotes", NULL},
|
||
|
L"ls \"d_doub\\\"quotes\""
|
||
|
},
|
||
|
{
|
||
|
/* test multiple double quotes */
|
||
|
L"ls mud",
|
||
|
"mud",
|
||
|
{"mud\"qu\"otes\"", NULL},
|
||
|
L"ls mud\\\"qu\\\"otes\\\" "
|
||
|
},
|
||
|
{
|
||
|
/* test multiple double quotes inside single quotes */
|
||
|
L"ls 'smud",
|
||
|
"smud",
|
||
|
{"smud\"qu\"otes\"", NULL},
|
||
|
L"ls 'smud\"qu\"otes\"'"
|
||
|
},
|
||
|
{
|
||
|
/* test multiple double quotes inside double quotes */
|
||
|
L"ls \"dmud",
|
||
|
"dmud",
|
||
|
{"dmud\"qu\"otes\"", NULL},
|
||
|
L"ls \"dmud\\\"qu\\\"otes\\\"\""
|
||
|
},
|
||
|
{
|
||
|
/* test one single quote */
|
||
|
L"ls sing",
|
||
|
"sing",
|
||
|
{"single'quote", NULL},
|
||
|
L"ls single\\'quote "
|
||
|
},
|
||
|
{
|
||
|
/* test one single quote inside single quote */
|
||
|
L"ls 'ssing",
|
||
|
"ssing",
|
||
|
{"ssingle'quote", NULL},
|
||
|
L"ls 'ssingle'\\''quote'"
|
||
|
},
|
||
|
{
|
||
|
/* test one single quote inside double quote */
|
||
|
L"ls \"dsing",
|
||
|
"dsing",
|
||
|
{"dsingle'quote", NULL},
|
||
|
L"ls \"dsingle'quote\""
|
||
|
},
|
||
|
{
|
||
|
/* test multiple single quotes */
|
||
|
L"ls mu_sing",
|
||
|
"mu_sing",
|
||
|
{"mu_single''quotes''", NULL},
|
||
|
L"ls mu_single\\'\\'quotes\\'\\' "
|
||
|
},
|
||
|
{
|
||
|
/* test multiple single quotes inside single quote */
|
||
|
L"ls 'smu_sing",
|
||
|
"smu_sing",
|
||
|
{"smu_single''quotes''", NULL},
|
||
|
L"ls 'smu_single'\\'''\\''quotes'\\\'''\\'''"
|
||
|
},
|
||
|
{
|
||
|
/* test multiple single quotes inside double quote */
|
||
|
L"ls \"dmu_sing",
|
||
|
"dmu_sing",
|
||
|
{"dmu_single''quotes''", NULL},
|
||
|
L"ls \"dmu_single''quotes''\""
|
||
|
},
|
||
|
{
|
||
|
/* test parenthesis */
|
||
|
L"ls paren",
|
||
|
"paren",
|
||
|
{"paren(test)", NULL},
|
||
|
L"ls paren\\(test\\) "
|
||
|
},
|
||
|
{
|
||
|
/* test parenthesis inside single quote */
|
||
|
L"ls 'sparen",
|
||
|
"sparen",
|
||
|
{"sparen(test)", NULL},
|
||
|
L"ls 'sparen(test)'"
|
||
|
},
|
||
|
{
|
||
|
/* test parenthesis inside double quote */
|
||
|
L"ls \"dparen",
|
||
|
"dparen",
|
||
|
{"dparen(test)", NULL},
|
||
|
L"ls \"dparen(test)\""
|
||
|
},
|
||
|
{
|
||
|
/* test pipe */
|
||
|
L"ls pip",
|
||
|
"pip",
|
||
|
{"pipe|test", NULL},
|
||
|
L"ls pipe\\|test "
|
||
|
},
|
||
|
{
|
||
|
/* test pipe inside single quote */
|
||
|
L"ls 'spip",
|
||
|
"spip",
|
||
|
{"spipe|test", NULL},
|
||
|
L"ls 'spipe|test'",
|
||
|
},
|
||
|
{
|
||
|
/* test pipe inside double quote */
|
||
|
L"ls \"dpip",
|
||
|
"dpip",
|
||
|
{"dpipe|test", NULL},
|
||
|
L"ls \"dpipe|test\""
|
||
|
},
|
||
|
{
|
||
|
/* test tab */
|
||
|
L"ls ta",
|
||
|
"ta",
|
||
|
{"tab\ttest", NULL},
|
||
|
L"ls tab\\\ttest "
|
||
|
},
|
||
|
{
|
||
|
/* test tab inside single quote */
|
||
|
L"ls 'sta",
|
||
|
"sta",
|
||
|
{"stab\ttest", NULL},
|
||
|
L"ls 'stab\ttest'"
|
||
|
},
|
||
|
{
|
||
|
/* test tab inside double quote */
|
||
|
L"ls \"dta",
|
||
|
"dta",
|
||
|
{"dtab\ttest", NULL},
|
||
|
L"ls \"dtab\ttest\""
|
||
|
},
|
||
|
{
|
||
|
/* test back tick */
|
||
|
L"ls tic",
|
||
|
"tic",
|
||
|
{"tick`test`", NULL},
|
||
|
L"ls tick\\`test\\` "
|
||
|
},
|
||
|
{
|
||
|
/* test back tick inside single quote */
|
||
|
L"ls 'stic",
|
||
|
"stic",
|
||
|
{"stick`test`", NULL},
|
||
|
L"ls 'stick`test`'"
|
||
|
},
|
||
|
{
|
||
|
/* test back tick inside double quote */
|
||
|
L"ls \"dtic",
|
||
|
"dtic",
|
||
|
{"dtick`test`", NULL},
|
||
|
L"ls \"dtick\\`test\\`\""
|
||
|
},
|
||
|
{
|
||
|
/* test for @ */
|
||
|
L"ls at",
|
||
|
"at",
|
||
|
{"atthe@rate", NULL},
|
||
|
L"ls atthe\\@rate "
|
||
|
},
|
||
|
{
|
||
|
/* test for @ inside single quote */
|
||
|
L"ls 'sat",
|
||
|
"sat",
|
||
|
{"satthe@rate", NULL},
|
||
|
L"ls 'satthe@rate'"
|
||
|
},
|
||
|
{
|
||
|
/* test for @ inside double quote */
|
||
|
L"ls \"dat",
|
||
|
"dat",
|
||
|
{"datthe@rate", NULL},
|
||
|
L"ls \"datthe@rate\""
|
||
|
},
|
||
|
{
|
||
|
/* test ; */
|
||
|
L"ls semi",
|
||
|
"semi",
|
||
|
{"semi;colon;test", NULL},
|
||
|
L"ls semi\\;colon\\;test "
|
||
|
},
|
||
|
{
|
||
|
/* test ; inside single quote */
|
||
|
L"ls 'ssemi",
|
||
|
"ssemi",
|
||
|
{"ssemi;colon;test", NULL},
|
||
|
L"ls 'ssemi;colon;test'"
|
||
|
},
|
||
|
{
|
||
|
/* test ; inside double quote */
|
||
|
L"ls \"dsemi",
|
||
|
"dsemi",
|
||
|
{"dsemi;colon;test", NULL},
|
||
|
L"ls \"dsemi;colon;test\""
|
||
|
},
|
||
|
{
|
||
|
/* test & */
|
||
|
L"ls amp",
|
||
|
"amp",
|
||
|
{"ampers&and", NULL},
|
||
|
L"ls ampers\\&and "
|
||
|
},
|
||
|
{
|
||
|
/* test & inside single quote */
|
||
|
L"ls 'samp",
|
||
|
"samp",
|
||
|
{"sampers&and", NULL},
|
||
|
L"ls 'sampers&and'"
|
||
|
},
|
||
|
{
|
||
|
/* test & inside double quote */
|
||
|
L"ls \"damp",
|
||
|
"damp",
|
||
|
{"dampers&and", NULL},
|
||
|
L"ls \"dampers&and\""
|
||
|
},
|
||
|
{
|
||
|
/* test completion when cursor at \ */
|
||
|
L"ls foo\\",
|
||
|
"foo",
|
||
|
{"foo bar", NULL},
|
||
|
L"ls foo\\ bar "
|
||
|
},
|
||
|
{
|
||
|
/* test completion when cursor at single quote */
|
||
|
L"ls foo'",
|
||
|
"foo'",
|
||
|
{"foo bar", NULL},
|
||
|
L"ls foo\\ bar "
|
||
|
},
|
||
|
{
|
||
|
/* test completion when cursor at double quote */
|
||
|
L"ls foo\"",
|
||
|
"foo\"",
|
||
|
{"foo bar", NULL},
|
||
|
L"ls foo\\ bar "
|
||
|
},
|
||
|
{
|
||
|
/* test multiple completion matches */
|
||
|
L"ls fo",
|
||
|
"fo",
|
||
|
{"foo bar", "foo baz"},
|
||
|
L"ls foo\\ ba"
|
||
|
},
|
||
|
{
|
||
|
L"ls ba",
|
||
|
"ba",
|
||
|
{"bar <bar>", "bar <baz>"},
|
||
|
L"ls bar\\ \\<ba"
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{(";
|
||
|
|
||
|
/*
|
||
|
* Custom completion function passed to fn_complet, NULLe.
|
||
|
* The function returns hardcoded completion matches
|
||
|
* based on the test cases present in inputs[] (above)
|
||
|
*/
|
||
|
static char *
|
||
|
mycomplet_func(const char *text, int index)
|
||
|
{
|
||
|
static int last_index = 0;
|
||
|
size_t i = 0;
|
||
|
if (last_index == 2) {
|
||
|
last_index = 0;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
|
||
|
if (strcmp(text, inputs[i].completion_function_input) == 0) {
|
||
|
if (inputs[i].expanded_text[last_index] != NULL)
|
||
|
return strdup(inputs[i].expanded_text[last_index++]);
|
||
|
else {
|
||
|
last_index = 0;
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
EditLine *el = el_init(argv[0], stdin, stdout, stderr);
|
||
|
size_t i;
|
||
|
size_t input_len;
|
||
|
el_line_t line;
|
||
|
wchar_t *buffer = malloc(64 * sizeof(*buffer));
|
||
|
if (buffer == NULL)
|
||
|
err(EXIT_FAILURE, "malloc failed");
|
||
|
|
||
|
for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) {
|
||
|
memset(buffer, 0, 64 * sizeof(*buffer));
|
||
|
input_len = wcslen(inputs[i].user_typed_text);
|
||
|
wmemcpy(buffer, inputs[i].user_typed_text, input_len);
|
||
|
buffer[input_len] = 0;
|
||
|
line.buffer = buffer;
|
||
|
line.cursor = line.buffer + input_len ;
|
||
|
line.lastchar = line.cursor - 1;
|
||
|
line.limit = line.buffer + 64 * sizeof(*buffer);
|
||
|
el->el_line = line;
|
||
|
fn_complete(el, mycomplet_func, NULL, break_chars, NULL, NULL, 10, NULL, NULL, NULL, NULL);
|
||
|
|
||
|
/*
|
||
|
* fn_complete would have expanded and escaped the input in el->el_line.buffer.
|
||
|
* We need to assert that it matches with the expected value in our test data
|
||
|
*/
|
||
|
printf("User input: %ls\t Expected output: %ls\t Generated output: %ls\n",
|
||
|
inputs[i].user_typed_text, inputs[i].escaped_output, el->el_line.buffer);
|
||
|
assert(wcscmp(el->el_line.buffer, inputs[i].escaped_output) == 0);
|
||
|
}
|
||
|
el_end(el);
|
||
|
return 0;
|
||
|
|
||
|
}
|