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 /* 44 * Standard includes. 45 */ 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <limits.h> 49 #include <errno.h> 50 #include <string.h> 51 #include <ctype.h> 52 53 /* 54 * Local includes. 55 */ 56 #include "libtecla.h" 57 #include "direader.h" 58 #include "homedir.h" 59 #include "pathutil.h" 60 #include "cplfile.h" 61 #include "errmsg.h" 62 63 /* 64 * Set the maximum length allowed for usernames. 65 * names. 66 */ 67 #define USR_LEN 100 68 69 /* 70 * Set the maximum length allowed for environment variable names. 71 */ 72 #define ENV_LEN 100 73 74 /* 75 * The resources needed to complete a filename are maintained in objects 76 * of the following type. 77 */ 78 struct CompleteFile { 79 ErrMsg *err; /* The error reporting buffer */ 80 DirReader *dr; /* A directory reader */ 81 HomeDir *home; /* A home directory expander */ 82 PathName *path; /* The buffer in which to accumulate the path */ 83 PathName *buff; /* A pathname work buffer */ 84 char usrnam[USR_LEN+1]; /* The buffer used when reading the names of */ 85 /* users. */ 86 char envnam[ENV_LEN+1]; /* The buffer used when reading the names of */ 87 /* environment variables. */ 88 }; 89 90 static int cf_expand_home_dir(CompleteFile *cf, const char *user); 91 static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, 92 const char *prefix, const char *line, 93 int word_start, int word_end, int escaped); 94 static HOME_DIR_FN(cf_homedir_callback); 95 static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, 96 const char *line, int word_start, int word_end, 97 int escaped, CplCheckFn *check_fn, 98 void *check_data); 99 static char *cf_read_name(CompleteFile *cf, const char *type, 100 const char *string, int slen, 101 char *nambuf, int nammax); 102 static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, 103 int add_escapes); 104 105 /* 106 * A stack based object of the following type is used to pass data to the 107 * cf_homedir_callback() function. 108 */ 109 typedef struct { 110 CompleteFile *cf; /* The file-completion resource object */ 111 WordCompletion *cpl; /* The string-completion rsource object */ 112 size_t prefix_len; /* The length of the prefix being completed */ 113 const char *line; /* The line from which the prefix was extracted */ 114 int word_start; /* The index in line[] of the start of the username */ 115 int word_end; /* The index in line[] following the end of the prefix */ 116 int escaped; /* If true, add escapes to the completion suffixes */ 117 } CfHomeArgs; 118 119 /*....................................................................... 120 * Create a new file-completion object. 121 * 122 * Output: 123 * return CompleteFile * The new object, or NULL on error. 124 */ 125 CompleteFile *_new_CompleteFile(void) 126 { 127 CompleteFile *cf; /* The object to be returned */ 128 /* 129 * Allocate the container. 130 */ 131 cf = (CompleteFile *) malloc(sizeof(CompleteFile)); 132 if(!cf) { 133 errno = ENOMEM; 134 return NULL; 135 }; 136 /* 137 * Before attempting any operation that might fail, initialize the 138 * container at least up to the point at which it can safely be passed 139 * to _del_CompleteFile(). 140 */ 141 cf->err = NULL; 142 cf->dr = NULL; 143 cf->home = NULL; 144 cf->path = NULL; 145 cf->buff = NULL; 146 cf->usrnam[0] = '\0'; 147 cf->envnam[0] = '\0'; 148 /* 149 * Allocate a place to record error messages. 150 */ 151 cf->err = _new_ErrMsg(); 152 if(!cf->err) 153 return _del_CompleteFile(cf); 154 /* 155 * Create the object that is used for reading directories. 156 */ 157 cf->dr = _new_DirReader(); 158 if(!cf->dr) 159 return _del_CompleteFile(cf); 160 /* 161 * Create the object that is used to lookup home directories. 162 */ 163 cf->home = _new_HomeDir(); 164 if(!cf->home) 165 return _del_CompleteFile(cf); 166 /* 167 * Create the buffer in which the completed pathname is accumulated. 168 */ 169 cf->path = _new_PathName(); 170 if(!cf->path) 171 return _del_CompleteFile(cf); 172 /* 173 * Create a pathname work buffer. 174 */ 175 cf->buff = _new_PathName(); 176 if(!cf->buff) 177 return _del_CompleteFile(cf); 178 return cf; 179 } 180 181 /*....................................................................... 182 * Delete a file-completion object. 183 * 184 * Input: 185 * cf CompleteFile * The object to be deleted. 186 * Output: 187 * return CompleteFile * The deleted object (always NULL). 188 */ 189 CompleteFile *_del_CompleteFile(CompleteFile *cf) 190 { 191 if(cf) { 192 cf->err = _del_ErrMsg(cf->err); 193 cf->dr = _del_DirReader(cf->dr); 194 cf->home = _del_HomeDir(cf->home); 195 cf->path = _del_PathName(cf->path); 196 cf->buff = _del_PathName(cf->buff); 197 free(cf); 198 }; 199 return NULL; 200 } 201 202 /*....................................................................... 203 * Look up the possible completions of the incomplete filename that 204 * lies between specified indexes of a given command-line string. 205 * 206 * Input: 207 * cpl WordCompletion * The object in which to record the completions. 208 * cf CompleteFile * The filename-completion resource object. 209 * line const char * The string containing the incomplete filename. 210 * word_start int The index of the first character in line[] 211 * of the incomplete filename. 212 * word_end int The index of the character in line[] that 213 * follows the last character of the incomplete 214 * filename. 215 * escaped int If true, backslashes in line[] are 216 * interpreted as escaping the characters 217 * that follow them, and any spaces, tabs, 218 * backslashes, or wildcard characters in the 219 * returned suffixes will be similarly escaped. 220 * If false, backslashes will be interpreted as 221 * literal parts of the file name, and no 222 * backslashes will be added to the returned 223 * suffixes. 224 * check_fn CplCheckFn * If not zero, this argument specifies a 225 * function to call to ask whether a given 226 * file should be included in the list 227 * of completions. 228 * check_data void * Anonymous data to be passed to check_fn(). 229 * Output: 230 * return int 0 - OK. 231 * 1 - Error. A description of the error can be 232 * acquired by calling _cf_last_error(cf). 233 */ 234 int _cf_complete_file(WordCompletion *cpl, CompleteFile *cf, 235 const char *line, int word_start, int word_end, 236 int escaped, CplCheckFn *check_fn, void *check_data) 237 { 238 const char *lptr; /* A pointer into line[] */ 239 int nleft; /* The number of characters still to be processed */ 240 /* in line[]. */ 241 /* 242 * Check the arguments. 243 */ 244 if(!cpl || !cf || !line || word_end < word_start) { 245 if(cf) { 246 _err_record_msg(cf->err, "_cf_complete_file: Invalid arguments", 247 END_ERR_MSG); 248 }; 249 return 1; 250 }; 251 /* 252 * Clear the buffer in which the filename will be constructed. 253 */ 254 _pn_clear_path(cf->path); 255 /* 256 * How many characters are to be processed? 257 */ 258 nleft = word_end - word_start; 259 /* 260 * Get a pointer to the start of the incomplete filename. 261 */ 262 lptr = line + word_start; 263 /* 264 * If the first character is a tilde, then perform home-directory 265 * interpolation. 266 */ 267 if(nleft > 0 && *lptr == '~') { 268 int slen; 269 if(!cf_read_name(cf, "User", ++lptr, --nleft, cf->usrnam, USR_LEN)) 270 return 1; 271 /* 272 * Advance over the username in the input line. 273 */ 274 slen = strlen(cf->usrnam); 275 lptr += slen; 276 nleft -= slen; 277 /* 278 * If we haven't hit the end of the input string then we have a complete 279 * username to translate to the corresponding home directory. 280 */ 281 if(nleft > 0) { 282 if(cf_expand_home_dir(cf, cf->usrnam)) 283 return 1; 284 /* 285 * ~user and ~ are usually followed by a directory separator to 286 * separate them from the file contained in the home directory. 287 * If the home directory is the root directory, then we don't want 288 * to follow the home directory by a directory separator, so we should 289 * skip over it so that it doesn't get copied into the filename. 290 */ 291 if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && 292 strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { 293 lptr += FS_DIR_SEP_LEN; 294 nleft -= FS_DIR_SEP_LEN; 295 }; 296 /* 297 * If we have reached the end of the input string, then the username 298 * may be incomplete, and we should attempt to complete it. 299 */ 300 } else { 301 /* 302 * Look up the possible completions of the username. 303 */ 304 return cf_complete_username(cf, cpl, cf->usrnam, line, word_start+1, 305 word_end, escaped); 306 }; 307 }; 308 /* 309 * Copy the rest of the path, stopping to expand $envvar expressions 310 * where encountered. 311 */ 312 while(nleft > 0) { 313 int seglen; /* The length of the next segment to be copied */ 314 /* 315 * Find the length of the next segment to be copied, stopping if an 316 * unescaped '$' is seen, or the end of the path is reached. 317 */ 318 for(seglen=0; seglen < nleft; seglen++) { 319 int c = lptr[seglen]; 320 if(escaped && c == '\\') 321 seglen++; 322 else if(c == '$') 323 break; 324 /* 325 * We will be completing the last component of the file name, 326 * so whenever a directory separator is seen, assume that it 327 * might be the start of the last component, and mark the character 328 * that follows it as the start of the name that is to be completed. 329 */ 330 if(nleft >= FS_DIR_SEP_LEN && 331 strncmp(lptr + seglen, FS_DIR_SEP, FS_DIR_SEP_LEN)==0) { 332 word_start = (lptr + seglen) - line + FS_DIR_SEP_LEN; 333 }; 334 }; 335 /* 336 * We have reached either the end of the filename or the start of 337 * $environment_variable expression. Record the newly checked 338 * segment of the filename in the output filename, removing 339 * backslash-escapes where needed. 340 */ 341 if(_pn_append_to_path(cf->path, lptr, seglen, escaped) == NULL) { 342 _err_record_msg(cf->err, "Insufficient memory to complete filename", 343 END_ERR_MSG); 344 return 1; 345 }; 346 lptr += seglen; 347 nleft -= seglen; 348 /* 349 * If the above loop finished before we hit the end of the filename, 350 * then this was because an unescaped $ was seen. In this case, interpolate 351 * the value of the environment variable that follows it into the output 352 * filename. 353 */ 354 if(nleft > 0) { 355 char *value; /* The value of the environment variable */ 356 int vlen; /* The length of the value string */ 357 int nlen; /* The length of the environment variable name */ 358 /* 359 * Read the name of the environment variable. 360 */ 361 if(!cf_read_name(cf, "Environment", ++lptr, --nleft, cf->envnam, ENV_LEN)) 362 return 1; 363 /* 364 * Advance over the environment variable name in the input line. 365 */ 366 nlen = strlen(cf->envnam); 367 lptr += nlen; 368 nleft -= nlen; 369 /* 370 * Get the value of the environment variable. 371 */ 372 value = getenv(cf->envnam); 373 if(!value) { 374 _err_record_msg(cf->err, "Unknown environment variable: ", cf->envnam, 375 END_ERR_MSG); 376 return 1; 377 }; 378 vlen = strlen(value); 379 /* 380 * If we are at the start of the filename and the first character of the 381 * environment variable value is a '~', attempt home-directory 382 * interpolation. 383 */ 384 if(cf->path->name[0] == '\0' && value[0] == '~') { 385 if(!cf_read_name(cf, "User", value+1, vlen-1, cf->usrnam, USR_LEN) || 386 cf_expand_home_dir(cf, cf->usrnam)) 387 return 1; 388 /* 389 * If the home directory is the root directory, and the ~usrname expression 390 * was followed by a directory separator, prevent the directory separator 391 * from being appended to the root directory by skipping it in the 392 * input line. 393 */ 394 if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && 395 strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { 396 lptr += FS_DIR_SEP_LEN; 397 nleft -= FS_DIR_SEP_LEN; 398 }; 399 } else { 400 /* 401 * Append the value of the environment variable to the output path. 402 */ 403 if(_pn_append_to_path(cf->path, value, strlen(value), escaped)==NULL) { 404 _err_record_msg(cf->err, "Insufficient memory to complete filename", 405 END_ERR_MSG); 406 return 1; 407 }; 408 /* 409 * Prevent extra directory separators from being added. 410 */ 411 if(nleft >= FS_DIR_SEP_LEN && 412 strcmp(cf->path->name, FS_ROOT_DIR) == 0 && 413 strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { 414 lptr += FS_DIR_SEP_LEN; 415 nleft -= FS_DIR_SEP_LEN; 416 } else if(vlen > FS_DIR_SEP_LEN && 417 strcmp(value + vlen - FS_DIR_SEP_LEN, FS_DIR_SEP)==0) { 418 cf->path->name[vlen-FS_DIR_SEP_LEN] = '\0'; 419 }; 420 }; 421 /* 422 * If adding the environment variable didn't form a valid directory, 423 * we can't complete the line, since there is no way to separate append 424 * a partial filename to an environment variable reference without 425 * that appended part of the name being seen later as part of the 426 * environment variable name. Thus if the currently constructed path 427 * isn't a directory, quite now with no completions having been 428 * registered. 429 */ 430 if(!_pu_path_is_dir(cf->path->name)) 431 return 0; 432 /* 433 * For the reasons given above, if we have reached the end of the filename 434 * with the expansion of an environment variable, the only allowed 435 * completion involves the addition of a directory separator. 436 */ 437 if(nleft == 0) { 438 if(cpl_add_completion(cpl, line, lptr-line, word_end, FS_DIR_SEP, 439 "", "")) { 440 _err_record_msg(cf->err, cpl_last_error(cpl), END_ERR_MSG); 441 return 1; 442 }; 443 return 0; 444 }; 445 }; 446 }; 447 /* 448 * Complete the filename if possible. 449 */ 450 return cf_complete_entry(cf, cpl, line, word_start, word_end, escaped, 451 check_fn, check_data); 452 } 453 454 /*....................................................................... 455 * Return a description of the last path-completion error that occurred. 456 * 457 * Input: 458 * cf CompleteFile * The path-completion resource object. 459 * Output: 460 * return const char * The description of the last error. 461 */ 462 const char *_cf_last_error(CompleteFile *cf) 463 { 464 return cf ? _err_get_msg(cf->err) : "NULL CompleteFile argument"; 465 } 466 467 /*....................................................................... 468 * Lookup the home directory of the specified user, or the current user 469 * if no name is specified, appending it to output pathname. 470 * 471 * Input: 472 * cf CompleteFile * The pathname completion resource object. 473 * user const char * The username to lookup, or "" to lookup the 474 * current user. 475 * Output: 476 * return int 0 - OK. 477 * 1 - Error. 478 */ 479 static int cf_expand_home_dir(CompleteFile *cf, const char *user) 480 { 481 /* 482 * Attempt to lookup the home directory. 483 */ 484 const char *home_dir = _hd_lookup_home_dir(cf->home, user); 485 /* 486 * Failed? 487 */ 488 if(!home_dir) { 489 _err_record_msg(cf->err, _hd_last_home_dir_error(cf->home), END_ERR_MSG); 490 return 1; 491 }; 492 /* 493 * Append the home directory to the pathname string. 494 */ 495 if(_pn_append_to_path(cf->path, home_dir, -1, 0) == NULL) { 496 _err_record_msg(cf->err, "Insufficient memory for home directory expansion", 497 END_ERR_MSG); 498 return 1; 499 }; 500 return 0; 501 } 502 503 /*....................................................................... 504 * Lookup and report all completions of a given username prefix. 505 * 506 * Input: 507 * cf CompleteFile * The filename-completion resource object. 508 * cpl WordCompletion * The object in which to record the completions. 509 * prefix const char * The prefix of the usernames to lookup. 510 * line const char * The command-line in which the username appears. 511 * word_start int The index within line[] of the start of the 512 * username that is being completed. 513 * word_end int The index within line[] of the character which 514 * follows the incomplete username. 515 * escaped int True if the completions need to have special 516 * characters escaped. 517 * Output: 518 * return int 0 - OK. 519 * 1 - Error. 520 */ 521 static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, 522 const char *prefix, const char *line, 523 int word_start, int word_end, int escaped) 524 { 525 /* 526 * Set up a container of anonymous arguments to be sent to the 527 * username-lookup iterator. 528 */ 529 CfHomeArgs args; 530 args.cf = cf; 531 args.cpl = cpl; 532 args.prefix_len = strlen(prefix); 533 args.line = line; 534 args.word_start = word_start; 535 args.word_end = word_end; 536 args.escaped = escaped; 537 /* 538 * Iterate through the list of users, recording those which start 539 * with the specified prefix. 540 */ 541 if(_hd_scan_user_home_dirs(cf->home, prefix, &args, cf_homedir_callback)) { 542 _err_record_msg(cf->err, _hd_last_home_dir_error(cf->home), END_ERR_MSG); 543 return 1; 544 }; 545 return 0; 546 } 547 548 /*....................................................................... 549 * The user/home-directory scanner callback function (see homedir.h) 550 * used by cf_complete_username(). 551 */ 552 static HOME_DIR_FN(cf_homedir_callback) 553 { 554 /* 555 * Get the file-completion resources from the anonymous data argument. 556 */ 557 CfHomeArgs *args = (CfHomeArgs *) data; 558 WordCompletion *cpl = args->cpl; 559 CompleteFile *cf = args->cf; 560 /* 561 * Copy the username into the pathname work buffer, adding backslash 562 * escapes where needed. 563 */ 564 if(cf_prepare_suffix(cf, usrnam+args->prefix_len, args->escaped)) { 565 strncpy(errmsg, _err_get_msg(cf->err), maxerr); 566 errmsg[maxerr] = '\0'; 567 return 1; 568 }; 569 /* 570 * Report the completion suffix that was copied above. 571 */ 572 if(cpl_add_completion(cpl, args->line, args->word_start, args->word_end, 573 cf->buff->name, FS_DIR_SEP, FS_DIR_SEP)) { 574 strncpy(errmsg, cpl_last_error(cpl), maxerr); 575 errmsg[maxerr] = '\0'; 576 return 1; 577 }; 578 return 0; 579 } 580 581 /*....................................................................... 582 * Report possible completions of the filename in cf->path->name[]. 583 * 584 * Input: 585 * cf CompleteFile * The file-completion resource object. 586 * cpl WordCompletion * The object in which to record the completions. 587 * line const char * The input line, as received by the callback 588 * function. 589 * word_start int The index within line[] of the start of the 590 * last component of the filename that is being 591 * completed. 592 * word_end int The index within line[] of the character which 593 * follows the incomplete filename. 594 * escaped int If true, escape special characters in the 595 * completion suffixes. 596 * check_fn CplCheckFn * If not zero, this argument specifies a 597 * function to call to ask whether a given 598 * file should be included in the list 599 * of completions. 600 * check_data void * Anonymous data to be passed to check_fn(). 601 * Output: 602 * return int 0 - OK. 603 * 1 - Error. 604 */ 605 static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, 606 const char *line, int word_start, int word_end, 607 int escaped, CplCheckFn *check_fn, 608 void *check_data) 609 { 610 const char *dirpath; /* The name of the parent directory */ 611 int start; /* The index of the start of the last filename */ 612 /* component in the transcribed filename. */ 613 const char *prefix; /* The filename prefix to be completed */ 614 int prefix_len; /* The length of the filename prefix */ 615 const char *file_name; /* The lastest filename being compared */ 616 int waserr = 0; /* True after errors */ 617 int terminated=0; /* True if the directory part had to be terminated */ 618 /* 619 * Get the pathname string and its current length. 620 */ 621 char *pathname = cf->path->name; 622 int pathlen = strlen(pathname); 623 /* 624 * Locate the start of the final component of the pathname. 625 */ 626 for(start=pathlen - 1; start >= 0 && 627 strncmp(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; start--) 628 ; 629 /* 630 * Is the parent directory the root directory? 631 */ 632 if(start==0 || 633 (start < 0 && strncmp(pathname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0)) { 634 dirpath = FS_ROOT_DIR; 635 start += FS_ROOT_DIR_LEN; 636 /* 637 * If we found a directory separator then the part which precedes the 638 * last component is the name of the directory to be opened. 639 */ 640 } else if(start > 0) { 641 /* 642 * The _dr_open_dir() function requires the directory name to be '\0' 643 * terminated, so temporarily do this by overwriting the first character 644 * of the directory separator. 645 */ 646 pathname[start] = '\0'; 647 dirpath = pathname; 648 terminated = 1; 649 /* 650 * We reached the start of the pathname before finding a directory 651 * separator, so arrange to open the current working directory. 652 */ 653 } else { 654 start = 0; 655 dirpath = FS_PWD; 656 }; 657 /* 658 * Attempt to open the directory. 659 */ 660 if(_dr_open_dir(cf->dr, dirpath, NULL)) { 661 _err_record_msg(cf->err, "Can't open directory: ", dirpath, END_ERR_MSG); 662 return 1; 663 }; 664 /* 665 * If removed above, restore the directory separator and skip over it 666 * to the start of the filename. 667 */ 668 if(terminated) { 669 memcpy(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN); 670 start += FS_DIR_SEP_LEN; 671 }; 672 /* 673 * Get the filename prefix and its length. 674 */ 675 prefix = pathname + start; 676 prefix_len = strlen(prefix); 677 /* 678 * Traverse the directory, looking for files who's prefixes match the 679 * last component of the pathname. 680 */ 681 while((file_name = _dr_next_file(cf->dr)) != NULL && !waserr) { 682 int name_len = strlen(file_name); 683 /* 684 * Is the latest filename a possible completion of the filename prefix? 685 */ 686 if(name_len >= prefix_len && strncmp(prefix, file_name, prefix_len)==0) { 687 /* 688 * When listing all files in a directory, don't list files that start 689 * with '.'. This is how hidden files are denoted in UNIX. 690 */ 691 if(prefix_len > 0 || file_name[0] != '.') { 692 /* 693 * Copy the completion suffix into the work pathname cf->buff->name, 694 * adding backslash escapes if needed. 695 */ 696 if(cf_prepare_suffix(cf, file_name + prefix_len, escaped)) { 697 waserr = 1; 698 } else { 699 /* 700 * We want directories to be displayed with directory suffixes, 701 * and other fully completed filenames to be followed by spaces. 702 * To check the type of the file, append the current suffix 703 * to the path being completed, check the filetype, then restore 704 * the path to its original form. 705 */ 706 const char *cont_suffix = ""; /* The suffix to add if fully */ 707 /* completed. */ 708 const char *type_suffix = ""; /* The suffix to add when listing */ 709 if(_pn_append_to_path(cf->path, file_name + prefix_len, 710 -1, escaped) == NULL) { 711 _err_record_msg(cf->err, 712 "Insufficient memory to complete filename.", 713 END_ERR_MSG); 714 return 1; 715 }; 716 /* 717 * Specify suffixes according to the file type. 718 */ 719 if(_pu_path_is_dir(cf->path->name)) { 720 cont_suffix = FS_DIR_SEP; 721 type_suffix = FS_DIR_SEP; 722 } else if(!check_fn || check_fn(check_data, cf->path->name)) { 723 cont_suffix = " "; 724 } else { 725 cf->path->name[pathlen] = '\0'; 726 continue; 727 }; 728 /* 729 * Remove the temporarily added suffix. 730 */ 731 cf->path->name[pathlen] = '\0'; 732 /* 733 * Record the latest completion. 734 */ 735 if(cpl_add_completion(cpl, line, word_start, word_end, cf->buff->name, 736 type_suffix, cont_suffix)) 737 waserr = 1; 738 }; 739 }; 740 }; 741 }; 742 /* 743 * Close the directory. 744 */ 745 _dr_close_dir(cf->dr); 746 return waserr; 747 } 748 749 /*....................................................................... 750 * Read a username or environment variable name, stopping when a directory 751 * separator is seen, when the end of the string is reached, or the 752 * output buffer overflows. 753 * 754 * Input: 755 * cf CompleteFile * The file-completion resource object. 756 * type char * The capitalized name of the type of name being read. 757 * string char * The string who's prefix contains the name. 758 * slen int The number of characters in string[]. 759 * nambuf char * The output name buffer. 760 * nammax int The longest string that will fit in nambuf[], excluding 761 * the '\0' terminator. 762 * Output: 763 * return char * A pointer to nambuf on success. On error NULL is 764 * returned and a description of the error is recorded 765 * in cf->err. 766 */ 767 static char *cf_read_name(CompleteFile *cf, const char *type, 768 const char *string, int slen, 769 char *nambuf, int nammax) 770 { 771 int namlen; /* The number of characters in nambuf[] */ 772 const char *sptr; /* A pointer into string[] */ 773 /* 774 * Work out the max number of characters that should be copied. 775 */ 776 int nmax = nammax < slen ? nammax : slen; 777 /* 778 * Get the environment variable name that follows the dollar. 779 */ 780 for(sptr=string,namlen=0; 781 namlen < nmax && (slen-namlen < FS_DIR_SEP_LEN || 782 strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0); 783 namlen++) { 784 nambuf[namlen] = *sptr++; 785 }; 786 /* 787 * Did the name overflow the buffer? 788 */ 789 if(namlen >= nammax) { 790 _err_record_msg(cf->err, type, " name too long", END_ERR_MSG); 791 return NULL; 792 }; 793 /* 794 * Terminate the string. 795 */ 796 nambuf[namlen] = '\0'; 797 return nambuf; 798 } 799 800 /*....................................................................... 801 * Using the work buffer cf->buff, make a suitably escaped copy of a 802 * given completion suffix, ready to be passed to cpl_add_completion(). 803 * 804 * Input: 805 * cf CompleteFile * The file-completion resource object. 806 * suffix char * The suffix to be copied. 807 * add_escapes int If true, escape special characters. 808 * Output: 809 * return int 0 - OK. 810 * 1 - Error. 811 */ 812 static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, 813 int add_escapes) 814 { 815 const char *sptr; /* A pointer into suffix[] */ 816 int nbsl; /* The number of backslashes to add to the suffix */ 817 int i; 818 /* 819 * How long is the suffix? 820 */ 821 int suffix_len = strlen(suffix); 822 /* 823 * Clear the work buffer. 824 */ 825 _pn_clear_path(cf->buff); 826 /* 827 * Count the number of backslashes that will have to be added to 828 * escape spaces, tabs, backslashes and wildcard characters. 829 */ 830 nbsl = 0; 831 if(add_escapes) { 832 for(sptr = suffix; *sptr; sptr++) { 833 switch(*sptr) { 834 case ' ': case '\t': case '\\': case '*': case '?': case '[': 835 nbsl++; 836 break; 837 }; 838 }; 839 }; 840 /* 841 * Arrange for the output path buffer to have sufficient room for the 842 * both the suffix and any backslashes that have to be inserted. 843 */ 844 if(_pn_resize_path(cf->buff, suffix_len + nbsl) == NULL) { 845 _err_record_msg(cf->err, "Insufficient memory to complete filename", 846 END_ERR_MSG); 847 return 1; 848 }; 849 /* 850 * If the suffix doesn't need any escapes, copy it directly into the 851 * work buffer. 852 */ 853 if(nbsl==0) { 854 strlcpy(cf->buff->name, suffix, cf->buff->dim); 855 } else { 856 /* 857 * Make a copy with special characters escaped? 858 */ 859 if(nbsl > 0) { 860 const char *src = suffix; 861 char *dst = cf->buff->name; 862 for(i=0; i<suffix_len; i++) { 863 switch(*src) { 864 case ' ': case '\t': case '\\': case '*': case '?': case '[': 865 *dst++ = '\\'; 866 }; 867 *dst++ = *src++; 868 }; 869 *dst = '\0'; 870 }; 871 }; 872 return 0; 873 } 874 875 #endif /* ifndef WITHOUT_FILE_SYSTEM */ 876