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 * If file-system access is to be excluded, this module has no function, 41 * so all of its code should be excluded. 42 */ 43 #ifndef WITHOUT_FILE_SYSTEM 44 45 #include <stdlib.h> 46 #include <string.h> 47 #include <stdio.h> 48 #include <errno.h> 49 50 #include "libtecla.h" 51 #include "pathutil.h" 52 #include "homedir.h" 53 #include "freelist.h" 54 #include "direader.h" 55 #include "stringrp.h" 56 #include "errmsg.h" 57 58 /* 59 * The new_PcaPathConf() constructor sets the integer first member of 60 * the returned object to the following magic number. This is then 61 * checked for by pca_path_completions() as a sanity check. 62 */ 63 #define PPC_ID_CODE 4567 64 65 /* 66 * A pointer to a structure of the following type can be passed to 67 * the builtin path-completion callback function to modify its behavior. 68 */ 69 struct PcaPathConf { 70 int id; /* This is set to PPC_ID_CODE by new_PcaPathConf() */ 71 PathCache *pc; /* The path-list cache in which to look up the executables */ 72 int escaped; /* If non-zero, backslashes in the input line are */ 73 /* interpreted as escaping special characters and */ 74 /* spaces, and any special characters and spaces in */ 75 /* the listed completions will also be escaped with */ 76 /* added backslashes. This is the default behaviour. */ 77 /* If zero, backslashes are interpreted as being */ 78 /* literal parts of the file name, and none are added */ 79 /* to the completion suffixes. */ 80 int file_start; /* The index in the input line of the first character */ 81 /* of the file name. If you specify -1 here, */ 82 /* pca_path_completions() identifies the */ 83 /* the start of the file by looking backwards for */ 84 /* an unescaped space, or the beginning of the line. */ 85 }; 86 87 /* 88 * Prepended to each chached filename is a character which contains 89 * one of the following status codes. When a given filename (minus 90 * this byte) is passed to the application's check_fn(), the result 91 * is recorded in this byte, such that the next time it is looked 92 * up, we don't have to call check_fn() again. These codes are cleared 93 * whenever the path is scanned and whenever the check_fn() callback 94 * is changed. 95 */ 96 typedef enum { 97 PCA_F_ENIGMA='?', /* The file remains to be checked */ 98 PCA_F_WANTED='+', /* The file has been selected by the caller's callback */ 99 PCA_F_IGNORE='-' /* The file has been rejected by the caller's callback */ 100 } PcaFileStatus; 101 102 /* 103 * Encapsulate the memory management objects which supply memoy for 104 * the arrays of filenames. 105 */ 106 typedef struct { 107 StringGroup *sg; /* The memory used to record the names of files */ 108 size_t files_dim; /* The allocated size of files[] */ 109 char **files; /* Memory for 'files_dim' pointers to files */ 110 size_t nfiles; /* The number of filenames currently in files[] */ 111 } CacheMem; 112 113 static CacheMem *new_CacheMem(void); 114 static CacheMem *del_CacheMem(CacheMem *cm); 115 static void rst_CacheMem(CacheMem *cm); 116 117 /* 118 * Lists of nodes of the following type are used to record the 119 * names and contents of individual directories. 120 */ 121 typedef struct PathNode PathNode; 122 struct PathNode { 123 PathNode *next; /* The next directory in the path */ 124 int relative; /* True if the directory is a relative pathname */ 125 CacheMem *mem; /* The memory used to store dir[] and files[] */ 126 char *dir; /* The directory pathname (stored in pc->sg) */ 127 int nfile; /* The number of filenames stored in 'files' */ 128 char **files; /* Files of interest in the current directory, */ 129 /* or NULL if dir[] is a relative pathname */ 130 /* who's contents can't be cached. This array */ 131 /* and its contents are taken from pc->abs_mem */ 132 /* or pc->rel_mem */ 133 }; 134 135 /* 136 * Append a new node to the list of directories in the path. 137 */ 138 static int add_PathNode(PathCache *pc, const char *dirname); 139 140 /* 141 * Set the maximum length allowed for usernames. 142 * names. 143 */ 144 #define USR_LEN 100 145 146 /* 147 * PathCache objects encapsulate the resources needed to record 148 * files of interest from comma-separated lists of directories. 149 */ 150 struct PathCache { 151 ErrMsg *err; /* The error reporting buffer */ 152 FreeList *node_mem; /* A free-list of PathNode objects */ 153 CacheMem *abs_mem; /* Memory for the filenames of absolute paths */ 154 CacheMem *rel_mem; /* Memory for the filenames of relative paths */ 155 PathNode *head; /* The head of the list of directories in the */ 156 /* path, or NULL if no path has been scanned yet. */ 157 PathNode *tail; /* The tail of the list of directories in the */ 158 /* path, or NULL if no path has been scanned yet. */ 159 PathName *path; /* The fully qualified name of a file */ 160 HomeDir *home; /* Home-directory lookup object */ 161 DirReader *dr; /* A portable directory reader */ 162 CplFileConf *cfc; /* Configuration parameters to pass to */ 163 /* cpl_file_completions() */ 164 CplCheckFn *check_fn; /* The callback used to determine if a given */ 165 /* filename should be recorded in the cache. */ 166 void *data; /* Annonymous data to be passed to pc->check_fn() */ 167 char usrnam[USR_LEN+1];/* The buffer used when reading the names of */ 168 /* users. */ 169 }; 170 171 /* 172 * Empty the cache. 173 */ 174 static void pca_clear_cache(PathCache *pc); 175 176 /* 177 * Read a username from string[] and record it in pc->usrnam[]. 178 */ 179 static int pca_read_username(PathCache *pc, const char *string, int slen, 180 int literal, const char **nextp); 181 182 /* 183 * Extract the next component of a colon separated list of directory 184 * paths. 185 */ 186 static int pca_extract_dir(PathCache *pc, const char *path, 187 const char **nextp); 188 189 /* 190 * Scan absolute directories for files of interest, recording their names 191 * in mem->sg and recording pointers to these names in mem->files[]. 192 */ 193 static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem); 194 195 /* 196 * A qsort() comparison function for comparing the cached filename 197 * strings pointed to by two (char **) array elements. Note that 198 * this ignores the initial cache-status byte of each filename. 199 */ 200 static int pca_cmp_matches(const void *v1, const void *v2); 201 202 /* 203 * A qsort() comparison function for comparing a filename 204 * against an element of an array of pointers to filename cache 205 * entries. 206 */ 207 static int pca_cmp_file(const void *v1, const void *v2); 208 209 /* 210 * Initialize a PcaPathConf configuration objects with the default 211 * options. 212 */ 213 static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc); 214 215 /* 216 * Make a copy of a completion suffix, suitable for passing to 217 * cpl_add_completion(). 218 */ 219 static int pca_prepare_suffix(PathCache *pc, const char *suffix, 220 int add_escapes); 221 222 /* 223 * Return non-zero if the specified string appears to start with a pathname. 224 */ 225 static int cpa_cmd_contains_path(const char *prefix, int prefix_len); 226 227 /* 228 * Return a given prefix with escapes optionally removed. 229 */ 230 static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, 231 size_t prefix_len, int escaped); 232 233 /* 234 * If there is a tilde expression at the beginning of the specified path, 235 * place the corresponding home directory into pc->path. Otherwise 236 * just clear pc->path. 237 */ 238 static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, 239 int literal, const char **endp); 240 241 /* 242 * Clear the filename status codes that are recorded before each filename 243 * in the cache. 244 */ 245 static void pca_remove_marks(PathCache *pc); 246 247 /* 248 * Specify how many PathNode's to allocate at a time. 249 */ 250 #define PATH_NODE_BLK 30 251 252 /* 253 * Specify the amount by which the files[] arrays are to be extended 254 * whenever they are found to be too small. 255 */ 256 #define FILES_BLK_FACT 256 257 258 /*....................................................................... 259 * Create a new object who's function is to maintain a cache of 260 * filenames found within a list of directories, and provide quick 261 * lookup and completion of selected files in this cache. 262 * 263 * Output: 264 * return PathCache * The new, initially empty cache, or NULL 265 * on error. 266 */ 267 PathCache *new_PathCache(void) 268 { 269 PathCache *pc; /* The object to be returned */ 270 /* 271 * Allocate the container. 272 */ 273 pc = (PathCache *)malloc(sizeof(PathCache)); 274 if(!pc) { 275 errno = ENOMEM; 276 return NULL; 277 }; 278 /* 279 * Before attempting any operation that might fail, initialize the 280 * container at least up to the point at which it can safely be passed 281 * to del_PathCache(). 282 */ 283 pc->err = NULL; 284 pc->node_mem = NULL; 285 pc->abs_mem = NULL; 286 pc->rel_mem = NULL; 287 pc->head = NULL; 288 pc->tail = NULL; 289 pc->path = NULL; 290 pc->home = NULL; 291 pc->dr = NULL; 292 pc->cfc = NULL; 293 pc->check_fn = 0; 294 pc->data = NULL; 295 pc->usrnam[0] = '\0'; 296 /* 297 * Allocate a place to record error messages. 298 */ 299 pc->err = _new_ErrMsg(); 300 if(!pc->err) 301 return del_PathCache(pc); 302 /* 303 * Allocate the freelist of directory list nodes. 304 */ 305 pc->node_mem = _new_FreeList(sizeof(PathNode), PATH_NODE_BLK); 306 if(!pc->node_mem) 307 return del_PathCache(pc); 308 /* 309 * Allocate memory for recording names of files in absolute paths. 310 */ 311 pc->abs_mem = new_CacheMem(); 312 if(!pc->abs_mem) 313 return del_PathCache(pc); 314 /* 315 * Allocate memory for recording names of files in relative paths. 316 */ 317 pc->rel_mem = new_CacheMem(); 318 if(!pc->rel_mem) 319 return del_PathCache(pc); 320 /* 321 * Allocate a pathname buffer. 322 */ 323 pc->path = _new_PathName(); 324 if(!pc->path) 325 return del_PathCache(pc); 326 /* 327 * Allocate an object for looking up home-directories. 328 */ 329 pc->home = _new_HomeDir(); 330 if(!pc->home) 331 return del_PathCache(pc); 332 /* 333 * Allocate an object for reading directories. 334 */ 335 pc->dr = _new_DirReader(); 336 if(!pc->dr) 337 return del_PathCache(pc); 338 /* 339 * Allocate a cpl_file_completions() configuration object. 340 */ 341 pc->cfc = new_CplFileConf(); 342 if(!pc->cfc) 343 return del_PathCache(pc); 344 /* 345 * Configure cpl_file_completions() to use check_fn() to select 346 * files of interest. 347 */ 348 cfc_set_check_fn(pc->cfc, pc->check_fn, pc->data); 349 /* 350 * Return the cache, ready for use. 351 */ 352 return pc; 353 } 354 355 /*....................................................................... 356 * Delete a given cache of files, returning the resources that it 357 * was using to the system. 358 * 359 * Input: 360 * pc PathCache * The cache to be deleted (can be NULL). 361 * Output: 362 * return PathCache * The deleted object (ie. allways NULL). 363 */ 364 PathCache *del_PathCache(PathCache *pc) 365 { 366 if(pc) { 367 /* 368 * Delete the error message buffer. 369 */ 370 pc->err = _del_ErrMsg(pc->err); 371 /* 372 * Delete the memory of the list of path nodes. 373 */ 374 pc->node_mem = _del_FreeList(pc->node_mem, 1); 375 /* 376 * Delete the memory used to record filenames. 377 */ 378 pc->abs_mem = del_CacheMem(pc->abs_mem); 379 pc->rel_mem = del_CacheMem(pc->rel_mem); 380 /* 381 * The list of PathNode's was already deleted when node_mem was 382 * deleted. 383 */ 384 pc->head = NULL; 385 pc->tail = NULL; 386 /* 387 * Delete the pathname buffer. 388 */ 389 pc->path = _del_PathName(pc->path); 390 /* 391 * Delete the home-directory lookup object. 392 */ 393 pc->home = _del_HomeDir(pc->home); 394 /* 395 * Delete the directory reader. 396 */ 397 pc->dr = _del_DirReader(pc->dr); 398 /* 399 * Delete the cpl_file_completions() config object. 400 */ 401 pc->cfc = del_CplFileConf(pc->cfc); 402 /* 403 * Delete the container. 404 */ 405 free(pc); 406 }; 407 return NULL; 408 } 409 410 /*....................................................................... 411 * If you want subsequent calls to pca_lookup_file() and 412 * pca_path_completions() to only return the filenames of certain 413 * types of files, for example executables, or filenames ending in 414 * ".ps", call this function to register a file-selection callback 415 * function. This callback function takes the full pathname of a file, 416 * plus application-specific data, and returns 1 if the file is of 417 * interest, and zero otherwise. 418 * 419 * Input: 420 * pc PathCache * The filename cache. 421 * check_fn CplCheckFn * The function to call to see if the name of 422 * a given file should be included in the 423 * cache. This determines what type of files 424 * will reside in the cache. To revert to 425 * selecting all files, regardless of type, 426 * pass 0 here. 427 * data void * You can pass a pointer to anything you 428 * like here, including NULL. It will be 429 * passed to your check_fn() callback 430 * function, for its private use. 431 */ 432 void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data) 433 { 434 if(pc) { 435 /* 436 * If the callback or its data pointer have changed, clear the cached 437 * statuses of files that were accepted or rejected by the previous 438 * calback. 439 */ 440 if(check_fn != pc->check_fn || data != pc->data) 441 pca_remove_marks(pc); 442 /* 443 * Record the new callback locally. 444 */ 445 pc->check_fn = check_fn; 446 pc->data = data; 447 /* 448 * Configure cpl_file_completions() to use the same callback to 449 * select files of interest. 450 */ 451 cfc_set_check_fn(pc->cfc, check_fn, data); 452 }; 453 return; 454 } 455 456 /*....................................................................... 457 * Return a description of the last path-caching error that occurred. 458 * 459 * Input: 460 * pc PathCache * The filename cache that suffered the error. 461 * Output: 462 * return char * The description of the last error. 463 */ 464 const char *pca_last_error(PathCache *pc) 465 { 466 return pc ? _err_get_msg(pc->err) : "NULL PathCache argument"; 467 } 468 469 /*....................................................................... 470 * Discard all cached filenames. 471 * 472 * Input: 473 * pc PathCache * The cache to be cleared. 474 */ 475 static void pca_clear_cache(PathCache *pc) 476 { 477 if(pc) { 478 /* 479 * Return all path-nodes to the freelist. 480 */ 481 _rst_FreeList(pc->node_mem); 482 pc->head = pc->tail = NULL; 483 /* 484 * Delete all filename strings. 485 */ 486 rst_CacheMem(pc->abs_mem); 487 rst_CacheMem(pc->rel_mem); 488 }; 489 return; 490 } 491 492 /*....................................................................... 493 * Build the list of files of interest contained in a given 494 * colon-separated list of directories. 495 * 496 * Input: 497 * pc PathCache * The cache in which to store the names of 498 * the files that are found in the list of 499 * directories. 500 * path const char * A colon-separated list of directory 501 * paths. Under UNIX, when searching for 502 * executables, this should be the return 503 * value of getenv("PATH"). 504 * Output: 505 * return int 0 - OK. 506 * 1 - An error occurred. A description of 507 * the error can be acquired by calling 508 * pca_last_error(pc). 509 */ 510 int pca_scan_path(PathCache *pc, const char *path) 511 { 512 const char *pptr; /* A pointer to the next unprocessed character in path[] */ 513 PathNode *node; /* A node in the list of directory paths */ 514 char **fptr; /* A pointer into pc->abs_mem->files[] */ 515 /* 516 * Check the arguments. 517 */ 518 if(!pc) 519 return 1; 520 /* 521 * Clear the outdated contents of the cache. 522 */ 523 pca_clear_cache(pc); 524 /* 525 * If no path list was provided, there is nothing to be added to the 526 * cache. 527 */ 528 if(!path) 529 return 0; 530 /* 531 * Extract directories from the path list, expanding tilde expressions 532 * on the fly into pc->pathname, then add them to the list of path 533 * nodes, along with a sorted list of the filenames of interest that 534 * the directories hold. 535 */ 536 pptr = path; 537 while(*pptr) { 538 /* 539 * Extract the next pathname component into pc->path->name. 540 */ 541 if(pca_extract_dir(pc, pptr, &pptr)) 542 return 1; 543 /* 544 * Add a new node to the list of paths, containing both the 545 * directory name and, if not a relative pathname, the list of 546 * files of interest in the directory. 547 */ 548 if(add_PathNode(pc, pc->path->name)) 549 return 1; 550 }; 551 /* 552 * The file arrays in each absolute directory node are sections of 553 * pc->abs_mem->files[]. Record pointers to the starts of each 554 * of these sections in each directory node. Note that this couldn't 555 * be done in add_PathNode(), because pc->abs_mem->files[] may 556 * get reallocated in subsequent calls to add_PathNode(), thus 557 * invalidating any pointers to it. 558 */ 559 fptr = pc->abs_mem->files; 560 for(node=pc->head; node; node=node->next) { 561 node->files = fptr; 562 fptr += node->nfile; 563 }; 564 return 0; 565 } 566 567 /*....................................................................... 568 * Extract the next directory path from a colon-separated list of 569 * directories, expanding tilde home-directory expressions where needed. 570 * 571 * Input: 572 * pc PathCache * The cache of filenames. 573 * path const char * A pointer to the start of the next component 574 * in the path list. 575 * Input/Output: 576 * nextp const char ** A pointer to the next unprocessed character 577 * in path[] will be assigned to *nextp. 578 * Output: 579 * return int 0 - OK. The extracted path is in pc->path->name. 580 * 1 - Error. A description of the error will 581 * have been left in pc->err. 582 */ 583 static int pca_extract_dir(PathCache *pc, const char *path, const char **nextp) 584 { 585 const char *pptr; /* A pointer into path[] */ 586 const char *sptr; /* The path following tilde expansion */ 587 int escaped = 0; /* True if the last character was a backslash */ 588 /* 589 * If there is a tilde expression at the beginning of the specified path, 590 * place the corresponding home directory into pc->path. Otherwise 591 * just clear pc->path. 592 */ 593 if(pca_expand_tilde(pc, path, strlen(path), 0, &pptr)) 594 return 1; 595 /* 596 * Keep a record of the current location in the path. 597 */ 598 sptr = pptr; 599 /* 600 * Locate the end of the directory name in the pathname string, stopping 601 * when either the end of the string is reached, or an un-escaped colon 602 * separator is seen. 603 */ 604 while(*pptr && (escaped || *pptr != ':')) 605 escaped = !escaped && *pptr++ == '\\'; 606 /* 607 * Append the rest of the directory path to the pathname buffer. 608 */ 609 if(_pn_append_to_path(pc->path, sptr, pptr - sptr, 1) == NULL) { 610 _err_record_msg(pc->err, "Insufficient memory to record directory name", 611 END_ERR_MSG); 612 return 1; 613 }; 614 /* 615 * To facilitate subsequently appending filenames to the directory 616 * path name, make sure that the recorded directory name ends in a 617 * directory separator. 618 */ 619 { 620 int dirlen = strlen(pc->path->name); 621 if(dirlen < FS_DIR_SEP_LEN || 622 strncmp(pc->path->name + dirlen - FS_DIR_SEP_LEN, FS_DIR_SEP, 623 FS_DIR_SEP_LEN) != 0) { 624 if(_pn_append_to_path(pc->path, FS_DIR_SEP, FS_DIR_SEP_LEN, 0) == NULL) { 625 _err_record_msg(pc->err, "Insufficient memory to record directory name", 626 END_ERR_MSG); 627 return 1; 628 }; 629 }; 630 }; 631 /* 632 * Skip the separator unless we have reached the end of the path. 633 */ 634 if(*pptr==':') 635 pptr++; 636 /* 637 * Return the unprocessed tail of the path-list string. 638 */ 639 *nextp = pptr; 640 return 0; 641 } 642 643 /*....................................................................... 644 * Read a username, stopping when a directory separator is seen, a colon 645 * separator is seen, the end of the string is reached, or the username 646 * buffer overflows. 647 * 648 * Input: 649 * pc PathCache * The cache of filenames. 650 * string char * The string who's prefix contains the name. 651 * slen int The max number of characters to read from string[]. 652 * literal int If true, treat backslashes as literal characters 653 * instead of escapes. 654 * Input/Output: 655 * nextp char ** A pointer to the next unprocessed character 656 * in string[] will be assigned to *nextp. 657 * Output: 658 * return int 0 - OK. The username can be found in pc->usrnam. 659 * 1 - Error. A description of the error message 660 * can be found in pc->err. 661 */ 662 static int pca_read_username(PathCache *pc, const char *string, int slen, 663 int literal, const char **nextp) 664 { 665 int usrlen; /* The number of characters in pc->usrnam[] */ 666 const char *sptr; /* A pointer into string[] */ 667 int escaped = 0; /* True if the last character was a backslash */ 668 /* 669 * Extract the username. 670 */ 671 for(sptr=string,usrlen=0; usrlen < USR_LEN && (sptr-string) < slen; sptr++) { 672 /* 673 * Stop if the end of the string is reached, or a directory separator 674 * or un-escaped colon separator is seen. 675 */ 676 if(!*sptr || strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN)==0 || 677 (!escaped && *sptr == ':')) 678 break; 679 /* 680 * Escape the next character? 681 */ 682 if(!literal && !escaped && *sptr == '\\') { 683 escaped = 1; 684 } else { 685 escaped = 0; 686 pc->usrnam[usrlen++] = *sptr; 687 }; 688 }; 689 /* 690 * Did the username overflow the buffer? 691 */ 692 if(usrlen >= USR_LEN) { 693 _err_record_msg(pc->err, "Username too long", END_ERR_MSG); 694 return 1; 695 }; 696 /* 697 * Terminate the string. 698 */ 699 pc->usrnam[usrlen] = '\0'; 700 /* 701 * Indicate where processing of the input string should continue. 702 */ 703 *nextp = sptr; 704 return 0; 705 } 706 707 708 /*....................................................................... 709 * Create a new CacheMem object. 710 * 711 * Output: 712 * return CacheMem * The new object, or NULL on error. 713 */ 714 static CacheMem *new_CacheMem(void) 715 { 716 CacheMem *cm; /* The object to be returned */ 717 /* 718 * Allocate the container. 719 */ 720 cm = (CacheMem *)malloc(sizeof(CacheMem)); 721 if(!cm) { 722 errno = ENOMEM; 723 return NULL; 724 }; 725 /* 726 * Before attempting any operation that might fail, initialize the 727 * container at least up to the point at which it can safely be passed 728 * to del_CacheMem(). 729 */ 730 cm->sg = NULL; 731 cm->files_dim = 0; 732 cm->files = NULL; 733 cm->nfiles = 0; 734 /* 735 * Allocate a list of string segments for storing filenames. 736 */ 737 cm->sg = _new_StringGroup(_pu_pathname_dim()); 738 if(!cm->sg) 739 return del_CacheMem(cm); 740 /* 741 * Allocate an array of pointers to filenames. 742 * This will be extended later if needed. 743 */ 744 cm->files_dim = FILES_BLK_FACT; 745 cm->files = (char **) malloc(sizeof(*cm->files) * cm->files_dim); 746 if(!cm->files) { 747 errno = ENOMEM; 748 return del_CacheMem(cm); 749 }; 750 return cm; 751 } 752 753 /*....................................................................... 754 * Delete a CacheMem object. 755 * 756 * Input: 757 * cm CacheMem * The object to be deleted. 758 * Output: 759 * return CacheMem * The deleted object (always NULL). 760 */ 761 static CacheMem *del_CacheMem(CacheMem *cm) 762 { 763 if(cm) { 764 /* 765 * Delete the memory that was used to record filename strings. 766 */ 767 cm->sg = _del_StringGroup(cm->sg); 768 /* 769 * Delete the array of pointers to filenames. 770 */ 771 cm->files_dim = 0; 772 if(cm->files) { 773 free(cm->files); 774 cm->files = NULL; 775 }; 776 /* 777 * Delete the container. 778 */ 779 free(cm); 780 }; 781 return NULL; 782 } 783 784 /*....................................................................... 785 * Re-initialize the memory used to allocate filename strings. 786 * 787 * Input: 788 * cm CacheMem * The memory cache to be cleared. 789 */ 790 static void rst_CacheMem(CacheMem *cm) 791 { 792 _clr_StringGroup(cm->sg); 793 cm->nfiles = 0; 794 return; 795 } 796 797 /*....................................................................... 798 * Append a new directory node to the list of directories read from the 799 * path. 800 * 801 * Input: 802 * pc PathCache * The filename cache. 803 * dirname const char * The name of the new directory. 804 * Output: 805 * return int 0 - OK. 806 * 1 - Error. 807 */ 808 static int add_PathNode(PathCache *pc, const char *dirname) 809 { 810 PathNode *node; /* The new directory list node */ 811 int relative; /* True if dirname[] is a relative pathname */ 812 /* 813 * Have we been passed a relative pathname or an absolute pathname? 814 */ 815 relative = strncmp(dirname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) != 0; 816 /* 817 * If it's an absolute pathname, ignore it if the corresponding 818 * directory doesn't exist. 819 */ 820 if(!relative && !_pu_path_is_dir(dirname)) 821 return 0; 822 /* 823 * Allocate a new list node to record the specifics of the new directory. 824 */ 825 node = (PathNode *) _new_FreeListNode(pc->node_mem); 826 if(!node) { 827 _err_record_msg(pc->err, "Insufficient memory to cache new directory.", 828 END_ERR_MSG); 829 return 1; 830 }; 831 /* 832 * Initialize the node. 833 */ 834 node->next = NULL; 835 node->relative = relative; 836 node->mem = relative ? pc->rel_mem : pc->abs_mem; 837 node->dir = NULL; 838 node->nfile = 0; 839 node->files = NULL; 840 /* 841 * Make a copy of the directory pathname. 842 */ 843 node->dir = _sg_store_string(pc->abs_mem->sg, dirname, 0); 844 if(!node->dir) { 845 _err_record_msg(pc->err, "Insufficient memory to store directory name.", 846 END_ERR_MSG); 847 return 1; 848 }; 849 /* 850 * Scan absolute directories for files of interest, recording their names 851 * in node->mem->sg and appending pointers to these names to the 852 * node->mem->files[] array. 853 */ 854 if(!node->relative) { 855 int nfile = node->nfile = pca_scan_dir(pc, node->dir, node->mem); 856 if(nfile < 1) { /* No files matched or an error occurred */ 857 node = (PathNode *) _del_FreeListNode(pc->node_mem, node); 858 return nfile < 0; 859 }; 860 }; 861 /* 862 * Append the new node to the list. 863 */ 864 if(pc->head) { 865 pc->tail->next = node; 866 pc->tail = node; 867 } else { 868 pc->head = pc->tail = node; 869 }; 870 return 0; 871 } 872 873 /*....................................................................... 874 * Scan a given directory for files of interest, record their names 875 * in mem->sg and append pointers to them to the mem->files[] array. 876 * 877 * Input: 878 * pc PathCache * The filename cache. 879 * dirname const char * The pathname of the directory to be scanned. 880 * mem CacheMem * The memory in which to store filenames of 881 * interest. 882 * Output: 883 * return int The number of files recorded, or -1 if a 884 * memory error occurs. Note that the 885 * inability to read the contents of the 886 * directory is not counted as an error. 887 */ 888 static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem) 889 { 890 int nfile = 0; /* The number of filenames recorded */ 891 const char *filename; /* The name of the file being looked at */ 892 /* 893 * Attempt to open the directory. If the directory can't be read then 894 * there are no accessible files of interest in the directory. 895 */ 896 if(_dr_open_dir(pc->dr, dirname, NULL)) 897 return 0; 898 /* 899 * Record the names of all files in the directory in the cache. 900 */ 901 while((filename = _dr_next_file(pc->dr))) { 902 char *copy; /* A copy of the filename */ 903 /* 904 * Make a temporary copy of the filename with an extra byte prepended. 905 */ 906 _pn_clear_path(pc->path); 907 if(_pn_append_to_path(pc->path, " ", 1, 0) == NULL || 908 _pn_append_to_path(pc->path, filename, -1, 1) == NULL) { 909 _err_record_msg(pc->err, "Insufficient memory to record filename", 910 END_ERR_MSG); 911 return -1; 912 }; 913 /* 914 * Store the filename. 915 */ 916 copy = _sg_store_string(mem->sg, pc->path->name, 0); 917 if(!copy) { 918 _err_record_msg(pc->err, "Insufficient memory to cache file name.", 919 END_ERR_MSG); 920 return -1; 921 }; 922 /* 923 * Mark the filename as unchecked. 924 */ 925 copy[0] = PCA_F_ENIGMA; 926 /* 927 * Make room to store a pointer to the copy in mem->files[]. 928 */ 929 if(mem->nfiles + 1 > mem->files_dim) { 930 int needed = mem->files_dim + FILES_BLK_FACT; 931 char **files = (char **) realloc(mem->files, sizeof(*mem->files)*needed); 932 if(!files) { 933 _err_record_msg(pc->err, 934 "Insufficient memory to extend filename cache.", 935 END_ERR_MSG); 936 return 1; 937 }; 938 mem->files = files; 939 mem->files_dim = needed; 940 }; 941 /* 942 * Record a pointer to the copy of the filename at the end of the files[] 943 * array. 944 */ 945 mem->files[mem->nfiles++] = copy; 946 /* 947 * Keep a record of the number of files matched so far. 948 */ 949 nfile++; 950 }; 951 /* 952 * Sort the list of files into lexical order. 953 */ 954 qsort(mem->files + mem->nfiles - nfile, nfile, sizeof(*mem->files), 955 pca_cmp_matches); 956 /* 957 * Return the number of files recorded in mem->files[]. 958 */ 959 return nfile; 960 } 961 962 /*....................................................................... 963 * A qsort() comparison function for comparing the cached filename 964 * strings pointed to by two (char **) array elements. Note that 965 * this ignores the initial cache-status byte of each filename. 966 * 967 * Input: 968 * v1, v2 void * Pointers to the pointers of two strings to be compared. 969 * Output: 970 * return int -1 -> v1 < v2. 971 * 0 -> v1 == v2 972 * 1 -> v1 > v2 973 */ 974 static int pca_cmp_matches(const void *v1, const void *v2) 975 { 976 const char **s1 = (const char **) v1; 977 const char **s2 = (const char **) v2; 978 return strcmp(*s1+1, *s2+1); 979 } 980 981 /*....................................................................... 982 * Given the simple name of a file, search the cached list of files 983 * in the order in which they where found in the list of directories 984 * previously presented to pca_scan_path(), and return the pathname 985 * of the first file which has this name. If a pathname to a file is 986 * given instead of a simple filename, this is returned without being 987 * looked up in the cache, but with any initial ~username expression 988 * expanded, and optionally, unescaped backslashes removed. 989 * 990 * Input: 991 * pc PathCache * The cached list of files. 992 * name const char * The name of the file to lookup. 993 * name_len int The length of the filename string at the 994 * beginning of name[], or -1 to indicate that 995 * the filename occupies the whole of the 996 * string. 997 * literal int If this argument is zero, lone backslashes 998 * in name[] are ignored during comparison 999 * with filenames in the cache, under the 1000 * assumption that they were in the input line 1001 * soley to escape the special significance of 1002 * characters like spaces. To have them treated 1003 * as normal characters, give this argument a 1004 * non-zero value, such as 1. 1005 * Output: 1006 * return char * The pathname of the first matching file, 1007 * or NULL if not found. Note that the returned 1008 * pointer points to memory owned by *pc, and 1009 * will become invalid on the next call to any 1010 * function in the PathCache module. 1011 */ 1012 char *pca_lookup_file(PathCache *pc, const char *name, int name_len, 1013 int literal) 1014 { 1015 PathNode *node; /* A node in the list of directories in the path */ 1016 char **match; /* A pointer to a matching filename string in the cache */ 1017 /* 1018 * Check the arguments. 1019 */ 1020 if(!pc || !name || name_len==0) 1021 return NULL; 1022 /* 1023 * If no length was specified, determine the length of the string to 1024 * be looked up. 1025 */ 1026 if(name_len < 0) 1027 name_len = strlen(name); 1028 /* 1029 * If the word starts with a ~username expression, the root directory, 1030 * of it contains any directory separators, then treat it isn't a simple 1031 * filename that can be looked up in the cache, but rather appears to 1032 * be the pathname of a file. If so, return a copy of this pathname with 1033 * escapes removed, if requested, and any initial ~username expression 1034 * expanded. 1035 */ 1036 if(cpa_cmd_contains_path(name, name_len)) { 1037 const char *nptr; 1038 if(pca_expand_tilde(pc, name, name_len, literal, &nptr) || 1039 _pn_append_to_path(pc->path, nptr, name_len - (nptr-name), 1040 !literal) == NULL) 1041 return NULL; 1042 return pc->path->name; 1043 }; 1044 /* 1045 * Look up the specified filename in each of the directories of the path, 1046 * in the same order that they were listed in the path, and stop as soon 1047 * as an instance of the file is found. 1048 */ 1049 for(node=pc->head; node; node=node->next) { 1050 /* 1051 * If the directory of the latest node is a relative pathname, 1052 * scan it for files of interest. 1053 */ 1054 if(node->relative) { 1055 rst_CacheMem(node->mem); 1056 if(pca_scan_dir(pc, node->dir, node->mem) < 1) 1057 continue; 1058 node->files = node->mem->files; 1059 node->nfile = node->mem->nfiles; 1060 }; 1061 /* 1062 * Copy the filename into a temporary buffer, while interpretting 1063 * escape characters if needed. 1064 */ 1065 _pn_clear_path(pc->path); 1066 if(_pn_append_to_path(pc->path, name, name_len, !literal) == NULL) 1067 return NULL; 1068 /* 1069 * Perform a binary search for the requested filename. 1070 */ 1071 match = (char **)bsearch(pc->path->name, node->files, node->nfile, 1072 sizeof(*node->files), pca_cmp_file); 1073 if(match) { 1074 /* 1075 * Prepend the pathname in which the directory was found, which we have 1076 * guaranteed to end in a directory separator, to the located filename. 1077 */ 1078 if(_pn_prepend_to_path(pc->path, node->dir, -1, 0) == NULL) 1079 return NULL; 1080 /* 1081 * Return the matching pathname unless it is rejected by the application. 1082 */ 1083 if(!pc->check_fn || (*match)[0] == PCA_F_WANTED || 1084 ((*match)[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))){ 1085 (*match)[0] = PCA_F_WANTED; 1086 return pc->path->name; 1087 } else { 1088 *(match)[0] = PCA_F_IGNORE; 1089 }; 1090 }; 1091 }; 1092 /* 1093 * File not found. 1094 */ 1095 return NULL; 1096 } 1097 1098 /*....................................................................... 1099 * A qsort() comparison function for comparing a filename string to 1100 * a cached filename string pointed to by a (char **) array element. 1101 * This ignores the initial code byte at the start of the cached filename 1102 * string. 1103 * 1104 * Input: 1105 * v1, v2 void * Pointers to the pointers of two strings to be compared. 1106 * Output: 1107 * return int -1 -> v1 < v2. 1108 * 0 -> v1 == v2 1109 * 1 -> v1 > v2 1110 */ 1111 static int pca_cmp_file(const void *v1, const void *v2) 1112 { 1113 const char *file_name = (const char *) v1; 1114 const char **cache_name = (const char **) v2; 1115 return strcmp(file_name, *cache_name + 1); 1116 } 1117 1118 /*....................................................................... 1119 * The PcaPathConf structure may have options added to it in the future. 1120 * To allow your application to be linked against a shared version of the 1121 * tecla library, without these additions causing your application to 1122 * crash, you should use new_PcaPathConf() to allocate such structures. 1123 * This will set all of the configuration options to their default values, 1124 * which you can then change before passing the structure to 1125 * pca_path_completions(). 1126 * 1127 * Input: 1128 * pc PathCache * The filename cache in which to look for 1129 * file name completions. 1130 * Output: 1131 * return PcaPathConf * The new configuration structure, or NULL 1132 * on error. A descripition of the error 1133 * can be found by calling pca_last_error(pc). 1134 */ 1135 PcaPathConf *new_PcaPathConf(PathCache *pc) 1136 { 1137 PcaPathConf *ppc; /* The object to be returned */ 1138 /* 1139 * Check the arguments. 1140 */ 1141 if(!pc) 1142 return NULL; 1143 /* 1144 * Allocate the container. 1145 */ 1146 ppc = (PcaPathConf *)malloc(sizeof(PcaPathConf)); 1147 if(!ppc) { 1148 _err_record_msg(pc->err, "Insufficient memory.", END_ERR_MSG); 1149 return NULL; 1150 }; 1151 /* 1152 * Before attempting any operation that might fail, initialize the 1153 * container at least up to the point at which it can safely be passed 1154 * to del_PcaPathConf(). 1155 */ 1156 if(pca_init_PcaPathConf(ppc, pc)) 1157 return del_PcaPathConf(ppc); 1158 return ppc; 1159 } 1160 1161 /*....................................................................... 1162 * Initialize a PcaPathConf configuration structure with defaults. 1163 * 1164 * Input: 1165 * ppc PcaPathConf * The structre to be initialized. 1166 * pc PathCache * The cache in which completions will be looked up. 1167 * Output: 1168 * return int 0 - OK. 1169 * 1 - Error. A description of the error can be 1170 * obtained by calling pca_last_error(pc). 1171 */ 1172 static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc) 1173 { 1174 /* 1175 * Check the arguments. 1176 */ 1177 if(!pc) 1178 return 1; 1179 /* 1180 * Set the default options. 1181 */ 1182 ppc->id = PPC_ID_CODE; 1183 ppc->pc = pc; 1184 ppc->escaped = 1; 1185 ppc->file_start = -1; 1186 return 0; 1187 } 1188 1189 /*....................................................................... 1190 * Delete a PcaPathConf object. 1191 * 1192 * Input: 1193 * ppc PcaPathConf * The object to be deleted. 1194 * Output: 1195 * return PcaPathConf * The deleted object (always NULL). 1196 */ 1197 PcaPathConf *del_PcaPathConf(PcaPathConf *ppc) 1198 { 1199 if(ppc) { 1200 ppc->pc = NULL; /* It is up to the caller to delete the cache */ 1201 /* 1202 * Delete the container. 1203 */ 1204 free(ppc); 1205 }; 1206 return NULL; 1207 } 1208 1209 /*....................................................................... 1210 * pca_path_completions() is a completion callback function for use 1211 * directly with cpl_complete_word() or gl_customize_completions(), or 1212 * indirectly from your own completion callback function. It requires 1213 * that a CpaPathArgs object be passed via its 'void *data' argument. 1214 */ 1215 CPL_MATCH_FN(pca_path_completions) 1216 { 1217 PcaPathConf *ppc; /* The configuration arguments */ 1218 PathCache *pc; /* The cache in which to look for completions */ 1219 PathNode *node; /* A node in the list of directories in the path */ 1220 const char *filename; /* The name of the file being looked at */ 1221 const char *start_path; /* The pointer to the start of the pathname */ 1222 /* in line[]. */ 1223 int word_start; /* The index in line[] corresponding to start_path */ 1224 const char *prefix; /* The file-name prefix being searched for */ 1225 size_t prefix_len; /* The length of the prefix being completed */ 1226 int bot; /* The lowest index of the array not searched yet */ 1227 int top; /* The highest index of the array not searched yet */ 1228 /* 1229 * Check the arguments. 1230 */ 1231 if(!cpl) 1232 return 1; 1233 if(!line || word_end < 0 || !data) { 1234 cpl_record_error(cpl, "pca_path_completions: Invalid arguments."); 1235 return 1; 1236 }; 1237 /* 1238 * Get the configuration arguments. 1239 */ 1240 ppc = (PcaPathConf *) data; 1241 /* 1242 * Check that the callback data is a PcaPathConf structure returned 1243 * by new_PcaPathConf(). 1244 */ 1245 if(ppc->id != PPC_ID_CODE) { 1246 cpl_record_error(cpl, 1247 "Invalid callback data passed to pca_path_completions()"); 1248 return 1; 1249 }; 1250 /* 1251 * Get the filename cache. 1252 */ 1253 pc = ppc->pc; 1254 /* 1255 * Get the start of the file name. If not specified by the caller, 1256 * identify it by searching backwards in the input line for an 1257 * unescaped space or the start of the line. 1258 */ 1259 if(ppc->file_start < 0) { 1260 start_path = _pu_start_of_path(line, word_end); 1261 if(!start_path) { 1262 cpl_record_error(cpl, "Unable to find the start of the file name."); 1263 return 1; 1264 }; 1265 } else { 1266 start_path = line + ppc->file_start; 1267 }; 1268 /* 1269 * Get the index of the start of the word being completed. 1270 */ 1271 word_start = start_path - line; 1272 /* 1273 * Work out the length of the prefix that is bein completed. 1274 */ 1275 prefix_len = word_end - word_start; 1276 /* 1277 * If the word starts with a ~username expression or the root directory, 1278 * of it contains any directory separators, then completion must be 1279 * delegated to cpl_file_completions(). 1280 */ 1281 if(cpa_cmd_contains_path(start_path, prefix_len)) { 1282 cfc_file_start(pc->cfc, word_start); 1283 return cpl_file_completions(cpl, pc->cfc, line, word_end); 1284 }; 1285 /* 1286 * Look up the specified file name in each of the directories of the path, 1287 * in the same order that they were listed in the path, and stop as soon 1288 * as an instance of the file is found. 1289 */ 1290 for(node=pc->head; node; node=node->next) { 1291 /* 1292 * If the directory of the latest node is a relative pathname, 1293 * scan it for files of interest. 1294 */ 1295 if(node->relative) { 1296 rst_CacheMem(node->mem); 1297 if(pca_scan_dir(pc, node->dir, node->mem) < 1) 1298 continue; 1299 node->files = node->mem->files; 1300 node->nfile = node->mem->nfiles; 1301 }; 1302 /* 1303 * If needed, make a copy of the file-name being matched, with 1304 * escapes removed. Note that we need to do this anew every loop 1305 * iteration, because the above call to pca_scan_dir() uses 1306 * pc->path. 1307 */ 1308 prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); 1309 if(!prefix) 1310 return 1; 1311 /* 1312 * The directory entries are sorted, so we can perform a binary 1313 * search for an instance of the prefix being searched for. 1314 */ 1315 bot = 0; 1316 top = node->nfile - 1; 1317 while(top >= bot) { 1318 int mid = (top + bot)/2; 1319 int test = strncmp(node->files[mid]+1, prefix, prefix_len); 1320 if(test > 0) 1321 top = mid - 1; 1322 else if(test < 0) 1323 bot = mid + 1; 1324 else { 1325 top = bot = mid; 1326 break; 1327 }; 1328 }; 1329 /* 1330 * If we found a match, look to see if any of its neigbors also match. 1331 */ 1332 if(top == bot) { 1333 while(--bot >= 0 && strncmp(node->files[bot]+1, prefix, prefix_len) == 0) 1334 ; 1335 while(++top < node->nfile && 1336 strncmp(node->files[top]+1, prefix, prefix_len) == 0) 1337 ; 1338 /* 1339 * We will have gone one too far in each direction. 1340 */ 1341 bot++; 1342 top--; 1343 /* 1344 * Add the completions to the list after checking them against the 1345 * callers requirements. 1346 */ 1347 for( ; bot<=top; bot++) { 1348 char *match = node->files[bot]; 1349 /* 1350 * Form the full pathname of the file. 1351 */ 1352 _pn_clear_path(pc->path); 1353 if(_pn_append_to_path(pc->path, node->dir, -1, 0) == NULL || 1354 _pn_append_to_path(pc->path, match+1, -1, 0) == NULL) { 1355 _err_record_msg(pc->err, "Insufficient memory to complete file name", 1356 END_ERR_MSG); 1357 return 1; 1358 }; 1359 /* 1360 * Should the file be included in the list of completions? 1361 */ 1362 if(!pc->check_fn || match[0] == PCA_F_WANTED || 1363 (match[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))) { 1364 match[0] = PCA_F_WANTED; 1365 /* 1366 * Copy the completion suffix into the work pathname pc->path->name, 1367 * adding backslash escapes if needed. 1368 */ 1369 if(pca_prepare_suffix(pc, match + 1 + prefix_len, 1370 ppc->escaped)) 1371 return 1; 1372 /* 1373 * Record the completion. 1374 */ 1375 if(cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, 1376 "", " ")) 1377 return 1; 1378 /* 1379 * The file was rejected by the application. 1380 */ 1381 } else { 1382 match[0] = PCA_F_IGNORE; 1383 }; 1384 }; 1385 }; 1386 }; 1387 /* 1388 * We now need to search for subdirectories of the current directory which 1389 * have matching prefixes. First, if needed, make a copy of the word being 1390 * matched, with escapes removed. 1391 */ 1392 prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); 1393 if(!prefix) 1394 return 1; 1395 /* 1396 * Now open the current directory. 1397 */ 1398 if(_dr_open_dir(pc->dr, FS_PWD, NULL)) 1399 return 0; 1400 /* 1401 * Scan the current directory for sub-directories whos names start with 1402 * the prefix that we are completing. 1403 */ 1404 while((filename = _dr_next_file(pc->dr))) { 1405 /* 1406 * Does the latest filename match the prefix, and is it a directory? 1407 */ 1408 if(strncmp(filename, prefix, prefix_len) == 0 && _pu_path_is_dir(filename)){ 1409 /* 1410 * Record the completion. 1411 */ 1412 if(pca_prepare_suffix(pc, filename + prefix_len, ppc->escaped) || 1413 cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, 1414 FS_DIR_SEP, FS_DIR_SEP)) 1415 return 1; 1416 /* 1417 * The prefix in pc->path->name will have been overwritten by 1418 * pca_prepare_suffix(). Restore it here. 1419 */ 1420 prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); 1421 if(!prefix) 1422 return 1; 1423 }; 1424 }; 1425 _dr_close_dir(pc->dr); 1426 return 0; 1427 } 1428 1429 /*....................................................................... 1430 * Using the work buffer pc->path, make a suitably escaped copy of a 1431 * given completion suffix, ready to be passed to cpl_add_completion(). 1432 * 1433 * Input: 1434 * pc PathCache * The filename cache resource object. 1435 * suffix char * The suffix to be copied. 1436 * add_escapes int If true, escape special characters. 1437 * Output: 1438 * return int 0 - OK. 1439 * 1 - Error. 1440 */ 1441 static int pca_prepare_suffix(PathCache *pc, const char *suffix, 1442 int add_escapes) 1443 { 1444 const char *sptr; /* A pointer into suffix[] */ 1445 int nbsl; /* The number of backslashes to add to the suffix */ 1446 int i; 1447 /* 1448 * How long is the suffix? 1449 */ 1450 int suffix_len = strlen(suffix); 1451 /* 1452 * Clear the work buffer. 1453 */ 1454 _pn_clear_path(pc->path); 1455 /* 1456 * Count the number of backslashes that will have to be added to 1457 * escape spaces, tabs, backslashes and wildcard characters. 1458 */ 1459 nbsl = 0; 1460 if(add_escapes) { 1461 for(sptr = suffix; *sptr; sptr++) { 1462 switch(*sptr) { 1463 case ' ': case '\t': case '\\': case '*': case '?': case '[': 1464 nbsl++; 1465 break; 1466 }; 1467 }; 1468 }; 1469 /* 1470 * Arrange for the output path buffer to have sufficient room for the 1471 * both the suffix and any backslashes that have to be inserted. 1472 */ 1473 if(_pn_resize_path(pc->path, suffix_len + nbsl) == NULL) { 1474 _err_record_msg(pc->err, "Insufficient memory to complete file name", 1475 END_ERR_MSG); 1476 return 1; 1477 }; 1478 /* 1479 * If the suffix doesn't need any escapes, copy it directly into the 1480 * work buffer. 1481 */ 1482 if(nbsl==0) { 1483 strlcpy(pc->path->name, suffix, pc->path->dim); 1484 } else { 1485 /* 1486 * Make a copy with special characters escaped? 1487 */ 1488 if(nbsl > 0) { 1489 const char *src = suffix; 1490 char *dst = pc->path->name; 1491 for(i=0; i<suffix_len; i++) { 1492 switch(*src) { 1493 case ' ': case '\t': case '\\': case '*': case '?': case '[': 1494 *dst++ = '\\'; 1495 }; 1496 *dst++ = *src++; 1497 }; 1498 *dst = '\0'; 1499 }; 1500 }; 1501 return 0; 1502 } 1503 1504 /*....................................................................... 1505 * Return non-zero if the specified string appears to start with a pathname. 1506 * 1507 * Input: 1508 * prefix const char * The filename prefix to check. 1509 * prefix_len int The length of the prefix. 1510 * Output: 1511 * return int 0 - Doesn't start with a path name. 1512 * 1 - Does start with a path name. 1513 */ 1514 static int cpa_cmd_contains_path(const char *prefix, int prefix_len) 1515 { 1516 int i; 1517 /* 1518 * If the filename starts with a ~, then this implies a ~username 1519 * expression, which constitutes a pathname. 1520 */ 1521 if(*prefix == '~') 1522 return 1; 1523 /* 1524 * If the filename starts with the root directory, then it obviously 1525 * starts with a pathname. 1526 */ 1527 if(prefix_len >= FS_ROOT_DIR_LEN && 1528 strncmp(prefix, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) 1529 return 1; 1530 /* 1531 * Search the prefix for directory separators, returning as soon as 1532 * any are found, since their presence indicates that the filename 1533 * starts with a pathname specification (valid or otherwise). 1534 */ 1535 for(i=0; i<prefix_len; i++) { 1536 if(prefix_len - i >= FS_DIR_SEP_LEN && 1537 strncmp(prefix + i, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) 1538 return 1; 1539 }; 1540 /* 1541 * The file name doesn't appear to start with a pathname specification. 1542 */ 1543 return 0; 1544 } 1545 1546 /*....................................................................... 1547 * If needed make a new copy of the prefix being matched, in pc->path->name, 1548 * but with escapes removed. If no escapes are to be removed, simply return 1549 * the original prefix string. 1550 * 1551 * Input: 1552 * pc PathCache * The cache being searched. 1553 * prefix const char * The prefix to be processed. 1554 * prefix_len size_t The length of the prefix. 1555 * escaped int If true, return a copy with escapes removed. 1556 * Output: 1557 * return const char * The prepared prefix, or NULL on error, in 1558 * which case an error message will have been 1559 * left in pc->err. 1560 */ 1561 static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, 1562 size_t prefix_len, int escaped) 1563 { 1564 /* 1565 * Make a copy with escapes removed? 1566 */ 1567 if(escaped) { 1568 _pn_clear_path(pc->path); 1569 if(_pn_append_to_path(pc->path, prefix, prefix_len, 1) == NULL) { 1570 _err_record_msg(pc->err, "Insufficient memory to complete filename", 1571 END_ERR_MSG); 1572 return NULL; 1573 }; 1574 return pc->path->name; 1575 }; 1576 return prefix; 1577 } 1578 1579 /*....................................................................... 1580 * If backslashes in the filename should be treated as literal 1581 * characters, call the following function with literal=1. Otherwise 1582 * the default is to treat them as escape characters, used for escaping 1583 * spaces etc.. 1584 * 1585 * Input: 1586 * ppc PcaPathConf * The pca_path_completions() configuration object 1587 * to be configured. 1588 * literal int Pass non-zero here to enable literal interpretation 1589 * of backslashes. Pass 0 to turn off literal 1590 * interpretation. 1591 */ 1592 void ppc_literal_escapes(PcaPathConf *ppc, int literal) 1593 { 1594 if(ppc) 1595 ppc->escaped = !literal; 1596 } 1597 1598 /*....................................................................... 1599 * Call this function if you know where the index at which the 1600 * filename prefix starts in the input line. Otherwise by default, 1601 * or if you specify start_index to be -1, the filename is taken 1602 * to start after the first unescaped space preceding the cursor, 1603 * or the start of the line, which ever comes first. 1604 * 1605 * Input: 1606 * ppc PcaPathConf * The pca_path_completions() configuration object 1607 * to be configured. 1608 * start_index int The index of the start of the filename in 1609 * the input line, or -1 to select the default. 1610 */ 1611 void ppc_file_start(PcaPathConf *ppc, int start_index) 1612 { 1613 if(ppc) 1614 ppc->file_start = start_index; 1615 } 1616 1617 /*....................................................................... 1618 * Expand any ~user expression found at the start of a path, leaving 1619 * either an empty string in pc->path if there is no ~user expression, 1620 * or the corresponding home directory. 1621 * 1622 * Input: 1623 * pc PathCache * The filename cache. 1624 * path const char * The path to expand. 1625 * pathlen int The max number of characters to look at in path[]. 1626 * literal int If true, treat backslashes as literal characters 1627 * instead of escapes. 1628 * Input/Output: 1629 * endp const char * A pointer to the next unprocessed character in 1630 * path[] will be assigned to *endp. 1631 * Output: 1632 * return int 0 - OK 1633 * 1 - Error (a description will have been placed 1634 * in pc->err). 1635 */ 1636 static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, 1637 int literal, const char **endp) 1638 { 1639 const char *pptr = path; /* A pointer into path[] */ 1640 const char *homedir=NULL; /* A home directory */ 1641 /* 1642 * Clear the pathname buffer. 1643 */ 1644 _pn_clear_path(pc->path); 1645 /* 1646 * If the first character is a tilde, then perform home-directory 1647 * interpolation. 1648 */ 1649 if(*pptr == '~') { 1650 /* 1651 * Skip the tilde character and attempt to read the username that follows 1652 * it, into pc->usrnam[]. 1653 */ 1654 if(pca_read_username(pc, ++pptr, pathlen-1, literal, &pptr)) 1655 return 1; 1656 /* 1657 * Attempt to lookup the home directory of the user. 1658 */ 1659 homedir = _hd_lookup_home_dir(pc->home, pc->usrnam); 1660 if(!homedir) { 1661 _err_record_msg(pc->err, _hd_last_home_dir_error(pc->home), END_ERR_MSG); 1662 return 1; 1663 }; 1664 /* 1665 * Append the home directory to the pathname string. 1666 */ 1667 if(_pn_append_to_path(pc->path, homedir, -1, 0) == NULL) { 1668 _err_record_msg(pc->err, 1669 "Insufficient memory for home directory expansion", 1670 END_ERR_MSG); 1671 return 1; 1672 }; 1673 }; 1674 /* 1675 * ~user and ~ are usually followed by a directory separator to 1676 * separate them from the file contained in the home directory. 1677 * If the home directory is the root directory, then we don't want 1678 * to follow the home directory by a directory separator, so we should 1679 * skip over it so that it doesn't get copied into the output pathname 1680 */ 1681 if(homedir && strcmp(homedir, FS_ROOT_DIR) == 0 && 1682 (pptr-path) + FS_DIR_SEP_LEN < pathlen && 1683 strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { 1684 pptr += FS_DIR_SEP_LEN; 1685 }; 1686 /* 1687 * Return a pointer to the next unprocessed character. 1688 */ 1689 *endp = pptr; 1690 return 0; 1691 } 1692 1693 /*....................................................................... 1694 * Clear the filename status codes that are recorded before each filename 1695 * in the cache. 1696 * 1697 * Input: 1698 * pc PathCache * The filename cache. 1699 */ 1700 static void pca_remove_marks(PathCache *pc) 1701 { 1702 PathNode *node; /* A node in the list of directories in the path */ 1703 int i; 1704 /* 1705 * Traverse the absolute directories of the path, clearing the 1706 * filename status marks that precede each filename. 1707 */ 1708 for(node=pc->head; node; node=node->next) { 1709 if(!node->relative) { 1710 for(i=0; i<node->nfile; i++) 1711 *node->files[i] = PCA_F_ENIGMA; 1712 }; 1713 }; 1714 return; 1715 } 1716 1717 #endif /* ifndef WITHOUT_FILE_SYSTEM */ 1718