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