1 /* 2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. 3 * 4 * All rights reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, and/or sell copies of the Software, and to permit persons 11 * to whom the Software is furnished to do so, provided that the above 12 * copyright notice(s) and this permission notice appear in all copies of 13 * the Software and that both the above copyright notice(s) and this 14 * permission notice appear in supporting documentation. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING 22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 25 * 26 * Except as contained in this notice, the name of a copyright holder 27 * shall not be used in advertising or otherwise to promote the sale, use 28 * or other dealings in this Software without prior written authorization 29 * of the copyright holder. 30 */ 31 32 /* 33 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 34 * Use is subject to license terms. 35 */ 36 37 /* 38 * Standard includes. 39 */ 40 #include <stdlib.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <errno.h> 44 45 /* 46 * Local includes. 47 */ 48 #include "libtecla.h" 49 #include "ioutil.h" 50 #include "stringrp.h" 51 #include "pathutil.h" 52 #include "cplfile.h" 53 #include "cplmatch.h" 54 #include "errmsg.h" 55 56 /* 57 * Specify the number of strings to allocate when the string free-list 58 * is exhausted. This also sets the number of elements to expand the 59 * matches[] array by whenever it is found to be too small. 60 */ 61 #define STR_BLK_FACT 100 62 63 /* 64 * Set the default number of spaces place between columns when listing 65 * a set of completions. 66 */ 67 #define CPL_COL_SEP 2 68 69 /* 70 * Completion matches are recorded in containers of the following 71 * type. 72 */ 73 struct WordCompletion { 74 ErrMsg *err; /* The error reporting buffer */ 75 StringGroup *sg; /* Memory for a group of strings */ 76 int matches_dim; /* The allocated size of result.matches[] */ 77 CplMatches result; /* Completions to be returned to the caller */ 78 #ifndef WITHOUT_FILE_SYSTEM 79 CompleteFile *cf; /* The resources used for filename completion */ 80 #endif 81 }; 82 83 static void cpl_sort_matches(WordCompletion *cpl); 84 static void cpl_zap_duplicates(WordCompletion *cpl); 85 static void cpl_clear_completions(WordCompletion *cpl); 86 static int cpl_cmp_matches(const void *v1, const void *v2); 87 static int cpl_cmp_suffixes(const void *v1, const void *v2); 88 89 /* 90 * The new_CplFileConf() constructor sets the integer first member of 91 * the returned object to the following magic number. On seeing this, 92 * cpl_file_completions() knows when it is passed a valid CplFileConf 93 * object. 94 */ 95 #define CFC_ID_CODE 4568 96 97 #ifndef WITHOUT_FILE_SYSTEM 98 /* 99 * A pointer to a structure of the following type can be passed to 100 * the builtin file-completion callback function to modify its behavior. 101 */ 102 struct CplFileConf { 103 int id; /* new_CplFileConf() sets this to CFC_ID_CODE */ 104 int escaped; /* If none-zero, backslashes in the input line are */ 105 /* interpreted as escaping special characters and */ 106 /* spaces, and any special characters and spaces in */ 107 /* the listed completions will also be escaped with */ 108 /* added backslashes. This is the default behaviour. */ 109 /* If zero, backslashes are interpreted as being */ 110 /* literal parts of the filename, and none are added */ 111 /* to the completion suffixes. */ 112 int file_start; /* The index in the input line of the first character */ 113 /* of the filename. If you specify -1 here, */ 114 /* cpl_file_completions() identifies the */ 115 /* the start of the filename by looking backwards for */ 116 /* an unescaped space, or the beginning of the line. */ 117 CplCheckFn *chk_fn; /* If not zero, this argument specifies a */ 118 /* function to call to ask whether a given */ 119 /* file should be included in the list */ 120 /* of completions. */ 121 void *chk_data; /* Anonymous data to be passed to check_fn(). */ 122 }; 123 124 static void cpl_init_FileConf(CplFileConf *cfc); 125 126 /* 127 * When file-system access is being excluded, define a dummy structure 128 * to satisfy the typedef in libtecla.h. 129 */ 130 #else 131 struct CplFileConf {int dummy;}; 132 #endif 133 134 /* 135 * Encapsulate the formatting information needed to layout a 136 * multi-column listing of completions. 137 */ 138 typedef struct { 139 int term_width; /* The width of the terminal (characters) */ 140 int column_width; /* The number of characters within in each column. */ 141 int ncol; /* The number of columns needed */ 142 int nline; /* The number of lines needed */ 143 } CplListFormat; 144 145 /* 146 * Given the current terminal width, and a list of completions, determine 147 * how to best use the terminal width to display a multi-column listing 148 * of completions. 149 */ 150 static void cpl_plan_listing(CplMatches *result, int term_width, 151 CplListFormat *fmt); 152 153 /* 154 * Display a given line of a multi-column list of completions. 155 */ 156 static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum, 157 GlWriteFn *write_fn, void *data); 158 159 /*....................................................................... 160 * Create a new string-completion object. 161 * 162 * Output: 163 * return WordCompletion * The new object, or NULL on error. 164 */ 165 WordCompletion *new_WordCompletion(void) 166 { 167 WordCompletion *cpl; /* The object to be returned */ 168 /* 169 * Allocate the container. 170 */ 171 cpl = (WordCompletion *) malloc(sizeof(WordCompletion)); 172 if(!cpl) { 173 errno = ENOMEM; 174 return NULL; 175 }; 176 /* 177 * Before attempting any operation that might fail, initialize the 178 * container at least up to the point at which it can safely be passed 179 * to del_WordCompletion(). 180 */ 181 cpl->err = NULL; 182 cpl->sg = NULL; 183 cpl->matches_dim = 0; 184 cpl->result.suffix = NULL; 185 cpl->result.cont_suffix = NULL; 186 cpl->result.matches = NULL; 187 cpl->result.nmatch = 0; 188 #ifndef WITHOUT_FILE_SYSTEM 189 cpl->cf = NULL; 190 #endif 191 /* 192 * Allocate a place to record error messages. 193 */ 194 cpl->err = _new_ErrMsg(); 195 if(!cpl->err) 196 return del_WordCompletion(cpl); 197 /* 198 * Allocate an object that allows a group of strings to be allocated 199 * efficiently by placing many of them in contiguous string segments. 200 */ 201 #ifdef WITHOUT_FILE_SYSTEM 202 cpl->sg = _new_StringGroup(MAX_PATHLEN_FALLBACK); 203 #else 204 cpl->sg = _new_StringGroup(_pu_pathname_dim()); 205 #endif 206 if(!cpl->sg) 207 return del_WordCompletion(cpl); 208 /* 209 * Allocate an array for matching completions. This will be extended later 210 * if needed. 211 */ 212 cpl->matches_dim = STR_BLK_FACT; 213 cpl->result.matches = (CplMatch *) malloc(sizeof(cpl->result.matches[0]) * 214 cpl->matches_dim); 215 if(!cpl->result.matches) { 216 errno = ENOMEM; 217 return del_WordCompletion(cpl); 218 }; 219 /* 220 * Allocate a filename-completion resource object. 221 */ 222 #ifndef WITHOUT_FILE_SYSTEM 223 cpl->cf = _new_CompleteFile(); 224 if(!cpl->cf) 225 return del_WordCompletion(cpl); 226 #endif 227 return cpl; 228 } 229 230 /*....................................................................... 231 * Delete a string-completion object. 232 * 233 * Input: 234 * cpl WordCompletion * The object to be deleted. 235 * Output: 236 * return WordCompletion * The deleted object (always NULL). 237 */ 238 WordCompletion *del_WordCompletion(WordCompletion *cpl) 239 { 240 if(cpl) { 241 cpl->err = _del_ErrMsg(cpl->err); 242 cpl->sg = _del_StringGroup(cpl->sg); 243 if(cpl->result.matches) { 244 free(cpl->result.matches); 245 cpl->result.matches = NULL; 246 #ifndef WITHOUT_FILE_SYSTEM 247 cpl->cf = _del_CompleteFile(cpl->cf); 248 #endif 249 }; 250 free(cpl); 251 }; 252 return NULL; 253 } 254 255 /*....................................................................... 256 * This function is designed to be called by CplMatchFn callback 257 * functions. It adds one possible completion of the token that is being 258 * completed to an array of completions. If the completion needs any 259 * special quoting to be valid when displayed in the input line, this 260 * quoting must be included in the string. 261 * 262 * Input: 263 * cpl WordCompletion * The argument of the same name that was passed 264 * to the calling CplMatchFn callback function. 265 * line const char * The input line, as received by the callback 266 * function. 267 * word_start int The index within line[] of the start of the 268 * word that is being completed. 269 * word_end int The index within line[] of the character which 270 * follows the incomplete word, as received by the 271 * calling callback function. 272 * suffix const char * The appropriately quoted string that could 273 * be appended to the incomplete token to complete 274 * it. A copy of this string will be allocated 275 * internally. 276 * type_suffix const char * When listing multiple completions, gl_get_line() 277 * appends this string to the completion to indicate 278 * its type to the user. If not pertinent pass "". 279 * Otherwise pass a literal or static string. 280 * cont_suffix const char * If this turns out to be the only completion, 281 * gl_get_line() will append this string as 282 * a continuation. For example, the builtin 283 * file-completion callback registers a directory 284 * separator here for directory matches, and a 285 * space otherwise. If the match were a function 286 * name you might want to append an open 287 * parenthesis, etc.. If not relevant pass "". 288 * Otherwise pass a literal or static string. 289 * Output: 290 * return int 0 - OK. 291 * 1 - Error. 292 */ 293 int cpl_add_completion(WordCompletion *cpl, const char *line, 294 int word_start, int word_end, const char *suffix, 295 const char *type_suffix, const char *cont_suffix) 296 { 297 CplMatch *match; /* The container of the new match */ 298 char *string; /* A newly allocated copy of the completion string */ 299 size_t len; 300 /* 301 * Check the arguments. 302 */ 303 if(!cpl) 304 return 1; 305 if(!suffix) 306 return 0; 307 if(!type_suffix) 308 type_suffix = ""; 309 if(!cont_suffix) 310 cont_suffix = ""; 311 /* 312 * Do we need to extend the array of matches[]? 313 */ 314 if(cpl->result.nmatch+1 > cpl->matches_dim) { 315 int needed = cpl->matches_dim + STR_BLK_FACT; 316 CplMatch *matches = (CplMatch *) realloc(cpl->result.matches, 317 sizeof(cpl->result.matches[0]) * needed); 318 if(!matches) { 319 _err_record_msg(cpl->err, 320 "Insufficient memory to extend array of matches.", 321 END_ERR_MSG); 322 return 1; 323 }; 324 cpl->result.matches = matches; 325 cpl->matches_dim = needed; 326 }; 327 /* 328 * Allocate memory to store the combined completion prefix and the 329 * new suffix. 330 */ 331 len = strlen(suffix); 332 string = _sg_alloc_string(cpl->sg, word_end-word_start + len); 333 if(!string) { 334 _err_record_msg(cpl->err, "Insufficient memory to extend array of matches.", 335 END_ERR_MSG); 336 return 1; 337 }; 338 /* 339 * Compose the string. 340 */ 341 strncpy(string, line + word_start, word_end - word_start); 342 strlcpy(string + word_end - word_start, suffix, len + 1); 343 /* 344 * Record the new match. 345 */ 346 match = cpl->result.matches + cpl->result.nmatch++; 347 match->completion = string; 348 match->suffix = string + word_end - word_start; 349 match->type_suffix = type_suffix; 350 /* 351 * Record the continuation suffix. 352 */ 353 cpl->result.cont_suffix = cont_suffix; 354 return 0; 355 } 356 357 /*....................................................................... 358 * Sort the array of matches. 359 * 360 * Input: 361 * cpl WordCompletion * The completion resource object. 362 */ 363 static void cpl_sort_matches(WordCompletion *cpl) 364 { 365 qsort(cpl->result.matches, cpl->result.nmatch, 366 sizeof(cpl->result.matches[0]), cpl_cmp_matches); 367 } 368 369 /*....................................................................... 370 * This is a qsort() comparison function used to sort matches. 371 * 372 * Input: 373 * v1, v2 void * Pointers to the two matches to be compared. 374 * Output: 375 * return int -1 -> v1 < v2. 376 * 0 -> v1 == v2 377 * 1 -> v1 > v2 378 */ 379 static int cpl_cmp_matches(const void *v1, const void *v2) 380 { 381 const CplMatch *m1 = (const CplMatch *) v1; 382 const CplMatch *m2 = (const CplMatch *) v2; 383 return strcmp(m1->completion, m2->completion); 384 } 385 386 /*....................................................................... 387 * Sort the array of matches in order of their suffixes. 388 * 389 * Input: 390 * cpl WordCompletion * The completion resource object. 391 */ 392 static void cpl_sort_suffixes(WordCompletion *cpl) 393 { 394 qsort(cpl->result.matches, cpl->result.nmatch, 395 sizeof(cpl->result.matches[0]), cpl_cmp_suffixes); 396 } 397 398 /*....................................................................... 399 * This is a qsort() comparison function used to sort matches in order of 400 * their suffixes. 401 * 402 * Input: 403 * v1, v2 void * Pointers to the two matches to be compared. 404 * Output: 405 * return int -1 -> v1 < v2. 406 * 0 -> v1 == v2 407 * 1 -> v1 > v2 408 */ 409 static int cpl_cmp_suffixes(const void *v1, const void *v2) 410 { 411 const CplMatch *m1 = (const CplMatch *) v1; 412 const CplMatch *m2 = (const CplMatch *) v2; 413 return strcmp(m1->suffix, m2->suffix); 414 } 415 416 /*....................................................................... 417 * Find the common prefix of all of the matching completion matches, 418 * and record a pointer to it in cpl->result.suffix. Note that this has 419 * the side effect of sorting the matches into suffix order. 420 * 421 * Input: 422 * cpl WordCompletion * The completion resource object. 423 * Output: 424 * return int 0 - OK. 425 * 1 - Error. 426 */ 427 static int cpl_common_suffix(WordCompletion *cpl) 428 { 429 CplMatches *result; /* The result container */ 430 const char *first, *last; /* The first and last matching suffixes */ 431 int length; /* The length of the common suffix */ 432 /* 433 * Get the container of the array of matching files. 434 */ 435 result = &cpl->result; 436 /* 437 * No matching completions? 438 */ 439 if(result->nmatch < 1) 440 return 0; 441 /* 442 * Sort th matches into suffix order. 443 */ 444 cpl_sort_suffixes(cpl); 445 /* 446 * Given that the array of matches is sorted, the first and last 447 * suffixes are those that differ most in their prefixes, so the common 448 * prefix of these strings is the longest common prefix of all of the 449 * suffixes. 450 */ 451 first = result->matches[0].suffix; 452 last = result->matches[result->nmatch - 1].suffix; 453 /* 454 * Find the point at which the first and last matching strings 455 * first difffer. 456 */ 457 while(*first && *first == *last) { 458 first++; 459 last++; 460 }; 461 /* 462 * How long is the common suffix? 463 */ 464 length = first - result->matches[0].suffix; 465 /* 466 * Allocate memory to record the common suffix. 467 */ 468 result->suffix = _sg_alloc_string(cpl->sg, length); 469 if(!result->suffix) { 470 _err_record_msg(cpl->err, 471 "Insufficient memory to record common completion suffix.", 472 END_ERR_MSG); 473 return 1; 474 }; 475 /* 476 * Record the common suffix. 477 */ 478 strncpy(result->suffix, result->matches[0].suffix, length); 479 result->suffix[length] = '\0'; 480 return 0; 481 } 482 483 /*....................................................................... 484 * Discard the contents of the array of possible completion matches. 485 * 486 * Input: 487 * cpl WordCompletion * The word-completion resource object. 488 */ 489 static void cpl_clear_completions(WordCompletion *cpl) 490 { 491 /* 492 * Discard all of the strings. 493 */ 494 _clr_StringGroup(cpl->sg); 495 /* 496 * Record the fact that the array is now empty. 497 */ 498 cpl->result.nmatch = 0; 499 cpl->result.suffix = NULL; 500 cpl->result.cont_suffix = ""; 501 /* 502 * Also clear the error message. 503 */ 504 _err_clear_msg(cpl->err); 505 return; 506 } 507 508 /*....................................................................... 509 * Given an input line and the point at which it completion is to be 510 * attempted, return an array of possible completions. 511 * 512 * Input: 513 * cpl WordCompletion * The completion resource object. 514 * line char * The current input line. 515 * word_end int The index of the character in line[] which 516 * follows the end of the token that is being 517 * completed. 518 * data void * Anonymous 'data' to be passed to match_fn(). 519 * match_fn CplMatchFn * The function that will identify the prefix 520 * to be completed from the input line, and 521 * record completion matches. 522 * Output: 523 * return CplMatches * The container of the array of possible 524 * completions. The returned pointer refers 525 * to a container owned by the parent WordCompletion 526 * object, and its contents thus potentially 527 * change on every call to cpl_matches(). 528 * On error, NULL is returned, and a description 529 * of the error can be acquired by calling 530 * cpl_last_error(cpl). 531 */ 532 CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, 533 int word_end, void *data, 534 CplMatchFn *match_fn) 535 { 536 int line_len; /* The total length of the input line */ 537 /* 538 * How long is the input line? 539 */ 540 line_len = strlen(line); 541 /* 542 * Check the arguments. 543 */ 544 if(!cpl || !line || !match_fn || word_end < 0 || word_end > line_len) { 545 if(cpl) { 546 _err_record_msg(cpl->err, "cpl_complete_word: Invalid arguments.", 547 END_ERR_MSG); 548 }; 549 return NULL; 550 }; 551 /* 552 * Clear the return container. 553 */ 554 cpl_clear_completions(cpl); 555 /* 556 * Have the matching function record possible completion matches in 557 * cpl->result.matches. 558 */ 559 if(match_fn(cpl, data, line, word_end)) { 560 if(_err_get_msg(cpl->err)[0] == '\0') 561 _err_record_msg(cpl->err, "Error completing word.", END_ERR_MSG); 562 return NULL; 563 }; 564 /* 565 * Record a copy of the common initial part of all of the prefixes 566 * in cpl->result.common. 567 */ 568 if(cpl_common_suffix(cpl)) 569 return NULL; 570 /* 571 * Sort the matches into lexicographic order. 572 */ 573 cpl_sort_matches(cpl); 574 /* 575 * Discard any duplicate matches. 576 */ 577 cpl_zap_duplicates(cpl); 578 /* 579 * If there is more than one match, discard the continuation suffix. 580 */ 581 if(cpl->result.nmatch > 1) 582 cpl->result.cont_suffix = ""; 583 /* 584 * Return the array of matches. 585 */ 586 return &cpl->result; 587 } 588 589 /*....................................................................... 590 * Recall the return value of the last call to cpl_complete_word(). 591 * 592 * Input: 593 * cpl WordCompletion * The completion resource object. 594 * Output: 595 * return CplMatches * The container of the array of possible 596 * completions, as returned by the last call to 597 * cpl_complete_word(). The returned pointer refers 598 * to a container owned by the parent WordCompletion 599 * object, and its contents thus potentially 600 * change on every call to cpl_complete_word(). 601 * On error, either in the execution of this 602 * function, or in the last call to 603 * cpl_complete_word(), NULL is returned, and a 604 * description of the error can be acquired by 605 * calling cpl_last_error(cpl). 606 */ 607 CplMatches *cpl_recall_matches(WordCompletion *cpl) 608 { 609 return (!cpl || *_err_get_msg(cpl->err)!='\0') ? NULL : &cpl->result; 610 } 611 612 /*....................................................................... 613 * Print out an array of matching completions. 614 * 615 * Input: 616 * result CplMatches * The container of the sorted array of 617 * completions. 618 * fp FILE * The output stream to write to. 619 * term_width int The width of the terminal. 620 * Output: 621 * return int 0 - OK. 622 * 1 - Error. 623 */ 624 int cpl_list_completions(CplMatches *result, FILE *fp, int term_width) 625 { 626 return _cpl_output_completions(result, _io_write_stdio, fp, term_width); 627 } 628 629 /*....................................................................... 630 * Print an array of matching completions via a callback function. 631 * 632 * Input: 633 * result CplMatches * The container of the sorted array of 634 * completions. 635 * write_fn GlWriteFn * The function to call to write the completions, 636 * or 0 to discard the output. 637 * data void * Anonymous data to pass to write_fn(). 638 * term_width int The width of the terminal. 639 * Output: 640 * return int 0 - OK. 641 * 1 - Error. 642 */ 643 int _cpl_output_completions(CplMatches *result, GlWriteFn *write_fn, void *data, 644 int term_width) 645 { 646 CplListFormat fmt; /* List formatting information */ 647 int lnum; /* The sequential number of the line to print next */ 648 /* 649 * Not enough space to list anything? 650 */ 651 if(term_width < 1) 652 return 0; 653 /* 654 * Do we have a callback to write via, and any completions to be listed? 655 */ 656 if(write_fn && result && result->nmatch>0) { 657 /* 658 * Work out how to arrange the listing into fixed sized columns. 659 */ 660 cpl_plan_listing(result, term_width, &fmt); 661 /* 662 * Print the listing via the specified callback. 663 */ 664 for(lnum=0; lnum < fmt.nline; lnum++) { 665 if(cpl_format_line(result, &fmt, lnum, write_fn, data)) 666 return 1; 667 }; 668 }; 669 return 0; 670 } 671 672 /*....................................................................... 673 * Return a description of the string-completion error that occurred. 674 * 675 * Input: 676 * cpl WordCompletion * The string-completion resource object. 677 * Output: 678 * return const char * The description of the last error. 679 */ 680 const char *cpl_last_error(WordCompletion *cpl) 681 { 682 return cpl ? _err_get_msg(cpl->err) : "NULL WordCompletion argument"; 683 } 684 685 /*....................................................................... 686 * When an error occurs while performing a completion, you registerf a 687 * terse description of the error by calling cpl_record_error(). This 688 * message will then be returned on the next call to cpl_last_error(). 689 * 690 * Input: 691 * cpl WordCompletion * The string-completion resource object that was 692 * originally passed to the callback. 693 * errmsg const char * The description of the error. 694 */ 695 void cpl_record_error(WordCompletion *cpl, const char *errmsg) 696 { 697 if(cpl && errmsg) 698 _err_record_msg(cpl->err, errmsg, END_ERR_MSG); 699 } 700 701 /*....................................................................... 702 * This is the builtin completion callback function which performs file 703 * completion. 704 * 705 * Input: 706 * cpl WordCompletion * An opaque pointer to the object that will 707 * contain the matches. This should be filled 708 * via zero or more calls to cpl_add_completion(). 709 * data void * Either NULL to request the default 710 * file-completion behavior, or a pointer to a 711 * CplFileConf structure, whose members specify 712 * a different behavior. 713 * line char * The current input line. 714 * word_end int The index of the character in line[] which 715 * follows the end of the token that is being 716 * completed. 717 * Output 718 * return int 0 - OK. 719 * 1 - Error. 720 */ 721 CPL_MATCH_FN(cpl_file_completions) 722 { 723 #ifdef WITHOUT_FILE_SYSTEM 724 return 0; 725 #else 726 const char *start_path; /* The pointer to the start of the pathname */ 727 /* in line[]. */ 728 CplFileConf *conf; /* The new-style configuration object. */ 729 /* 730 * The following configuration object will be used if the caller didn't 731 * provide one. 732 */ 733 CplFileConf default_conf; 734 /* 735 * This function can be called externally, so check its arguments. 736 */ 737 if(!cpl) 738 return 1; 739 if(!line || word_end < 0) { 740 _err_record_msg(cpl->err, "cpl_file_completions: Invalid arguments.", 741 END_ERR_MSG); 742 return 1; 743 }; 744 /* 745 * The 'data' argument is either a CplFileConf pointer, identifiable 746 * by having an integer id code as its first member, or the deprecated 747 * CplFileArgs pointer, or can be NULL to request the default 748 * configuration. 749 */ 750 if(data && *(int *)data == CFC_ID_CODE) { 751 conf = (CplFileConf *) data; 752 } else { 753 /* 754 * Select the defaults. 755 */ 756 conf = &default_conf; 757 cpl_init_FileConf(&default_conf); 758 /* 759 * If we have been passed an instance of the deprecated CplFileArgs 760 * structure, copy its configuration parameters over the defaults. 761 */ 762 if(data) { 763 CplFileArgs *args = (CplFileArgs *) data; 764 conf->escaped = args->escaped; 765 conf->file_start = args->file_start; 766 }; 767 }; 768 /* 769 * Get the start of the filename. If not specified by the caller 770 * identify it by searching backwards in the input line for an 771 * unescaped space or the start of the line. 772 */ 773 if(conf->file_start < 0) { 774 start_path = _pu_start_of_path(line, word_end); 775 if(!start_path) { 776 _err_record_msg(cpl->err, "Unable to find the start of the filename.", 777 END_ERR_MSG); 778 return 1; 779 }; 780 } else { 781 start_path = line + conf->file_start; 782 }; 783 /* 784 * Perform the completion. 785 */ 786 if(_cf_complete_file(cpl, cpl->cf, line, start_path - line, word_end, 787 conf->escaped, conf->chk_fn, conf->chk_data)) { 788 cpl_record_error(cpl, _cf_last_error(cpl->cf)); 789 return 1; 790 }; 791 return 0; 792 #endif 793 } 794 795 /*....................................................................... 796 * Initialize a CplFileArgs structure with default configuration 797 * parameters. Note that the CplFileArgs configuration type is 798 * deprecated. The opaque CplFileConf object should be used in future 799 * applications. 800 * 801 * Input: 802 * cfa CplFileArgs * The configuration object of the 803 * cpl_file_completions() callback. 804 */ 805 void cpl_init_FileArgs(CplFileArgs *cfa) 806 { 807 if(cfa) { 808 cfa->escaped = 1; 809 cfa->file_start = -1; 810 }; 811 } 812 813 #ifndef WITHOUT_FILE_SYSTEM 814 /*....................................................................... 815 * Initialize a CplFileConf structure with default configuration 816 * parameters. 817 * 818 * Input: 819 * cfc CplFileConf * The configuration object of the 820 * cpl_file_completions() callback. 821 */ 822 static void cpl_init_FileConf(CplFileConf *cfc) 823 { 824 if(cfc) { 825 cfc->id = CFC_ID_CODE; 826 cfc->escaped = 1; 827 cfc->file_start = -1; 828 cfc->chk_fn = 0; 829 cfc->chk_data = NULL; 830 }; 831 } 832 #endif 833 834 /*....................................................................... 835 * Create a new CplFileConf object and initialize it with defaults. 836 * 837 * Output: 838 * return CplFileConf * The new object, or NULL on error. 839 */ 840 CplFileConf *new_CplFileConf(void) 841 { 842 #ifdef WITHOUT_FILE_SYSTEM 843 errno = EINVAL; 844 return NULL; 845 #else 846 CplFileConf *cfc; /* The object to be returned */ 847 /* 848 * Allocate the container. 849 */ 850 cfc = (CplFileConf *)malloc(sizeof(CplFileConf)); 851 if(!cfc) 852 return NULL; 853 /* 854 * Before attempting any operation that might fail, initialize the 855 * container at least up to the point at which it can safely be passed 856 * to del_CplFileConf(). 857 */ 858 cpl_init_FileConf(cfc); 859 return cfc; 860 #endif 861 } 862 863 /*....................................................................... 864 * Delete a CplFileConf object. 865 * 866 * Input: 867 * cfc CplFileConf * The object to be deleted. 868 * Output: 869 * return CplFileConf * The deleted object (always NULL). 870 */ 871 CplFileConf *del_CplFileConf(CplFileConf *cfc) 872 { 873 #ifndef WITHOUT_FILE_SYSTEM 874 if(cfc) { 875 /* 876 * Delete the container. 877 */ 878 free(cfc); 879 }; 880 #endif 881 return NULL; 882 } 883 884 /*....................................................................... 885 * If backslashes in the filename should be treated as literal 886 * characters, call the following function with literal=1. Otherwise 887 * the default is to treat them as escape characters, used for escaping 888 * spaces etc.. 889 * 890 * Input: 891 * cfc CplFileConf * The cpl_file_completions() configuration object 892 * to be configured. 893 * literal int Pass non-zero here to enable literal interpretation 894 * of backslashes. Pass 0 to turn off literal 895 * interpretation. 896 */ 897 void cfc_literal_escapes(CplFileConf *cfc, int literal) 898 { 899 #ifndef WITHOUT_FILE_SYSTEM 900 if(cfc) 901 cfc->escaped = !literal; 902 #endif 903 } 904 905 /*....................................................................... 906 * Call this function if you know where the index at which the 907 * filename prefix starts in the input line. Otherwise by default, 908 * or if you specify start_index to be -1, the filename is taken 909 * to start after the first unescaped space preceding the cursor, 910 * or the start of the line, which ever comes first. 911 * 912 * Input: 913 * cfc CplFileConf * The cpl_file_completions() configuration object 914 * to be configured. 915 * start_index int The index of the start of the filename in 916 * the input line, or -1 to select the default. 917 */ 918 void cfc_file_start(CplFileConf *cfc, int start_index) 919 { 920 #ifndef WITHOUT_FILE_SYSTEM 921 if(cfc) 922 cfc->file_start = start_index; 923 #endif 924 } 925 926 /*....................................................................... 927 * If you only want certain types of files to be included in the 928 * list of completions, you use the following function to specify a 929 * callback function which will be called to ask whether a given file 930 * should be included. 931 * 932 * Input: 933 * cfc CplFileConf * The cpl_file_completions() configuration object 934 * to be configured. 935 * chk_fn CplCheckFn * Zero to disable filtering, or a pointer to a 936 * function that returns 1 if a given file should 937 * be included in the list of completions. 938 * chk_data void * Anonymous data to be passed to chk_fn() 939 * every time that it is called. 940 */ 941 void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data) 942 { 943 #ifndef WITHOUT_FILE_SYSTEM 944 if(cfc) { 945 cfc->chk_fn = chk_fn; 946 cfc->chk_data = chk_data; 947 }; 948 #endif 949 } 950 951 /*....................................................................... 952 * The following CplCheckFn callback returns non-zero if the specified 953 * filename is that of an executable. 954 */ 955 CPL_CHECK_FN(cpl_check_exe) 956 { 957 #ifdef WITHOUT_FILE_SYSTEM 958 return 0; 959 #else 960 return _pu_path_is_exe(pathname); 961 #endif 962 } 963 964 /*....................................................................... 965 * Remove duplicates from a sorted array of matches. 966 * 967 * Input: 968 * cpl WordCompletion * The completion resource object. 969 */ 970 static void cpl_zap_duplicates(WordCompletion *cpl) 971 { 972 CplMatch *matches; /* The array of matches */ 973 int nmatch; /* The number of elements in matches[] */ 974 const char *completion; /* The completion string of the last unique match */ 975 const char *type_suffix; /* The type of the last unique match */ 976 int src; /* The index of the match being considered */ 977 int dst; /* The index at which to record the next */ 978 /* unique match. */ 979 /* 980 * Get the array of matches and the number of matches that it 981 * contains. 982 */ 983 matches = cpl->result.matches; 984 nmatch = cpl->result.nmatch; 985 /* 986 * No matches? 987 */ 988 if(nmatch < 1) 989 return; 990 /* 991 * Initialize the comparison strings with the first match. 992 */ 993 completion = matches[0].completion; 994 type_suffix = matches[0].type_suffix; 995 /* 996 * Go through the array of matches, copying each new unrecorded 997 * match at the head of the array, while discarding duplicates. 998 */ 999 for(src=dst=1; src<nmatch; src++) { 1000 CplMatch *match = matches + src; 1001 if(strcmp(completion, match->completion) != 0 || 1002 strcmp(type_suffix, match->type_suffix) != 0) { 1003 if(src != dst) 1004 matches[dst] = *match; 1005 dst++; 1006 completion = match->completion; 1007 type_suffix = match->type_suffix; 1008 }; 1009 }; 1010 /* 1011 * Record the number of unique matches that remain. 1012 */ 1013 cpl->result.nmatch = dst; 1014 return; 1015 } 1016 1017 /*....................................................................... 1018 * Work out how to arrange a given array of completions into a listing 1019 * of one or more fixed size columns. 1020 * 1021 * Input: 1022 * result CplMatches * The set of completions to be listed. 1023 * term_width int The width of the terminal. A lower limit of 1024 * zero is quietly enforced. 1025 * Input/Output: 1026 * fmt CplListFormat * The formatting information will be assigned 1027 * to the members of *fmt. 1028 */ 1029 static void cpl_plan_listing(CplMatches *result, int term_width, 1030 CplListFormat *fmt) 1031 { 1032 int maxlen; /* The length of the longest matching string */ 1033 int i; 1034 /* 1035 * Ensure that term_width >= 0. 1036 */ 1037 if(term_width < 0) 1038 term_width = 0; 1039 /* 1040 * Start by assuming the worst case, that either nothing will fit 1041 * on the screen, or that there are no matches to be listed. 1042 */ 1043 fmt->term_width = term_width; 1044 fmt->column_width = 0; 1045 fmt->nline = fmt->ncol = 0; 1046 /* 1047 * Work out the maximum length of the matching strings. 1048 */ 1049 maxlen = 0; 1050 for(i=0; i<result->nmatch; i++) { 1051 CplMatch *match = result->matches + i; 1052 int len = strlen(match->completion) + strlen(match->type_suffix); 1053 if(len > maxlen) 1054 maxlen = len; 1055 }; 1056 /* 1057 * Nothing to list? 1058 */ 1059 if(maxlen == 0) 1060 return; 1061 /* 1062 * Split the available terminal width into columns of 1063 * maxlen + CPL_COL_SEP characters. 1064 */ 1065 fmt->column_width = maxlen; 1066 fmt->ncol = fmt->term_width / (fmt->column_width + CPL_COL_SEP); 1067 /* 1068 * If the column width is greater than the terminal width, zero columns 1069 * will have been selected. Set a lower limit of one column. Leave it 1070 * up to the caller how to deal with completions who's widths exceed 1071 * the available terminal width. 1072 */ 1073 if(fmt->ncol < 1) 1074 fmt->ncol = 1; 1075 /* 1076 * How many lines of output will be needed? 1077 */ 1078 fmt->nline = (result->nmatch + fmt->ncol - 1) / fmt->ncol; 1079 return; 1080 } 1081 1082 /*....................................................................... 1083 * Render one line of a multi-column listing of completions, using a 1084 * callback function to pass the output to an arbitrary destination. 1085 * 1086 * Input: 1087 * result CplMatches * The container of the sorted array of 1088 * completions. 1089 * fmt CplListFormat * Formatting information. 1090 * lnum int The index of the line to print, starting 1091 * from 0, and incrementing until the return 1092 * value indicates that there is nothing more 1093 * to be printed. 1094 * write_fn GlWriteFn * The function to call to write the line, or 1095 * 0 to discard the output. 1096 * data void * Anonymous data to pass to write_fn(). 1097 * Output: 1098 * return int 0 - Line printed ok. 1099 * 1 - Nothing to print. 1100 */ 1101 static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum, 1102 GlWriteFn *write_fn, void *data) 1103 { 1104 int col; /* The index of the list column being output */ 1105 /* 1106 * If the line index is out of bounds, there is nothing to be written. 1107 */ 1108 if(lnum < 0 || lnum >= fmt->nline) 1109 return 1; 1110 /* 1111 * If no output function has been provided, return as though the 1112 * line had been printed. 1113 */ 1114 if(!write_fn) 1115 return 0; 1116 /* 1117 * Print the matches in 'ncol' columns, sorted in line order within each 1118 * column. 1119 */ 1120 for(col=0; col < fmt->ncol; col++) { 1121 int m = col*fmt->nline + lnum; 1122 /* 1123 * Is there another match to be written? Note that in general 1124 * the last line of a listing will have fewer filled columns 1125 * than the initial lines. 1126 */ 1127 if(m < result->nmatch) { 1128 CplMatch *match = result->matches + m; 1129 /* 1130 * How long are the completion and type-suffix strings? 1131 */ 1132 int clen = strlen(match->completion); 1133 int tlen = strlen(match->type_suffix); 1134 /* 1135 * Write the completion string. 1136 */ 1137 if(write_fn(data, match->completion, clen) != clen) 1138 return 1; 1139 /* 1140 * Write the type suffix, if any. 1141 */ 1142 if(tlen > 0 && write_fn(data, match->type_suffix, tlen) != tlen) 1143 return 1; 1144 /* 1145 * If another column follows the current one, pad to its start with spaces. 1146 */ 1147 if(col+1 < fmt->ncol) { 1148 /* 1149 * The following constant string of spaces is used to pad the output. 1150 */ 1151 static const char spaces[] = " "; 1152 static const int nspace = sizeof(spaces) - 1; 1153 /* 1154 * Pad to the next column, using as few sub-strings of the spaces[] 1155 * array as possible. 1156 */ 1157 int npad = fmt->column_width + CPL_COL_SEP - clen - tlen; 1158 while(npad>0) { 1159 int n = npad > nspace ? nspace : npad; 1160 if(write_fn(data, spaces + nspace - n, n) != n) 1161 return 1; 1162 npad -= n; 1163 }; 1164 }; 1165 }; 1166 }; 1167 /* 1168 * Start a new line. 1169 */ 1170 { 1171 char s[] = "\r\n"; 1172 int n = strlen(s); 1173 if(write_fn(data, s, n) != n) 1174 return 1; 1175 }; 1176 return 0; 1177 } 1178