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