/*....................................................................... * Append a string to a pathname, increasing the size of the pathname * buffer if needed. * * Input: * path PathName * The pathname container. * string const char * The string to be appended to the pathname. * Note that regardless of the slen argument, * this should be a '\0' terminated string. * slen int The maximum number of characters to append * from string[], or -1 to append the whole * string. * remove_escapes int If true, remove the backslashes that escape * spaces, tabs, backslashes etc.. * Output: * return char * The pathname string path->name[], which may * have been reallocated, or NULL if there was * insufficient memory to extend the pathname. */ char *_pn_append_to_path(PathName *path, const char *string, int slen, int remove_escapes) { int pathlen; /* The length of the pathname */ int i; /* * Check the arguments. */ if(!path || !string) { errno = EINVAL; return NULL; }; /* * Get the current length of the pathname. */ pathlen = strlen(path->name); /* * How many characters should be appended? */ if(slen < 0 || slen > strlen(string)) slen = strlen(string); /* * Resize the pathname if needed. */ if(!_pn_resize_path(path, pathlen + slen)) return NULL; /* * Append the string to the output pathname, removing any escape * characters found therein. */ if(remove_escapes) { int is_escape = 0; for(i=0; i<slen; i++) { is_escape = !is_escape && string[i] == '\\'; if(!is_escape) path->name[pathlen++] = string[i]; }; /* * Terminate the string. */ path->name[pathlen] = '\0'; } else { /* * Append the string directly to the pathname. */ memcpy(path->name + pathlen, string, slen); path->name[pathlen + slen] = '\0'; }; return path->name; }
/*....................................................................... * Preprocess a path, expanding ~/, ~user/ and $envvar references, using * ef->path as a work buffer, then copy the result into a cache entry, * and return a pointer to this copy. * * Input: * ef ExpandFile * The resource object of the file matcher. * pathlen int The length of the prefix of path[] to be expanded. * Output: * return char * A pointer to a copy of the output path in the * cache. On error NULL is returned, and a description * of the error is left in ef->errmsg[]. */ static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen) { int spos; /* The index of the start of the path segment that needs */ /* to be copied from path[] to the output pathname. */ int ppos; /* The index of a character in path[] */ char *pptr; /* A pointer into the output path */ int escaped; /* True if the previous character was a '\' */ int i; /* * Clear the pathname buffer. */ _pn_clear_path(ef->path); /* * We need to perform two passes, one to expand environment variables * and a second to do tilde expansion. This caters for the case * where an initial dollar expansion yields a tilde expression. */ escaped = 0; for(spos=ppos=0; ppos < pathlen; ppos++) { int c = path[ppos]; if(escaped) { escaped = 0; } else if(c == '\\') { escaped = 1; } else if(c == '$') { int envlen; /* The length of the environment variable */ char *value; /* The value of the environment variable */ /* * Record the preceding unrecorded part of the pathname. */ if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) == NULL) { strncpy(ef->errmsg, "Insufficient memory to expand path", sizeof(ef->errmsg)); return NULL; }; /* * Skip the dollar. */ ppos++; /* * Copy the environment variable name that follows the dollar into * ef->envnam[], stopping if a directory separator or end of string * is seen. */ for(envlen=0; envlen<ENV_LEN && ppos < pathlen && strncmp(path + ppos, FS_DIR_SEP, FS_DIR_SEP_LEN); envlen++) ef->envnam[envlen] = path[ppos++]; /* * If the username overflowed the buffer, treat it as invalid (note that * on most unix systems only 8 characters are allowed in a username, * whereas our ENV_LEN is much bigger than that. */ if(envlen >= ENV_LEN) { strncpy(ef->errmsg, "Environment variable name too long", sizeof(ef->errmsg)); return NULL; }; /* * Terminate the environment variable name. */ ef->envnam[envlen] = '\0'; /* * Lookup the value of the environment variable. */ value = getenv(ef->envnam); if(!value) { const char *fmt = "No expansion found for: $%.*s"; snprintf(ef->errmsg, sizeof(ef->errmsg), fmt, ERRLEN - strlen(fmt), ef->envnam); return NULL; }; /* * Copy the value of the environment variable into the output pathname. */ if(_pn_append_to_path(ef->path, value, -1, 0) == NULL) { strncpy(ef->errmsg, "Insufficient memory to expand path", sizeof(ef->errmsg)); return NULL; }; /* * Record the start of the uncopied tail of the input pathname. */ spos = ppos; }; }; /* * Record the uncopied tail of the pathname. */ if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) == NULL) { strncpy(ef->errmsg, "Insufficient memory to expand path", sizeof(ef->errmsg)); return NULL; }; /* * If the first character of the resulting pathname is a tilde, * then attempt to substitute the home directory of the specified user. */ pptr = ef->path->name; if(*pptr == '~' && path[0] != '\\') { int usrlen; /* The length of the username following the tilde */ const char *homedir; /* The home directory of the user */ int homelen; /* The length of the home directory string */ int plen; /* The current length of the path */ int skip=0; /* The number of characters to skip after the ~user */ /* * Get the current length of the output path. */ plen = strlen(ef->path->name); /* * Skip the tilde. */ pptr++; /* * Copy the optional username that follows the tilde into ef->usrnam[]. */ for(usrlen=0; usrlen<USR_LEN && *pptr && strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN); usrlen++) ef->usrnam[usrlen] = *pptr++; /* * If the username overflowed the buffer, treat it as invalid (note that * on most unix systems only 8 characters are allowed in a username, * whereas our USR_LEN is much bigger than that. */ if(usrlen >= USR_LEN) { strncpy(ef->errmsg, "Username too long", sizeof(ef->errmsg)); return NULL; }; /* * Terminate the username string. */ ef->usrnam[usrlen] = '\0'; /* * Lookup the home directory of the user. */ homedir = _hd_lookup_home_dir(ef->home, ef->usrnam); if(!homedir) { strncpy(ef->errmsg, _hd_last_home_dir_error(ef->home), ERRLEN); ef->errmsg[ERRLEN] = '\0'; return NULL; }; homelen = strlen(homedir); /* * ~user and ~ are usually followed by a directory separator to * separate them from the file contained in the home directory. * If the home directory is the root directory, then we don't want * to follow the home directory by a directory separator, so we must * erase it. */ if(strcmp(homedir, FS_ROOT_DIR) == 0 && strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { skip = FS_DIR_SEP_LEN; }; /* * If needed, increase the size of the pathname buffer to allow it * to accomodate the home directory instead of the tilde expression. * Note that pptr may not be valid after this call. */ if(_pn_resize_path(ef->path, plen - usrlen - 1 - skip + homelen)==NULL) { strncpy(ef->errmsg, "Insufficient memory to expand filename", sizeof(ef->errmsg)); return NULL; }; /* * Move the part of the pathname that follows the tilde expression to * the end of where the home directory will need to be inserted. */ memmove(ef->path->name + homelen, ef->path->name + 1 + usrlen + skip, plen - usrlen - 1 - skip+1); /* * Write the home directory at the beginning of the string. */ for(i=0; i<homelen; i++) ef->path->name[i] = homedir[i]; }; /* * Copy the result into the cache, and return a pointer to the copy. */ return ef_cache_pathname(ef, ef->path->name, 0); }
/*....................................................................... * Prepend a string to a pathname, increasing the size of the pathname * buffer if needed. * * Input: * path PathName * The pathname container. * string const char * The string to be prepended to the pathname. * Note that regardless of the slen argument, * this should be a '\0' terminated string. * slen int The maximum number of characters to prepend * from string[], or -1 to append the whole * string. * remove_escapes int If true, remove the backslashes that escape * spaces, tabs, backslashes etc.. * Output: * return char * The pathname string path->name[], which may * have been reallocated, or NULL if there was * insufficient memory to extend the pathname. */ char *_pn_prepend_to_path(PathName *path, const char *string, int slen, int remove_escapes) { int pathlen; /* The length of the pathname */ int shift; /* The number of characters to shift the suffix by */ int i,j; /* * Check the arguments. */ if(!path || !string) { errno = EINVAL; return NULL; }; /* * Get the current length of the pathname. */ pathlen = strlen(path->name); /* * How many characters should be appended? */ if(slen < 0 || slen > strlen(string)) slen = strlen(string); /* * Work out how far we need to shift the original path string to make * way for the new prefix. When removing escape characters, we need * final length of the new prefix, after unescaped backslashes have * been removed. */ if(remove_escapes) { int is_escape = 0; for(shift=0,i=0; i<slen; i++) { is_escape = !is_escape && string[i] == '\\'; if(!is_escape) shift++; }; } else { shift = slen; }; /* * Resize the pathname if needed. */ if(!_pn_resize_path(path, pathlen + shift)) return NULL; /* * Make room for the prefix at the beginning of the string. */ memmove(path->name + shift, path->name, pathlen+1); /* * Copy the new prefix into the vacated space at the beginning of the * output pathname, removing any escape characters if needed. */ if(remove_escapes) { int is_escape = 0; for(i=j=0; i<slen; i++) { is_escape = !is_escape && string[i] == '\\'; if(!is_escape) path->name[j++] = string[i]; }; } else { memcpy(path->name, string, slen); }; return path->name; }