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