/* $NetBSD: test_filecompletion.c,v 1.4 2019/03/31 03:04:57 abhinav Exp $ */ /*- * Copyright (c) 2017 Abhinav Upadhyay * 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 #include #include #include #include #include #include #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", {"angtest", NULL}, L"ls ang\\test " }, { /* test angular bracket inside double quotes: ls "dq_ang */ L"ls \"dq_ang", "dq_ang", {"dq_angtest", NULL}, L"ls \"dq_angtest\" " }, { /* test angular bracket inside singlq quotes: ls "sq_ang */ L"ls 'sq_ang", "sq_ang", {"sq_angtest", NULL}, L"ls 'sq_angtest' " }, { /* 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 "}, L"ls bar\\ \\<=;|&{("; /* * 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; }