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