1 /* $NetBSD: test_filecompletion.c,v 1.3 2018/05/04 16:39:15 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; /* 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", 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", 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", 72 L"ls 'sq_ang<ular>test' " 73 }, 74 { 75 /* simple test for backslash */ 76 L"ls back", 77 "back", 78 "backslash\\test", 79 L"ls backslash\\\\test " 80 }, 81 { 82 /* backslash inside single quotes */ 83 L"ls 'sback", 84 "sback", 85 "sbackslash\\test", 86 L"ls 'sbackslash\\test' " 87 }, 88 { 89 /* backslash inside double quotes */ 90 L"ls \"dback", 91 "dback", 92 "dbackslash\\test", 93 L"ls \"dbackslash\\\\test\" " 94 }, 95 { 96 /* test braces */ 97 L"ls br", 98 "br", 99 "braces{test}", 100 L"ls braces\\{test\\} " 101 }, 102 { 103 /* test braces inside single quotes */ 104 L"ls 'sbr", 105 "sbr", 106 "sbraces{test}", 107 L"ls 'sbraces{test}' " 108 }, 109 { 110 /* test braces inside double quotes */ 111 L"ls \"dbr", 112 "dbr", 113 "dbraces{test}", 114 L"ls \"dbraces{test}\" " 115 }, 116 { 117 /* test dollar */ 118 L"ls doll", 119 "doll", 120 "doll$artest", 121 L"ls doll\\$artest " 122 }, 123 { 124 /* test dollar inside single quotes */ 125 L"ls 'sdoll", 126 "sdoll", 127 "sdoll$artest", 128 L"ls 'sdoll$artest' " 129 }, 130 { 131 /* test dollar inside double quotes */ 132 L"ls \"ddoll", 133 "ddoll", 134 "ddoll$artest", 135 L"ls \"ddoll\\$artest\" " 136 }, 137 { 138 /* test equals */ 139 L"ls eq", 140 "eq", 141 "equals==test", 142 L"ls equals\\=\\=test " 143 }, 144 { 145 /* test equals inside sinqle quotes */ 146 L"ls 'seq", 147 "seq", 148 "sequals==test", 149 L"ls 'sequals==test' " 150 }, 151 { 152 /* test equals inside double quotes */ 153 L"ls \"deq", 154 "deq", 155 "dequals==test", 156 L"ls \"dequals==test\" " 157 }, 158 { 159 /* test \n */ 160 L"ls new", 161 "new", 162 "new\\nline", 163 L"ls new\\\\nline " 164 }, 165 { 166 /* test \n inside single quotes */ 167 L"ls 'snew", 168 "snew", 169 "snew\nline", 170 L"ls 'snew\nline' " 171 }, 172 { 173 /* test \n inside double quotes */ 174 L"ls \"dnew", 175 "dnew", 176 "dnew\nline", 177 L"ls \"dnew\nline\" " 178 }, 179 { 180 /* test single space */ 181 L"ls spac", 182 "spac", 183 "space test", 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", 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", 198 L"ls \"d_space test\" " 199 }, 200 { 201 /* test multiple spaces */ 202 L"ls multi", 203 "multi", 204 "multi space test", 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", 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", 219 L"ls \"d_multi space test\" " 220 }, 221 { 222 /* test double quotes */ 223 L"ls doub", 224 "doub", 225 "doub\"quotes", 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", 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", 240 L"ls \"d_doub\\\"quotes\" " 241 }, 242 { 243 /* test multiple double quotes */ 244 L"ls mud", 245 "mud", 246 "mud\"qu\"otes\"", 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\"", 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\"", 261 L"ls \"dmud\\\"qu\\\"otes\\\"\" " 262 }, 263 { 264 /* test one single quote */ 265 L"ls sing", 266 "sing", 267 "single'quote", 268 L"ls single\\'quote " 269 }, 270 { 271 /* test one single quote inside single quote */ 272 L"ls 'ssing", 273 "ssing", 274 "ssingle'quote", 275 L"ls 'ssingle'\\''quote' " 276 }, 277 { 278 /* test one single quote inside double quote */ 279 L"ls \"dsing", 280 "dsing", 281 "dsingle'quote", 282 L"ls \"dsingle'quote\" " 283 }, 284 { 285 /* test multiple single quotes */ 286 L"ls mu_sing", 287 "mu_sing", 288 "mu_single''quotes''", 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''", 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''", 303 L"ls \"dmu_single''quotes''\" " 304 }, 305 { 306 /* test parenthesis */ 307 L"ls paren", 308 "paren", 309 "paren(test)", 310 L"ls paren\\(test\\) " 311 }, 312 { 313 /* test parenthesis inside single quote */ 314 L"ls 'sparen", 315 "sparen", 316 "sparen(test)", 317 L"ls 'sparen(test)' " 318 }, 319 { 320 /* test parenthesis inside double quote */ 321 L"ls \"dparen", 322 "dparen", 323 "dparen(test)", 324 L"ls \"dparen(test)\" " 325 }, 326 { 327 /* test pipe */ 328 L"ls pip", 329 "pip", 330 "pipe|test", 331 L"ls pipe\\|test " 332 }, 333 { 334 /* test pipe inside single quote */ 335 L"ls 'spip", 336 "spip", 337 "spipe|test", 338 L"ls 'spipe|test' ", 339 }, 340 { 341 /* test pipe inside double quote */ 342 L"ls \"dpip", 343 "dpip", 344 "dpipe|test", 345 L"ls \"dpipe|test\" " 346 }, 347 { 348 /* test tab */ 349 L"ls ta", 350 "ta", 351 "tab\ttest", 352 L"ls tab\\\ttest " 353 }, 354 { 355 /* test tab inside single quote */ 356 L"ls 'sta", 357 "sta", 358 "stab\ttest", 359 L"ls 'stab\ttest' " 360 }, 361 { 362 /* test tab inside double quote */ 363 L"ls \"dta", 364 "dta", 365 "dtab\ttest", 366 L"ls \"dtab\ttest\" " 367 }, 368 { 369 /* test back tick */ 370 L"ls tic", 371 "tic", 372 "tick`test`", 373 L"ls tick\\`test\\` " 374 }, 375 { 376 /* test back tick inside single quote */ 377 L"ls 'stic", 378 "stic", 379 "stick`test`", 380 L"ls 'stick`test`' " 381 }, 382 { 383 /* test back tick inside double quote */ 384 L"ls \"dtic", 385 "dtic", 386 "dtick`test`", 387 L"ls \"dtick\\`test\\`\" " 388 }, 389 { 390 /* test for @ */ 391 L"ls at", 392 "at", 393 "atthe@rate", 394 L"ls atthe\\@rate " 395 }, 396 { 397 /* test for @ inside single quote */ 398 L"ls 'sat", 399 "sat", 400 "satthe@rate", 401 L"ls 'satthe@rate' " 402 }, 403 { 404 /* test for @ inside double quote */ 405 L"ls \"dat", 406 "dat", 407 "datthe@rate", 408 L"ls \"datthe@rate\" " 409 }, 410 { 411 /* test ; */ 412 L"ls semi", 413 "semi", 414 "semi;colon;test", 415 L"ls semi\\;colon\\;test " 416 }, 417 { 418 /* test ; inside single quote */ 419 L"ls 'ssemi", 420 "ssemi", 421 "ssemi;colon;test", 422 L"ls 'ssemi;colon;test' " 423 }, 424 { 425 /* test ; inside double quote */ 426 L"ls \"dsemi", 427 "dsemi", 428 "dsemi;colon;test", 429 L"ls \"dsemi;colon;test\" " 430 }, 431 { 432 /* test & */ 433 L"ls amp", 434 "amp", 435 "ampers&and", 436 L"ls ampers\\&and " 437 }, 438 { 439 /* test & inside single quote */ 440 L"ls 'samp", 441 "samp", 442 "sampers&and", 443 L"ls 'sampers&and' " 444 }, 445 { 446 /* test & inside double quote */ 447 L"ls \"damp", 448 "damp", 449 "dampers&and", 450 L"ls \"dampers&and\" " 451 }, 452 { 453 /* test completion when cursor at \ */ 454 L"ls foo\\", 455 "foo", 456 "foo bar", 457 L"ls foo\\ bar " 458 }, 459 { 460 /* test completion when cursor at single quote */ 461 L"ls foo'", 462 "foo", 463 "foo bar", 464 L"ls foo\\ bar " 465 }, 466 { 467 /* test completion when cursor at double quote */ 468 L"ls foo\"", 469 "foo", 470 "foo bar", 471 L"ls foo\\ bar " 472 } 473 }; 474 475 static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{("; 476 477 /* 478 * Custom completion function passed to fn_complete. 479 * The function returns hardcoded completion matches 480 * based on the test cases present in inputs[] (above) 481 */ 482 static char * 483 mycomplet_func(const char *text, int index) 484 { 485 static char *last_input = NULL; 486 size_t i = 0; 487 if (last_input && strcmp(last_input, text) == 0) { 488 free(last_input); 489 last_input = NULL; 490 return NULL; 491 } 492 last_input = strdup(text); 493 494 for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) { 495 if (strcmp(text, inputs[i].completion_function_input) == 0) 496 return strdup(inputs[i].expanded_text); 497 } 498 499 return NULL; 500 } 501 502 int 503 main(int argc, char **argv) 504 { 505 EditLine *el = el_init(argv[0], stdin, stdout, stderr); 506 size_t i; 507 size_t input_len; 508 el_line_t line; 509 wchar_t *buffer = malloc(64 * sizeof(*buffer)); 510 if (buffer == NULL) 511 err(EXIT_FAILURE, "malloc failed"); 512 513 for (i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) { 514 memset(buffer, 0, 64 * sizeof(*buffer)); 515 input_len = wcslen(inputs[i].user_typed_text); 516 wmemcpy(buffer, inputs[i].user_typed_text, input_len); 517 buffer[input_len] = 0; 518 line.buffer = buffer; 519 line.cursor = line.buffer + input_len ; 520 line.lastchar = line.cursor - 1; 521 line.limit = line.buffer + 64 * sizeof(*buffer); 522 el->el_line = line; 523 fn_complete(el, mycomplet_func, NULL, break_chars, NULL, NULL, 10, NULL, NULL, NULL, NULL); 524 525 /* 526 * fn_complete would have expanded and escaped the input in el->el_line.buffer. 527 * We need to assert that it matches with the expected value in our test data 528 */ 529 printf("User input: %ls\t Expected output: %ls\t Generated output: %ls\n", 530 inputs[i].user_typed_text, inputs[i].escaped_output, el->el_line.buffer); 531 assert(wcscmp(el->el_line.buffer, inputs[i].escaped_output) == 0); 532 } 533 el_end(el); 534 return 0; 535 536 } 537