2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
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.
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.
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.
33 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
34 * Use is subject to license terms.
37 #pragma ident "%Z%%M% %I% %E% SMI"
40 * If file-system access is to be excluded, this module has no function,
41 * so all of its code should be excluded.
43 #ifndef WITHOUT_FILE_SYSTEM
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.
63 #define PPC_ID_CODE 4567
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.
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. */
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
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 */
103 * Encapsulate the memory management objects which supply memoy for
104 * the arrays of filenames.
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[] */
113 static CacheMem
*new_CacheMem(void);
114 static CacheMem
*del_CacheMem(CacheMem
*cm
);
115 static void rst_CacheMem(CacheMem
*cm
);
118 * Lists of nodes of the following type are used to record the
119 * names and contents of individual directories.
121 typedef struct PathNode 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 */
136 * Append a new node to the list of directories in the path.
138 static int add_PathNode(PathCache
*pc
, const char *dirname
);
141 * Set the maximum length allowed for usernames.
147 * PathCache objects encapsulate the resources needed to record
148 * files of interest from comma-separated lists of directories.
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 */
174 static void pca_clear_cache(PathCache
*pc
);
177 * Read a username from string[] and record it in pc->usrnam[].
179 static int pca_read_username(PathCache
*pc
, const char *string
, int slen
,
180 int literal
, const char **nextp
);
183 * Extract the next component of a colon separated list of directory
186 static int pca_extract_dir(PathCache
*pc
, const char *path
,
190 * Scan absolute directories for files of interest, recording their names
191 * in mem->sg and recording pointers to these names in mem->files[].
193 static int pca_scan_dir(PathCache
*pc
, const char *dirname
, CacheMem
*mem
);
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.
200 static int pca_cmp_matches(const void *v1
, const void *v2
);
203 * A qsort() comparison function for comparing a filename
204 * against an element of an array of pointers to filename cache
207 static int pca_cmp_file(const void *v1
, const void *v2
);
210 * Initialize a PcaPathConf configuration objects with the default
213 static int pca_init_PcaPathConf(PcaPathConf
*ppc
, PathCache
*pc
);
216 * Make a copy of a completion suffix, suitable for passing to
217 * cpl_add_completion().
219 static int pca_prepare_suffix(PathCache
*pc
, const char *suffix
,
223 * Return non-zero if the specified string appears to start with a pathname.
225 static int cpa_cmd_contains_path(const char *prefix
, int prefix_len
);
228 * Return a given prefix with escapes optionally removed.
230 static const char *pca_prepare_prefix(PathCache
*pc
, const char *prefix
,
231 size_t prefix_len
, int escaped
);
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.
238 static int pca_expand_tilde(PathCache
*pc
, const char *path
, int pathlen
,
239 int literal
, const char **endp
);
242 * Clear the filename status codes that are recorded before each filename
245 static void pca_remove_marks(PathCache
*pc
);
248 * Specify how many PathNode's to allocate at a time.
250 #define PATH_NODE_BLK 30
253 * Specify the amount by which the files[] arrays are to be extended
254 * whenever they are found to be too small.
256 #define FILES_BLK_FACT 256
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.
264 * return PathCache * The new, initially empty cache, or NULL
267 PathCache
*new_PathCache(void)
269 PathCache
*pc
; /* The object to be returned */
271 * Allocate the container.
273 pc
= (PathCache
*)malloc(sizeof(PathCache
));
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().
295 pc
->usrnam
[0] = '\0';
297 * Allocate a place to record error messages.
299 pc
->err
= _new_ErrMsg();
301 return del_PathCache(pc
);
303 * Allocate the freelist of directory list nodes.
305 pc
->node_mem
= _new_FreeList(sizeof(PathNode
), PATH_NODE_BLK
);
307 return del_PathCache(pc
);
309 * Allocate memory for recording names of files in absolute paths.
311 pc
->abs_mem
= new_CacheMem();
313 return del_PathCache(pc
);
315 * Allocate memory for recording names of files in relative paths.
317 pc
->rel_mem
= new_CacheMem();
319 return del_PathCache(pc
);
321 * Allocate a pathname buffer.
323 pc
->path
= _new_PathName();
325 return del_PathCache(pc
);
327 * Allocate an object for looking up home-directories.
329 pc
->home
= _new_HomeDir();
331 return del_PathCache(pc
);
333 * Allocate an object for reading directories.
335 pc
->dr
= _new_DirReader();
337 return del_PathCache(pc
);
339 * Allocate a cpl_file_completions() configuration object.
341 pc
->cfc
= new_CplFileConf();
343 return del_PathCache(pc
);
345 * Configure cpl_file_completions() to use check_fn() to select
348 cfc_set_check_fn(pc
->cfc
, pc
->check_fn
, pc
->data
);
350 * Return the cache, ready for use.
355 /*.......................................................................
356 * Delete a given cache of files, returning the resources that it
357 * was using to the system.
360 * pc PathCache * The cache to be deleted (can be NULL).
362 * return PathCache * The deleted object (ie. allways NULL).
364 PathCache
*del_PathCache(PathCache
*pc
)
368 * Delete the error message buffer.
370 pc
->err
= _del_ErrMsg(pc
->err
);
372 * Delete the memory of the list of path nodes.
374 pc
->node_mem
= _del_FreeList(pc
->node_mem
, 1);
376 * Delete the memory used to record filenames.
378 pc
->abs_mem
= del_CacheMem(pc
->abs_mem
);
379 pc
->rel_mem
= del_CacheMem(pc
->rel_mem
);
381 * The list of PathNode's was already deleted when node_mem was
387 * Delete the pathname buffer.
389 pc
->path
= _del_PathName(pc
->path
);
391 * Delete the home-directory lookup object.
393 pc
->home
= _del_HomeDir(pc
->home
);
395 * Delete the directory reader.
397 pc
->dr
= _del_DirReader(pc
->dr
);
399 * Delete the cpl_file_completions() config object.
401 pc
->cfc
= del_CplFileConf(pc
->cfc
);
403 * Delete the container.
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.
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,
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.
432 void pca_set_check_fn(PathCache
*pc
, CplCheckFn
*check_fn
, void *data
)
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
440 if(check_fn
!= pc
->check_fn
|| data
!= pc
->data
)
441 pca_remove_marks(pc
);
443 * Record the new callback locally.
445 pc
->check_fn
= check_fn
;
448 * Configure cpl_file_completions() to use the same callback to
449 * select files of interest.
451 cfc_set_check_fn(pc
->cfc
, check_fn
, data
);
456 /*.......................................................................
457 * Return a description of the last path-caching error that occurred.
460 * pc PathCache * The filename cache that suffered the error.
462 * return char * The description of the last error.
464 const char *pca_last_error(PathCache
*pc
)
466 return pc
? _err_get_msg(pc
->err
) : "NULL PathCache argument";
469 /*.......................................................................
470 * Discard all cached filenames.
473 * pc PathCache * The cache to be cleared.
475 static void pca_clear_cache(PathCache
*pc
)
479 * Return all path-nodes to the freelist.
481 _rst_FreeList(pc
->node_mem
);
482 pc
->head
= pc
->tail
= NULL
;
484 * Delete all filename strings.
486 rst_CacheMem(pc
->abs_mem
);
487 rst_CacheMem(pc
->rel_mem
);
492 /*.......................................................................
493 * Build the list of files of interest contained in a given
494 * colon-separated list of directories.
497 * pc PathCache * The cache in which to store the names of
498 * the files that are found in the list of
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").
506 * 1 - An error occurred. A description of
507 * the error can be acquired by calling
508 * pca_last_error(pc).
510 int pca_scan_path(PathCache
*pc
, const char *path
)
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[] */
516 * Check the arguments.
521 * Clear the outdated contents of the cache.
525 * If no path list was provided, there is nothing to be added to the
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.
539 * Extract the next pathname component into pc->path->name.
541 if(pca_extract_dir(pc
, pptr
, &pptr
))
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.
548 if(add_PathNode(pc
, pc
->path
->name
))
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.
559 fptr
= pc
->abs_mem
->files
;
560 for(node
=pc
->head
; node
; node
=node
->next
) {
567 /*.......................................................................
568 * Extract the next directory path from a colon-separated list of
569 * directories, expanding tilde home-directory expressions where needed.
572 * pc PathCache * The cache of filenames.
573 * path const char * A pointer to the start of the next component
576 * nextp const char ** A pointer to the next unprocessed character
577 * in path[] will be assigned to *nextp.
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.
583 static int pca_extract_dir(PathCache
*pc
, const char *path
, const char **nextp
)
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 */
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.
593 if(pca_expand_tilde(pc
, path
, strlen(path
), 0, &pptr
))
596 * Keep a record of the current location in the path.
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
604 while(*pptr
&& (escaped
|| *pptr
!= ':'))
605 escaped
= !escaped
&& *pptr
++ == '\\';
607 * Append the rest of the directory path to the pathname buffer.
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",
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.
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",
632 * Skip the separator unless we have reached the end of the path.
637 * Return the unprocessed tail of the path-list string.
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
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.
655 * nextp char ** A pointer to the next unprocessed character
656 * in string[] will be assigned to *nextp.
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.
662 static int pca_read_username(PathCache
*pc
, const char *string
, int slen
,
663 int literal
, const char **nextp
)
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 */
669 * Extract the username.
671 for(sptr
=string
,usrlen
=0; usrlen
< USR_LEN
&& (sptr
-string
) < slen
; sptr
++) {
673 * Stop if the end of the string is reached, or a directory separator
674 * or un-escaped colon separator is seen.
676 if(!*sptr
|| strncmp(sptr
, FS_DIR_SEP
, FS_DIR_SEP_LEN
)==0 ||
677 (!escaped
&& *sptr
== ':'))
680 * Escape the next character?
682 if(!literal
&& !escaped
&& *sptr
== '\\') {
686 pc
->usrnam
[usrlen
++] = *sptr
;
690 * Did the username overflow the buffer?
692 if(usrlen
>= USR_LEN
) {
693 _err_record_msg(pc
->err
, "Username too long", END_ERR_MSG
);
697 * Terminate the string.
699 pc
->usrnam
[usrlen
] = '\0';
701 * Indicate where processing of the input string should continue.
708 /*.......................................................................
709 * Create a new CacheMem object.
712 * return CacheMem * The new object, or NULL on error.
714 static CacheMem
*new_CacheMem(void)
716 CacheMem
*cm
; /* The object to be returned */
718 * Allocate the container.
720 cm
= (CacheMem
*)malloc(sizeof(CacheMem
));
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
735 * Allocate a list of string segments for storing filenames.
737 cm
->sg
= _new_StringGroup(_pu_pathname_dim());
739 return del_CacheMem(cm
);
741 * Allocate an array of pointers to filenames.
742 * This will be extended later if needed.
744 cm
->files_dim
= FILES_BLK_FACT
;
745 cm
->files
= (char **) malloc(sizeof(*cm
->files
) * cm
->files_dim
);
748 return del_CacheMem(cm
);
753 /*.......................................................................
754 * Delete a CacheMem object.
757 * cm CacheMem * The object to be deleted.
759 * return CacheMem * The deleted object (always NULL).
761 static CacheMem
*del_CacheMem(CacheMem
*cm
)
765 * Delete the memory that was used to record filename strings.
767 cm
->sg
= _del_StringGroup(cm
->sg
);
769 * Delete the array of pointers to filenames.
777 * Delete the container.
784 /*.......................................................................
785 * Re-initialize the memory used to allocate filename strings.
788 * cm CacheMem * The memory cache to be cleared.
790 static void rst_CacheMem(CacheMem
*cm
)
792 _clr_StringGroup(cm
->sg
);
797 /*.......................................................................
798 * Append a new directory node to the list of directories read from the
802 * pc PathCache * The filename cache.
803 * dirname const char * The name of the new directory.
808 static int add_PathNode(PathCache
*pc
, const char *dirname
)
810 PathNode
*node
; /* The new directory list node */
811 int relative
; /* True if dirname[] is a relative pathname */
813 * Have we been passed a relative pathname or an absolute pathname?
815 relative
= strncmp(dirname
, FS_ROOT_DIR
, FS_ROOT_DIR_LEN
) != 0;
817 * If it's an absolute pathname, ignore it if the corresponding
818 * directory doesn't exist.
820 if(!relative
&& !_pu_path_is_dir(dirname
))
823 * Allocate a new list node to record the specifics of the new directory.
825 node
= (PathNode
*) _new_FreeListNode(pc
->node_mem
);
827 _err_record_msg(pc
->err
, "Insufficient memory to cache new directory.",
832 * Initialize the node.
835 node
->relative
= relative
;
836 node
->mem
= relative
? pc
->rel_mem
: pc
->abs_mem
;
841 * Make a copy of the directory pathname.
843 node
->dir
= _sg_store_string(pc
->abs_mem
->sg
, dirname
, 0);
845 _err_record_msg(pc
->err
, "Insufficient memory to store directory name.",
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.
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
);
862 * Append the new node to the list.
865 pc
->tail
->next
= node
;
868 pc
->head
= pc
->tail
= node
;
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.
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
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.
888 static int pca_scan_dir(PathCache
*pc
, const char *dirname
, CacheMem
*mem
)
890 int nfile
= 0; /* The number of filenames recorded */
891 const char *filename
; /* The name of the file being looked at */
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.
896 if(_dr_open_dir(pc
->dr
, dirname
, NULL
))
899 * Record the names of all files in the directory in the cache.
901 while((filename
= _dr_next_file(pc
->dr
))) {
902 char *copy
; /* A copy of the filename */
904 * Make a temporary copy of the filename with an extra byte prepended.
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",
914 * Store the filename.
916 copy
= _sg_store_string(mem
->sg
, pc
->path
->name
, 0);
918 _err_record_msg(pc
->err
, "Insufficient memory to cache file name.",
923 * Mark the filename as unchecked.
925 copy
[0] = PCA_F_ENIGMA
;
927 * Make room to store a pointer to the copy in mem->files[].
929 if(mem
->nfiles
+ 1 > mem
->files_dim
) {
930 int needed
= mem
->files_dim
+ FILES_BLK_FACT
;
931 char **files
= (char **) reallocarray(mem
->files
, needed
,
932 sizeof(*mem
->files
));
934 _err_record_msg(pc
->err
,
935 "Insufficient memory to extend filename cache.",
940 mem
->files_dim
= needed
;
943 * Record a pointer to the copy of the filename at the end of the files[]
946 mem
->files
[mem
->nfiles
++] = copy
;
948 * Keep a record of the number of files matched so far.
953 * Sort the list of files into lexical order.
955 qsort(mem
->files
+ mem
->nfiles
- nfile
, nfile
, sizeof(*mem
->files
),
958 * Return the number of files recorded in mem->files[].
963 /*.......................................................................
964 * A qsort() comparison function for comparing the cached filename
965 * strings pointed to by two (char **) array elements. Note that
966 * this ignores the initial cache-status byte of each filename.
969 * v1, v2 void * Pointers to the pointers of two strings to be compared.
971 * return int -1 -> v1 < v2.
975 static int pca_cmp_matches(const void *v1
, const void *v2
)
977 const char **s1
= (const char **) v1
;
978 const char **s2
= (const char **) v2
;
979 return strcmp(*s1
+1, *s2
+1);
982 /*.......................................................................
983 * Given the simple name of a file, search the cached list of files
984 * in the order in which they where found in the list of directories
985 * previously presented to pca_scan_path(), and return the pathname
986 * of the first file which has this name. If a pathname to a file is
987 * given instead of a simple filename, this is returned without being
988 * looked up in the cache, but with any initial ~username expression
989 * expanded, and optionally, unescaped backslashes removed.
992 * pc PathCache * The cached list of files.
993 * name const char * The name of the file to lookup.
994 * name_len int The length of the filename string at the
995 * beginning of name[], or -1 to indicate that
996 * the filename occupies the whole of the
998 * literal int If this argument is zero, lone backslashes
999 * in name[] are ignored during comparison
1000 * with filenames in the cache, under the
1001 * assumption that they were in the input line
1002 * soley to escape the special significance of
1003 * characters like spaces. To have them treated
1004 * as normal characters, give this argument a
1005 * non-zero value, such as 1.
1007 * return char * The pathname of the first matching file,
1008 * or NULL if not found. Note that the returned
1009 * pointer points to memory owned by *pc, and
1010 * will become invalid on the next call to any
1011 * function in the PathCache module.
1013 char *pca_lookup_file(PathCache
*pc
, const char *name
, int name_len
,
1016 PathNode
*node
; /* A node in the list of directories in the path */
1017 char **match
; /* A pointer to a matching filename string in the cache */
1019 * Check the arguments.
1021 if(!pc
|| !name
|| name_len
==0)
1024 * If no length was specified, determine the length of the string to
1028 name_len
= strlen(name
);
1030 * If the word starts with a ~username expression, the root directory,
1031 * of it contains any directory separators, then treat it isn't a simple
1032 * filename that can be looked up in the cache, but rather appears to
1033 * be the pathname of a file. If so, return a copy of this pathname with
1034 * escapes removed, if requested, and any initial ~username expression
1037 if(cpa_cmd_contains_path(name
, name_len
)) {
1039 if(pca_expand_tilde(pc
, name
, name_len
, literal
, &nptr
) ||
1040 _pn_append_to_path(pc
->path
, nptr
, name_len
- (nptr
-name
),
1043 return pc
->path
->name
;
1046 * Look up the specified filename in each of the directories of the path,
1047 * in the same order that they were listed in the path, and stop as soon
1048 * as an instance of the file is found.
1050 for(node
=pc
->head
; node
; node
=node
->next
) {
1052 * If the directory of the latest node is a relative pathname,
1053 * scan it for files of interest.
1055 if(node
->relative
) {
1056 rst_CacheMem(node
->mem
);
1057 if(pca_scan_dir(pc
, node
->dir
, node
->mem
) < 1)
1059 node
->files
= node
->mem
->files
;
1060 node
->nfile
= node
->mem
->nfiles
;
1063 * Copy the filename into a temporary buffer, while interpretting
1064 * escape characters if needed.
1066 _pn_clear_path(pc
->path
);
1067 if(_pn_append_to_path(pc
->path
, name
, name_len
, !literal
) == NULL
)
1070 * Perform a binary search for the requested filename.
1072 match
= (char **)bsearch(pc
->path
->name
, node
->files
, node
->nfile
,
1073 sizeof(*node
->files
), pca_cmp_file
);
1076 * Prepend the pathname in which the directory was found, which we have
1077 * guaranteed to end in a directory separator, to the located filename.
1079 if(_pn_prepend_to_path(pc
->path
, node
->dir
, -1, 0) == NULL
)
1082 * Return the matching pathname unless it is rejected by the application.
1084 if(!pc
->check_fn
|| (*match
)[0] == PCA_F_WANTED
||
1085 ((*match
)[0]==PCA_F_ENIGMA
&& pc
->check_fn(pc
->data
, pc
->path
->name
))){
1086 (*match
)[0] = PCA_F_WANTED
;
1087 return pc
->path
->name
;
1089 *(match
)[0] = PCA_F_IGNORE
;
1099 /*.......................................................................
1100 * A qsort() comparison function for comparing a filename string to
1101 * a cached filename string pointed to by a (char **) array element.
1102 * This ignores the initial code byte at the start of the cached filename
1106 * v1, v2 void * Pointers to the pointers of two strings to be compared.
1108 * return int -1 -> v1 < v2.
1112 static int pca_cmp_file(const void *v1
, const void *v2
)
1114 const char *file_name
= (const char *) v1
;
1115 const char **cache_name
= (const char **) v2
;
1116 return strcmp(file_name
, *cache_name
+ 1);
1119 /*.......................................................................
1120 * The PcaPathConf structure may have options added to it in the future.
1121 * To allow your application to be linked against a shared version of the
1122 * tecla library, without these additions causing your application to
1123 * crash, you should use new_PcaPathConf() to allocate such structures.
1124 * This will set all of the configuration options to their default values,
1125 * which you can then change before passing the structure to
1126 * pca_path_completions().
1129 * pc PathCache * The filename cache in which to look for
1130 * file name completions.
1132 * return PcaPathConf * The new configuration structure, or NULL
1133 * on error. A descripition of the error
1134 * can be found by calling pca_last_error(pc).
1136 PcaPathConf
*new_PcaPathConf(PathCache
*pc
)
1138 PcaPathConf
*ppc
; /* The object to be returned */
1140 * Check the arguments.
1145 * Allocate the container.
1147 ppc
= (PcaPathConf
*)malloc(sizeof(PcaPathConf
));
1149 _err_record_msg(pc
->err
, "Insufficient memory.", END_ERR_MSG
);
1153 * Before attempting any operation that might fail, initialize the
1154 * container at least up to the point at which it can safely be passed
1155 * to del_PcaPathConf().
1157 if(pca_init_PcaPathConf(ppc
, pc
))
1158 return del_PcaPathConf(ppc
);
1162 /*.......................................................................
1163 * Initialize a PcaPathConf configuration structure with defaults.
1166 * ppc PcaPathConf * The structre to be initialized.
1167 * pc PathCache * The cache in which completions will be looked up.
1169 * return int 0 - OK.
1170 * 1 - Error. A description of the error can be
1171 * obtained by calling pca_last_error(pc).
1173 static int pca_init_PcaPathConf(PcaPathConf
*ppc
, PathCache
*pc
)
1176 * Check the arguments.
1181 * Set the default options.
1183 ppc
->id
= PPC_ID_CODE
;
1186 ppc
->file_start
= -1;
1190 /*.......................................................................
1191 * Delete a PcaPathConf object.
1194 * ppc PcaPathConf * The object to be deleted.
1196 * return PcaPathConf * The deleted object (always NULL).
1198 PcaPathConf
*del_PcaPathConf(PcaPathConf
*ppc
)
1201 ppc
->pc
= NULL
; /* It is up to the caller to delete the cache */
1203 * Delete the container.
1210 /*.......................................................................
1211 * pca_path_completions() is a completion callback function for use
1212 * directly with cpl_complete_word() or gl_customize_completions(), or
1213 * indirectly from your own completion callback function. It requires
1214 * that a CpaPathArgs object be passed via its 'void *data' argument.
1216 CPL_MATCH_FN(pca_path_completions
)
1218 PcaPathConf
*ppc
; /* The configuration arguments */
1219 PathCache
*pc
; /* The cache in which to look for completions */
1220 PathNode
*node
; /* A node in the list of directories in the path */
1221 const char *filename
; /* The name of the file being looked at */
1222 const char *start_path
; /* The pointer to the start of the pathname */
1224 int word_start
; /* The index in line[] corresponding to start_path */
1225 const char *prefix
; /* The file-name prefix being searched for */
1226 size_t prefix_len
; /* The length of the prefix being completed */
1227 int bot
; /* The lowest index of the array not searched yet */
1228 int top
; /* The highest index of the array not searched yet */
1230 * Check the arguments.
1234 if(!line
|| word_end
< 0 || !data
) {
1235 cpl_record_error(cpl
, "pca_path_completions: Invalid arguments.");
1239 * Get the configuration arguments.
1241 ppc
= (PcaPathConf
*) data
;
1243 * Check that the callback data is a PcaPathConf structure returned
1244 * by new_PcaPathConf().
1246 if(ppc
->id
!= PPC_ID_CODE
) {
1247 cpl_record_error(cpl
,
1248 "Invalid callback data passed to pca_path_completions()");
1252 * Get the filename cache.
1256 * Get the start of the file name. If not specified by the caller,
1257 * identify it by searching backwards in the input line for an
1258 * unescaped space or the start of the line.
1260 if(ppc
->file_start
< 0) {
1261 start_path
= _pu_start_of_path(line
, word_end
);
1263 cpl_record_error(cpl
, "Unable to find the start of the file name.");
1267 start_path
= line
+ ppc
->file_start
;
1270 * Get the index of the start of the word being completed.
1272 word_start
= start_path
- line
;
1274 * Work out the length of the prefix that is bein completed.
1276 prefix_len
= word_end
- word_start
;
1278 * If the word starts with a ~username expression or the root directory,
1279 * of it contains any directory separators, then completion must be
1280 * delegated to cpl_file_completions().
1282 if(cpa_cmd_contains_path(start_path
, prefix_len
)) {
1283 cfc_file_start(pc
->cfc
, word_start
);
1284 return cpl_file_completions(cpl
, pc
->cfc
, line
, word_end
);
1287 * Look up the specified file name in each of the directories of the path,
1288 * in the same order that they were listed in the path, and stop as soon
1289 * as an instance of the file is found.
1291 for(node
=pc
->head
; node
; node
=node
->next
) {
1293 * If the directory of the latest node is a relative pathname,
1294 * scan it for files of interest.
1296 if(node
->relative
) {
1297 rst_CacheMem(node
->mem
);
1298 if(pca_scan_dir(pc
, node
->dir
, node
->mem
) < 1)
1300 node
->files
= node
->mem
->files
;
1301 node
->nfile
= node
->mem
->nfiles
;
1304 * If needed, make a copy of the file-name being matched, with
1305 * escapes removed. Note that we need to do this anew every loop
1306 * iteration, because the above call to pca_scan_dir() uses
1309 prefix
= pca_prepare_prefix(pc
, start_path
, prefix_len
, ppc
->escaped
);
1313 * The directory entries are sorted, so we can perform a binary
1314 * search for an instance of the prefix being searched for.
1317 top
= node
->nfile
- 1;
1319 int mid
= (top
+ bot
)/2;
1320 int test
= strncmp(node
->files
[mid
]+1, prefix
, prefix_len
);
1331 * If we found a match, look to see if any of its neigbors also match.
1334 while(--bot
>= 0 && strncmp(node
->files
[bot
]+1, prefix
, prefix_len
) == 0)
1336 while(++top
< node
->nfile
&&
1337 strncmp(node
->files
[top
]+1, prefix
, prefix_len
) == 0)
1340 * We will have gone one too far in each direction.
1345 * Add the completions to the list after checking them against the
1346 * callers requirements.
1348 for( ; bot
<=top
; bot
++) {
1349 char *match
= node
->files
[bot
];
1351 * Form the full pathname of the file.
1353 _pn_clear_path(pc
->path
);
1354 if(_pn_append_to_path(pc
->path
, node
->dir
, -1, 0) == NULL
||
1355 _pn_append_to_path(pc
->path
, match
+1, -1, 0) == NULL
) {
1356 _err_record_msg(pc
->err
, "Insufficient memory to complete file name",
1361 * Should the file be included in the list of completions?
1363 if(!pc
->check_fn
|| match
[0] == PCA_F_WANTED
||
1364 (match
[0]==PCA_F_ENIGMA
&& pc
->check_fn(pc
->data
, pc
->path
->name
))) {
1365 match
[0] = PCA_F_WANTED
;
1367 * Copy the completion suffix into the work pathname pc->path->name,
1368 * adding backslash escapes if needed.
1370 if(pca_prepare_suffix(pc
, match
+ 1 + prefix_len
,
1374 * Record the completion.
1376 if(cpl_add_completion(cpl
, line
, word_start
, word_end
, pc
->path
->name
,
1380 * The file was rejected by the application.
1383 match
[0] = PCA_F_IGNORE
;
1389 * We now need to search for subdirectories of the current directory which
1390 * have matching prefixes. First, if needed, make a copy of the word being
1391 * matched, with escapes removed.
1393 prefix
= pca_prepare_prefix(pc
, start_path
, prefix_len
, ppc
->escaped
);
1397 * Now open the current directory.
1399 if(_dr_open_dir(pc
->dr
, FS_PWD
, NULL
))
1402 * Scan the current directory for sub-directories whos names start with
1403 * the prefix that we are completing.
1405 while((filename
= _dr_next_file(pc
->dr
))) {
1407 * Does the latest filename match the prefix, and is it a directory?
1409 if(strncmp(filename
, prefix
, prefix_len
) == 0 && _pu_path_is_dir(filename
)){
1411 * Record the completion.
1413 if(pca_prepare_suffix(pc
, filename
+ prefix_len
, ppc
->escaped
) ||
1414 cpl_add_completion(cpl
, line
, word_start
, word_end
, pc
->path
->name
,
1415 FS_DIR_SEP
, FS_DIR_SEP
))
1418 * The prefix in pc->path->name will have been overwritten by
1419 * pca_prepare_suffix(). Restore it here.
1421 prefix
= pca_prepare_prefix(pc
, start_path
, prefix_len
, ppc
->escaped
);
1426 _dr_close_dir(pc
->dr
);
1430 /*.......................................................................
1431 * Using the work buffer pc->path, make a suitably escaped copy of a
1432 * given completion suffix, ready to be passed to cpl_add_completion().
1435 * pc PathCache * The filename cache resource object.
1436 * suffix char * The suffix to be copied.
1437 * add_escapes int If true, escape special characters.
1439 * return int 0 - OK.
1442 static int pca_prepare_suffix(PathCache
*pc
, const char *suffix
,
1445 const char *sptr
; /* A pointer into suffix[] */
1446 int nbsl
; /* The number of backslashes to add to the suffix */
1449 * How long is the suffix?
1451 int suffix_len
= strlen(suffix
);
1453 * Clear the work buffer.
1455 _pn_clear_path(pc
->path
);
1457 * Count the number of backslashes that will have to be added to
1458 * escape spaces, tabs, backslashes and wildcard characters.
1462 for(sptr
= suffix
; *sptr
; sptr
++) {
1464 case ' ': case '\t': case '\\': case '*': case '?': case '[':
1471 * Arrange for the output path buffer to have sufficient room for the
1472 * both the suffix and any backslashes that have to be inserted.
1474 if(_pn_resize_path(pc
->path
, suffix_len
+ nbsl
) == NULL
) {
1475 _err_record_msg(pc
->err
, "Insufficient memory to complete file name",
1480 * If the suffix doesn't need any escapes, copy it directly into the
1484 strlcpy(pc
->path
->name
, suffix
, pc
->path
->dim
);
1487 * Make a copy with special characters escaped?
1490 const char *src
= suffix
;
1491 char *dst
= pc
->path
->name
;
1492 for(i
=0; i
<suffix_len
; i
++) {
1494 case ' ': case '\t': case '\\': case '*': case '?': case '[':
1505 /*.......................................................................
1506 * Return non-zero if the specified string appears to start with a pathname.
1509 * prefix const char * The filename prefix to check.
1510 * prefix_len int The length of the prefix.
1512 * return int 0 - Doesn't start with a path name.
1513 * 1 - Does start with a path name.
1515 static int cpa_cmd_contains_path(const char *prefix
, int prefix_len
)
1519 * If the filename starts with a ~, then this implies a ~username
1520 * expression, which constitutes a pathname.
1525 * If the filename starts with the root directory, then it obviously
1526 * starts with a pathname.
1528 if(prefix_len
>= FS_ROOT_DIR_LEN
&&
1529 strncmp(prefix
, FS_ROOT_DIR
, FS_ROOT_DIR_LEN
) == 0)
1532 * Search the prefix for directory separators, returning as soon as
1533 * any are found, since their presence indicates that the filename
1534 * starts with a pathname specification (valid or otherwise).
1536 for(i
=0; i
<prefix_len
; i
++) {
1537 if(prefix_len
- i
>= FS_DIR_SEP_LEN
&&
1538 strncmp(prefix
+ i
, FS_DIR_SEP
, FS_DIR_SEP_LEN
) == 0)
1542 * The file name doesn't appear to start with a pathname specification.
1547 /*.......................................................................
1548 * If needed make a new copy of the prefix being matched, in pc->path->name,
1549 * but with escapes removed. If no escapes are to be removed, simply return
1550 * the original prefix string.
1553 * pc PathCache * The cache being searched.
1554 * prefix const char * The prefix to be processed.
1555 * prefix_len size_t The length of the prefix.
1556 * escaped int If true, return a copy with escapes removed.
1558 * return const char * The prepared prefix, or NULL on error, in
1559 * which case an error message will have been
1562 static const char *pca_prepare_prefix(PathCache
*pc
, const char *prefix
,
1563 size_t prefix_len
, int escaped
)
1566 * Make a copy with escapes removed?
1569 _pn_clear_path(pc
->path
);
1570 if(_pn_append_to_path(pc
->path
, prefix
, prefix_len
, 1) == NULL
) {
1571 _err_record_msg(pc
->err
, "Insufficient memory to complete filename",
1575 return pc
->path
->name
;
1580 /*.......................................................................
1581 * If backslashes in the filename should be treated as literal
1582 * characters, call the following function with literal=1. Otherwise
1583 * the default is to treat them as escape characters, used for escaping
1587 * ppc PcaPathConf * The pca_path_completions() configuration object
1589 * literal int Pass non-zero here to enable literal interpretation
1590 * of backslashes. Pass 0 to turn off literal
1593 void ppc_literal_escapes(PcaPathConf
*ppc
, int literal
)
1596 ppc
->escaped
= !literal
;
1599 /*.......................................................................
1600 * Call this function if you know where the index at which the
1601 * filename prefix starts in the input line. Otherwise by default,
1602 * or if you specify start_index to be -1, the filename is taken
1603 * to start after the first unescaped space preceding the cursor,
1604 * or the start of the line, which ever comes first.
1607 * ppc PcaPathConf * The pca_path_completions() configuration object
1609 * start_index int The index of the start of the filename in
1610 * the input line, or -1 to select the default.
1612 void ppc_file_start(PcaPathConf
*ppc
, int start_index
)
1615 ppc
->file_start
= start_index
;
1618 /*.......................................................................
1619 * Expand any ~user expression found at the start of a path, leaving
1620 * either an empty string in pc->path if there is no ~user expression,
1621 * or the corresponding home directory.
1624 * pc PathCache * The filename cache.
1625 * path const char * The path to expand.
1626 * pathlen int The max number of characters to look at in path[].
1627 * literal int If true, treat backslashes as literal characters
1628 * instead of escapes.
1630 * endp const char * A pointer to the next unprocessed character in
1631 * path[] will be assigned to *endp.
1634 * 1 - Error (a description will have been placed
1637 static int pca_expand_tilde(PathCache
*pc
, const char *path
, int pathlen
,
1638 int literal
, const char **endp
)
1640 const char *pptr
= path
; /* A pointer into path[] */
1641 const char *homedir
=NULL
; /* A home directory */
1643 * Clear the pathname buffer.
1645 _pn_clear_path(pc
->path
);
1647 * If the first character is a tilde, then perform home-directory
1652 * Skip the tilde character and attempt to read the username that follows
1653 * it, into pc->usrnam[].
1655 if(pca_read_username(pc
, ++pptr
, pathlen
-1, literal
, &pptr
))
1658 * Attempt to lookup the home directory of the user.
1660 homedir
= _hd_lookup_home_dir(pc
->home
, pc
->usrnam
);
1662 _err_record_msg(pc
->err
, _hd_last_home_dir_error(pc
->home
), END_ERR_MSG
);
1666 * Append the home directory to the pathname string.
1668 if(_pn_append_to_path(pc
->path
, homedir
, -1, 0) == NULL
) {
1669 _err_record_msg(pc
->err
,
1670 "Insufficient memory for home directory expansion",
1676 * ~user and ~ are usually followed by a directory separator to
1677 * separate them from the file contained in the home directory.
1678 * If the home directory is the root directory, then we don't want
1679 * to follow the home directory by a directory separator, so we should
1680 * skip over it so that it doesn't get copied into the output pathname
1682 if(homedir
&& strcmp(homedir
, FS_ROOT_DIR
) == 0 &&
1683 (pptr
-path
) + FS_DIR_SEP_LEN
< pathlen
&&
1684 strncmp(pptr
, FS_DIR_SEP
, FS_DIR_SEP_LEN
) == 0) {
1685 pptr
+= FS_DIR_SEP_LEN
;
1688 * Return a pointer to the next unprocessed character.
1694 /*.......................................................................
1695 * Clear the filename status codes that are recorded before each filename
1699 * pc PathCache * The filename cache.
1701 static void pca_remove_marks(PathCache
*pc
)
1703 PathNode
*node
; /* A node in the list of directories in the path */
1706 * Traverse the absolute directories of the path, clearing the
1707 * filename status marks that precede each filename.
1709 for(node
=pc
->head
; node
; node
=node
->next
) {
1710 if(!node
->relative
) {
1711 for(i
=0; i
<node
->nfile
; i
++)
1712 *node
->files
[i
] = PCA_F_ENIGMA
;
1718 #endif /* ifndef WITHOUT_FILE_SYSTEM */