/* $NetBSD: test_filecompletion.c,v 1.2 2017/10/15 19:17:30 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; /* 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", L"ls ang\\test " }, { /* test angular bracket inside double quotes: ls "dq_ang */ L"ls \"dq_ang", "dq_ang", "dq_angtest", L"ls \"dq_angtest\" " }, { /* test angular bracket inside singlq quotes: ls "sq_ang */ L"ls 'sq_ang", "sq_ang", "sq_angtest", L"ls 'sq_angtest' " }, { /* simple test for backslash */ L"ls back", "back", "backslash\\test", L"ls backslash\\\\test " }, { /* backslash inside single quotes */ L"ls 'sback", "sback", "sbackslash\\test", L"ls 'sbackslash\\test' " }, { /* backslash inside double quotes */ L"ls \"dback", "dback", "dbackslash\\test", L"ls \"dbackslash\\\\test\" " }, { /* test braces */ L"ls br", "br", "braces{test}", L"ls braces\\{test\\} " }, { /* test braces inside single quotes */ L"ls 'sbr", "sbr", "sbraces{test}", L"ls 'sbraces{test}' " }, { /* test braces inside double quotes */ L"ls \"dbr", "dbr", "dbraces{test}", L"ls \"dbraces{test}\" " }, { /* test dollar */ L"ls doll", "doll", "doll$artest", L"ls doll\\$artest " }, { /* test dollar inside single quotes */ L"ls 'sdoll", "sdoll", "sdoll$artest", L"ls 'sdoll$artest' " }, { /* test dollar inside double quotes */ L"ls \"ddoll", "ddoll", "ddoll$artest", L"ls \"ddoll\\$artest\" " }, { /* test equals */ L"ls eq", "eq", "equals==test", L"ls equals\\=\\=test " }, { /* test equals inside sinqle quotes */ L"ls 'seq", "seq", "sequals==test", L"ls 'sequals==test' " }, { /* test equals inside double quotes */ L"ls \"deq", "deq", "dequals==test", L"ls \"dequals==test\" " }, { /* test \n */ L"ls new", "new", "new\\nline", L"ls new\\\\nline " }, { /* test \n inside single quotes */ L"ls 'snew", "snew", "snew\nline", L"ls 'snew\nline' " }, { /* test \n inside double quotes */ L"ls \"dnew", "dnew", "dnew\nline", L"ls \"dnew\nline\" " }, { /* test single space */ L"ls spac", "spac", "space test", L"ls space\\ test " }, { /* test single space inside singlq quotes */ L"ls 's_spac", "s_spac", "s_space test", L"ls 's_space test' " }, { /* test single space inside double quotes */ L"ls \"d_spac", "d_spac", "d_space test", L"ls \"d_space test\" " }, { /* test multiple spaces */ L"ls multi", "multi", "multi space test", L"ls multi\\ space\\ \\ test " }, { /* test multiple spaces inside single quotes */ L"ls 's_multi", "s_multi", "s_multi space test", L"ls 's_multi space test' " }, { /* test multiple spaces inside double quotes */ L"ls \"d_multi", "d_multi", "d_multi space test", L"ls \"d_multi space test\" " }, { /* test double quotes */ L"ls doub", "doub", "doub\"quotes", L"ls doub\\\"quotes " }, { /* test double quotes inside single quotes */ L"ls 's_doub", "s_doub", "s_doub\"quotes", L"ls 's_doub\"quotes' " }, { /* test double quotes inside double quotes */ L"ls \"d_doub", "d_doub", "d_doub\"quotes", L"ls \"d_doub\\\"quotes\" " }, { /* test multiple double quotes */ L"ls mud", "mud", "mud\"qu\"otes\"", L"ls mud\\\"qu\\\"otes\\\" " }, { /* test multiple double quotes inside single quotes */ L"ls 'smud", "smud", "smud\"qu\"otes\"", L"ls 'smud\"qu\"otes\"' " }, { /* test multiple double quotes inside double quotes */ L"ls \"dmud", "dmud", "dmud\"qu\"otes\"", L"ls \"dmud\\\"qu\\\"otes\\\"\" " }, { /* test one single quote */ L"ls sing", "sing", "single'quote", L"ls single\\'quote " }, { /* test one single quote inside single quote */ L"ls 'ssing", "ssing", "ssingle'quote", L"ls 'ssingle'\\''quote' " }, { /* test one single quote inside double quote */ L"ls \"dsing", "dsing", "dsingle'quote", L"ls \"dsingle'quote\" " }, { /* test multiple single quotes */ L"ls mu_sing", "mu_sing", "mu_single''quotes''", L"ls mu_single\\'\\'quotes\\'\\' " }, { /* test multiple single quotes inside single quote */ L"ls 'smu_sing", "smu_sing", "smu_single''quotes''", L"ls 'smu_single'\\'''\\''quotes'\\\'''\\''' " }, { /* test multiple single quotes inside double quote */ L"ls \"dmu_sing", "dmu_sing", "dmu_single''quotes''", L"ls \"dmu_single''quotes''\" " }, { /* test parenthesis */ L"ls paren", "paren", "paren(test)", L"ls paren\\(test\\) " }, { /* test parenthesis inside single quote */ L"ls 'sparen", "sparen", "sparen(test)", L"ls 'sparen(test)' " }, { /* test parenthesis inside double quote */ L"ls \"dparen", "dparen", "dparen(test)", L"ls \"dparen(test)\" " }, { /* test pipe */ L"ls pip", "pip", "pipe|test", L"ls pipe\\|test " }, { /* test pipe inside single quote */ L"ls 'spip", "spip", "spipe|test", L"ls 'spipe|test' ", }, { /* test pipe inside double quote */ L"ls \"dpip", "dpip", "dpipe|test", L"ls \"dpipe|test\" " }, { /* test tab */ L"ls ta", "ta", "tab\ttest", L"ls tab\\\ttest " }, { /* test tab inside single quote */ L"ls 'sta", "sta", "stab\ttest", L"ls 'stab\ttest' " }, { /* test tab inside double quote */ L"ls \"dta", "dta", "dtab\ttest", L"ls \"dtab\ttest\" " }, { /* test back tick */ L"ls tic", "tic", "tick`test`", L"ls tick\\`test\\` " }, { /* test back tick inside single quote */ L"ls 'stic", "stic", "stick`test`", L"ls 'stick`test`' " }, { /* test back tick inside double quote */ L"ls \"dtic", "dtic", "dtick`test`", L"ls \"dtick\\`test\\`\" " }, { /* test for @ */ L"ls at", "at", "atthe@rate", L"ls atthe\\@rate " }, { /* test for @ inside single quote */ L"ls 'sat", "sat", "satthe@rate", L"ls 'satthe@rate' " }, { /* test for @ inside double quote */ L"ls \"dat", "dat", "datthe@rate", L"ls \"datthe@rate\" " }, { /* test ; */ L"ls semi", "semi", "semi;colon;test", L"ls semi\\;colon\\;test " }, { /* test ; inside single quote */ L"ls 'ssemi", "ssemi", "ssemi;colon;test", L"ls 'ssemi;colon;test' " }, { /* test ; inside double quote */ L"ls \"dsemi", "dsemi", "dsemi;colon;test", L"ls \"dsemi;colon;test\" " }, { /* test & */ L"ls amp", "amp", "ampers&and", L"ls ampers\\&and " }, { /* test & inside single quote */ L"ls 'samp", "samp", "sampers&and", L"ls 'sampers&and' " }, { /* test & inside double quote */ L"ls \"damp", "damp", "dampers&and", L"ls \"dampers&and\" " }, }; static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{("; /* * Custom completion function passed to fn_complete. * 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 char *last_input = NULL; size_t i = 0; if (last_input && strcmp(last_input, text) == 0) { free(last_input); last_input = NULL; return NULL; } last_input = strdup(text); for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) { if (strcmp(text, inputs[i].completion_function_input) == 0) return strdup(inputs[i].expanded_text); } 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; }