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