summaryrefslogtreecommitdiff
path: root/libtecla-1.4.1/expand.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtecla-1.4.1/expand.c')
-rw-r--r--libtecla-1.4.1/expand.c1265
1 files changed, 0 insertions, 1265 deletions
diff --git a/libtecla-1.4.1/expand.c b/libtecla-1.4.1/expand.c
deleted file mode 100644
index c1600ab..0000000
--- a/libtecla-1.4.1/expand.c
+++ /dev/null
@@ -1,1265 +0,0 @@
-/*
- * Copyright (c) 2000, 2001 by Martin C. Shepherd.
- *
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, and/or sell copies of the Software, and to permit persons
- * to whom the Software is furnished to do so, provided that the above
- * copyright notice(s) and this permission notice appear in all copies of
- * the Software and that both the above copyright notice(s) and this
- * permission notice appear in supporting documentation.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
- * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
- * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
- * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
- * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Except as contained in this notice, the name of a copyright holder
- * shall not be used in advertising or otherwise to promote the sale, use
- * or other dealings in this Software without prior written authorization
- * of the copyright holder.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include "freelist.h"
-#include "direader.h"
-#include "pathutil.h"
-#include "homedir.h"
-#include "stringrp.h"
-#include "libtecla.h"
-
-/*
- * Specify the number of elements to extend the files[] array by
- * when it proves to be too small. This also sets the initial size
- * of the array.
- */
-#define MATCH_BLK_FACT 256
-
-/*
- * A list of directory iterators is maintained using nodes of the
- * following form.
- */
-typedef struct DirNode DirNode;
-struct DirNode {
- DirNode *next; /* The next directory in the list */
- DirNode *prev; /* The node that precedes this node in the list */
- DirReader *dr; /* The directory reader object */
-};
-
-typedef struct {
- FreeList *mem; /* Memory for DirNode list nodes */
- DirNode *head; /* The head of the list of used and unused cache nodes */
- DirNode *next; /* The next unused node between head and tail */
- DirNode *tail; /* The tail of the list of unused cache nodes */
-} DirCache;
-
-/*
- * Specify how many directory cache nodes to allocate at a time.
- */
-#define DIR_CACHE_BLK 20
-
-/*
- * Set the maximum length allowed for usernames.
- */
-#define USR_LEN 100
-
-/*
- * Set the maximum length allowed for environment variable names.
- */
-#define ENV_LEN 100
-
-/*
- * Set the max length of the error-reporting string. There is no point
- * in this being longer than the width of a typical terminal window.
- * In composing error messages, I have assumed that this number is
- * at least 80, so you don't decrease it below this number.
- */
-#define ERRLEN 200
-
-struct ExpandFile {
- StringGroup *sg; /* A list of string segments in which */
- /* matching filenames are stored. */
- DirCache cache; /* The cache of directory reader objects */
- PathName *path; /* The pathname being matched */
- HomeDir *home; /* Home-directory lookup object */
- int files_dim; /* The allocated dimension of result.files[] */
- char usrnam[USR_LEN+1]; /* A user name */
- char envnam[ENV_LEN+1]; /* An environment variable name */
- char errmsg[ERRLEN+1]; /* Error-report buffer */
- FileExpansion result; /* The container used to return the results of */
- /* expanding a path. */
-};
-
-static int ef_record_pathname(ExpandFile *ef, const char *pathname,
- int remove_escapes);
-static char *ef_cache_pathname(ExpandFile *ef, const char *pathname,
- int remove_escapes);
-static void ef_clear_files(ExpandFile *ef);
-
-static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname);
-static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node);
-static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen);
-static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr,
- const char *pattern, int separate);
-static int ef_matches_range(int c, const char *pattern, const char **endp);
-static int ef_string_matches_pattern(const char *file, const char *pattern,
- int xplicit, const char *nextp);
-static int ef_cmp_strings(const void *v1, const void *v2);
-
-/*.......................................................................
- * Create the resources needed to expand filenames.
- *
- * Output:
- * return ExpandFile * The new object, or NULL on error.
- */
-ExpandFile *new_ExpandFile(void)
-{
- ExpandFile *ef; /* The object to be returned */
-/*
- * Allocate the container.
- */
- ef = (ExpandFile *) malloc(sizeof(ExpandFile));
- if(!ef) {
- fprintf(stderr, "new_ExpandFile: Insufficient memory.\n");
- return NULL;
- };
-/*
- * Before attempting any operation that might fail, initialize the
- * container at least up to the point at which it can safely be passed
- * to del_ExpandFile().
- */
- ef->sg = NULL;
- ef->cache.mem = NULL;
- ef->cache.head = NULL;
- ef->cache.next = NULL;
- ef->cache.tail = NULL;
- ef->path = NULL;
- ef->home = NULL;
- ef->result.files = NULL;
- ef->result.nfile = 0;
- ef->usrnam[0] = '\0';
- ef->envnam[0] = '\0';
- ef->errmsg[0] = '\0';
-/*
- * Allocate a list of string segments for storing filenames.
- */
- ef->sg = _new_StringGroup(_pu_pathname_dim());
- if(!ef->sg)
- return del_ExpandFile(ef);
-/*
- * Allocate a freelist for allocating directory cache nodes.
- */
- ef->cache.mem = _new_FreeList("new_ExpandFile", sizeof(DirNode), DIR_CACHE_BLK);
- if(!ef->cache.mem)
- return del_ExpandFile(ef);
-/*
- * Allocate a pathname buffer.
- */
- ef->path = _new_PathName();
- if(!ef->path)
- return del_ExpandFile(ef);
-/*
- * Allocate an object for looking up home-directories.
- */
- ef->home = _new_HomeDir();
- if(!ef->home)
- return del_ExpandFile(ef);
-/*
- * Allocate an array for files. This will be extended later if needed.
- */
- ef->files_dim = MATCH_BLK_FACT;
- ef->result.files = (char **) malloc(sizeof(ef->result.files[0]) *
- ef->files_dim);
- if(!ef->result.files) {
- fprintf(stderr,
- "new_ExpandFile: Insufficient memory to allocate array of files.\n");
- return del_ExpandFile(ef);
- };
- return ef;
-}
-
-/*.......................................................................
- * Delete a ExpandFile object.
- *
- * Input:
- * ef ExpandFile * The object to be deleted.
- * Output:
- * return ExpandFile * The deleted object (always NULL).
- */
-ExpandFile *del_ExpandFile(ExpandFile *ef)
-{
- if(ef) {
- DirNode *dnode;
-/*
- * Delete the string segments.
- */
- ef->sg = _del_StringGroup(ef->sg);
-/*
- * Delete the cached directory readers.
- */
- for(dnode=ef->cache.head; dnode; dnode=dnode->next)
- dnode->dr = _del_DirReader(dnode->dr);
-/*
- * Delete the memory from which the DirNode list was allocated, thus
- * deleting the list at the same time.
- */
- ef->cache.mem = _del_FreeList("del_ExpandFile", ef->cache.mem, 1);
- ef->cache.head = ef->cache.tail = ef->cache.next = NULL;
-/*
- * Delete the pathname buffer.
- */
- ef->path = _del_PathName(ef->path);
-/*
- * Delete the home-directory lookup object.
- */
- ef->home = _del_HomeDir(ef->home);
-/*
- * Delete the array of pointers to files.
- */
- if(ef->result.files) {
- free(ef->result.files);
- ef->result.files = NULL;
- };
-/*
- * Delete the container.
- */
- free(ef);
- };
- return NULL;
-}
-
-/*.......................................................................
- * Expand a pathname, converting ~user/ and ~/ patterns at the start
- * of the pathname to the corresponding home directories, replacing
- * $envvar with the value of the corresponding environment variable,
- * and then, if there are any wildcards, matching these against existing
- * filenames.
- *
- * If no errors occur, a container is returned containing the array of
- * files that resulted from the expansion. If there were no wildcards
- * in the input pathname, this will contain just the original pathname
- * after expansion of ~ and $ expressions. If there were any wildcards,
- * then the array will contain the files that matched them. Note that
- * if there were any wildcards but no existing files match them, this
- * is counted as an error and NULL is returned.
- *
- * The supported wildcards and their meanings are:
- * * - Match any sequence of zero or more characters.
- * ? - Match any single character.
- * [chars] - Match any single character that appears in 'chars'.
- * If 'chars' contains an expression of the form a-b,
- * then any character between a and b, including a and b,
- * matches. The '-' character looses its special meaning
- * as a range specifier when it appears at the start
- * of the sequence of characters.
- * [^chars] - The same as [chars] except that it matches any single
- * character that doesn't appear in 'chars'.
- *
- * Wildcard expressions are applied to individual filename components.
- * They don't match across directory separators. A '.' character at
- * the beginning of a filename component must also be matched
- * explicitly by a '.' character in the input pathname, since these
- * are UNIX's hidden files.
- *
- * Input:
- * ef ExpandFile * The pathname expansion resource object.
- * path char * The path name to be expanded.
- * pathlen int The length of the suffix of path[] that
- * constitutes the filename to be expanded,
- * or -1 to specify that the whole of the
- * path string should be used. Note that
- * regardless of the value of this argument,
- * path[] must contain a '\0' terminated
- * string, since this function checks that
- * pathlen isn't mistakenly too long.
- * Output:
- * return FileExpansion * A pointer to a container within the given
- * ExpandFile object. This contains an array
- * of the pathnames that resulted from expanding
- * ~ and $ expressions and from matching any
- * wildcards, sorted into lexical order.
- * This container and its contents will be
- * recycled on subsequent calls, so if you need
- * to keep the results of two successive runs,
- * you will either have to allocate a private
- * copy of the array, or use two ExpandFile
- * objects.
- *
- * On error NULL is returned. A description
- * of the error can be acquired by calling the
- * ef_last_error() function.
- */
-FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen)
-{
- DirNode *dnode; /* A directory-reader cache node */
- const char *dirname; /* The name of the top level directory of the search */
- const char *pptr; /* A pointer into path[] */
- int wild; /* True if the path contains any wildcards */
-/*
- * Check the arguments.
- */
- if(!ef || !path) {
- if(ef)
- strcpy(ef->errmsg, "ef_expand_file: NULL path argument");
- else
- fprintf(stderr, "ef_expand_file: NULL argument(s).\n");
- return NULL;
- };
-/*
- * If the caller specified that the whole of path[] be matched,
- * work out the corresponding length.
- */
- if(pathlen < 0 || pathlen > strlen(path))
- pathlen = strlen(path);
-/*
- * Discard previous expansion results.
- */
- ef_clear_files(ef);
-/*
- * Preprocess the path, expanding ~/, ~user/ and $envvar references,
- * using ef->path as a work directory and returning a pointer to
- * a copy of the resulting pattern in the cache.
- */
- path = ef_expand_special(ef, path, pathlen);
- if(!path)
- return NULL;
-/*
- * Clear the pathname buffer.
- */
- _pn_clear_path(ef->path);
-/*
- * Does the pathname contain any wildcards?
- */
- for(wild=0,pptr=path; !wild && *pptr; pptr++) {
- switch(*pptr) {
- case '\\': /* Skip escaped characters */
- if(pptr[1])
- pptr++;
- break;
- case '*': case '?': case '[': /* A wildcard character? */
- wild = 1;
- break;
- };
- };
-/*
- * If there are no wildcards to match, copy the current expanded
- * path into the output array, removing backslash escapes while doing so.
- */
- if(!wild) {
- if(ef_record_pathname(ef, path, 1))
- return NULL;
-/*
- * Does the filename exist?
- */
- ef->result.exists = _pu_file_exists(ef->result.files[0]);
-/*
- * Match wildcards against existing files.
- */
- } else {
-/*
- * Only existing files that match the pattern will be returned in the
- * cache.
- */
- ef->result.exists = 1;
-/*
- * Treat matching of the root-directory as a special case since it
- * isn't contained in a directory.
- */
- if(strcmp(path, FS_ROOT_DIR) == 0) {
- if(ef_record_pathname(ef, FS_ROOT_DIR, 0))
- return NULL;
- } else {
-/*
- * What should the top level directory of the search be?
- */
- if(strncmp(path, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) {
- dirname = FS_ROOT_DIR;
- if(!_pn_append_to_path(ef->path, FS_ROOT_DIR, -1, 0)) {
- strcpy(ef->errmsg, "Insufficient memory to record path");
- return NULL;
- };
- path += FS_ROOT_DIR_LEN;
- } else {
- dirname = FS_PWD;
- };
-/*
- * Open the top-level directory of the search.
- */
- dnode = ef_open_dir(ef, dirname);
- if(!dnode)
- return NULL;
-/*
- * Recursively match successive directory components of the path.
- */
- if(ef_match_relative_pathname(ef, dnode->dr, path, 0)) {
- dnode = ef_close_dir(ef, dnode);
- return NULL;
- };
-/*
- * Cleanup.
- */
- dnode = ef_close_dir(ef, dnode);
- };
-/*
- * No files matched?
- */
- if(ef->result.nfile < 1) {
- strcpy(ef->errmsg, "No files match");
- return NULL;
- };
-/*
- * Sort the pathnames that matched.
- */
- qsort(ef->result.files, ef->result.nfile, sizeof(ef->result.files[0]),
- ef_cmp_strings);
- };
-/*
- * Return the result container.
- */
- return &ef->result;
-}
-
-/*.......................................................................
- * Attempt to recursively match the given pattern with the contents of
- * the current directory, descending sub-directories as needed.
- *
- * Input:
- * ef ExpandFile * The pathname expansion resource object.
- * dr DirReader * The directory reader object of the directory
- * to be searched.
- * pattern const char * The pattern to match with files in the current
- * directory.
- * separate int When appending a filename from the specified
- * directory to ef->pathname, insert a directory
- * separator between the existing pathname and
- * the filename, unless separate is zero.
- * Output:
- * return int 0 - OK.
- * 1 - Error.
- */
-static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr,
- const char *pattern, int separate)
-{
- const char *nextp; /* The pointer to the character that follows the part */
- /* of the pattern that is to be matched with files */
- /* in the current directory. */
- char *file; /* The name of the file being matched */
- int pathlen; /* The length of ef->pathname[] on entry to this */
- /* function */
-/*
- * Record the current length of the pathname string recorded in
- * ef->pathname[].
- */
- pathlen = strlen(ef->path->name);
-/*
- * Get a pointer to the character that follows the end of the part of
- * the pattern that should be matched to files within the current directory.
- * This will either point to a directory separator, or to the '\0' terminator
- * of the pattern string.
- */
- for(nextp=pattern; *nextp && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0;
- nextp++)
- ;
-/*
- * Read each file from the directory, attempting to match it to the
- * current pattern.
- */
- while((file=_dr_next_file(dr)) != NULL) {
-/*
- * Does the latest file match the pattern up to nextp?
- */
- if(ef_string_matches_pattern(file, pattern, file[0]=='.', nextp)) {
-/*
- * Append the new directory entry to the current matching pathname.
- */
- if((separate && _pn_append_to_path(ef->path, FS_DIR_SEP, -1, 0)==NULL) ||
- _pn_append_to_path(ef->path, file, -1, 0)==NULL) {
- strcpy(ef->errmsg, "Insufficient memory to record path");
- return 1;
- };
-/*
- * If we have reached the end of the pattern, record the accumulated
- * pathname in the list of matching files.
- */
- if(*nextp == '\0') {
- if(ef_record_pathname(ef, ef->path->name, 0))
- return 1;
-/*
- * If the matching directory entry is a subdirectory, and the
- * next character of the pattern is a directory separator,
- * recursively call the current function to scan the sub-directory
- * for matches.
- */
- } else if(_pu_path_is_dir(ef->path->name) &&
- strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) {
-/*
- * If the pattern finishes with the directory separator, then
- * record the pathame as matching.
- */
- if(nextp[FS_DIR_SEP_LEN] == '\0') {
- if(ef_record_pathname(ef, ef->path->name, 0))
- return 1;
-/*
- * Match files within the directory.
- */
- } else {
- DirNode *subdnode = ef_open_dir(ef, ef->path->name);
- if(subdnode) {
- if(ef_match_relative_pathname(ef, subdnode->dr,
- nextp+FS_DIR_SEP_LEN, 1)) {
- subdnode = ef_close_dir(ef, subdnode);
- return 1;
- };
- subdnode = ef_close_dir(ef, subdnode);
- };
- };
- };
-/*
- * Remove the latest filename from the pathname string, so that
- * another matching file can be appended.
- */
- ef->path->name[pathlen] = '\0';
- };
- };
- return 0;
-}
-
-/*.......................................................................
- * Record a new matching filename.
- *
- * Input:
- * ef ExpandFile * The filename-match resource object.
- * pathname const char * The pathname to record.
- * remove_escapes int If true, remove backslash escapes in the
- * recorded copy of the pathname.
- * Output:
- * return int 0 - OK.
- * 1 - Error (ef->errmsg will contain a
- * description of the error).
- */
-static int ef_record_pathname(ExpandFile *ef, const char *pathname,
- int remove_escapes)
-{
- char *copy; /* The recorded copy of pathname[] */
-/*
- * Attempt to make a copy of the pathname in the cache.
- */
- copy = ef_cache_pathname(ef, pathname, remove_escapes);
- if(!copy)
- return 1;
-/*
- * If there isn't room to record a pointer to the recorded pathname in the
- * array of files, attempt to extend the array.
- */
- if(ef->result.nfile + 1 > ef->files_dim) {
- int files_dim = ef->files_dim + MATCH_BLK_FACT;
- char **files = (char **) realloc(ef->result.files,
- files_dim * sizeof(files[0]));
- if(!files) {
- sprintf(ef->errmsg,
- "Insufficient memory to record all of the matching filenames");
- return 1;
- };
- ef->result.files = files;
- ef->files_dim = files_dim;
- };
-/*
- * Record a pointer to the new match.
- */
- ef->result.files[ef->result.nfile++] = copy;
- return 0;
-}
-
-/*.......................................................................
- * Record a pathname in the cache.
- *
- * Input:
- * ef ExpandFile * The filename-match resource object.
- * pathname char * The pathname to record.
- * remove_escapes int If true, remove backslash escapes in the
- * copy of the pathname.
- * Output:
- * return char * The pointer to the copy of the pathname.
- * On error NULL is returned and a description
- * of the error is left in ef->errmsg[].
- */
-static char *ef_cache_pathname(ExpandFile *ef, const char *pathname,
- int remove_escapes)
-{
- char *copy = _sg_store_string(ef->sg, pathname, remove_escapes);
- if(!copy)
- strcpy(ef->errmsg, "Insufficient memory to store pathname");
- return copy;
-}
-
-/*.......................................................................
- * Clear the results of the previous expansion operation, ready for the
- * next.
- *
- * Input:
- * ef ExpandFile * The pathname expansion resource object.
- */
-static void ef_clear_files(ExpandFile *ef)
-{
- _clr_StringGroup(ef->sg);
- _pn_clear_path(ef->path);
- ef->result.exists = 0;
- ef->result.nfile = 0;
- ef->errmsg[0] = '\0';
- return;
-}
-
-/*.......................................................................
- * Get a new directory reader object from the cache.
- *
- * Input:
- * ef ExpandFile * The pathname expansion resource object.
- * pathname const char * The pathname of the directory.
- * Output:
- * return DirNode * The cache entry of the new directory reader,
- * or NULL on error. On error, ef->errmsg will
- * contain a description of the error.
- */
-static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname)
-{
- char *errmsg = NULL; /* An error message from a called function */
- DirNode *node; /* The cache node used */
-/*
- * Get the directory reader cache.
- */
- DirCache *cache = &ef->cache;
-/*
- * Extend the cache if there are no free cache nodes.
- */
- if(!cache->next) {
- node = (DirNode *) _new_FreeListNode(cache->mem);
- if(!node) {
- sprintf(ef->errmsg, "Insufficient memory to open a new directory");
- return NULL;
- };
-/*
- * Initialize the cache node.
- */
- node->next = NULL;
- node->prev = NULL;
- node->dr = NULL;
-/*
- * Allocate a directory reader object.
- */
- node->dr = _new_DirReader();
- if(!node->dr) {
- sprintf(ef->errmsg, "Insufficient memory to open a new directory");
- node = (DirNode *) _del_FreeListNode(cache->mem, node);
- return NULL;
- };
-/*
- * Append the node to the cache list.
- */
- node->prev = cache->tail;
- if(cache->tail)
- cache->tail->next = node;
- else
- cache->head = node;
- cache->next = cache->tail = node;
- };
-/*
- * Get the first unused node, but don't remove it from the list yet.
- */
- node = cache->next;
-/*
- * Attempt to open the specified directory.
- */
- if(_dr_open_dir(node->dr, pathname, &errmsg)) {
- strncpy(ef->errmsg, errmsg, ERRLEN);
- ef->errmsg[ERRLEN] = '\0';
- return NULL;
- };
-/*
- * Now that we have successfully opened the specified directory,
- * remove the cache node from the list, and relink the list around it.
- */
- cache->next = node->next;
- if(node->prev)
- node->prev->next = node->next;
- else
- cache->head = node->next;
- if(node->next)
- node->next->prev = node->prev;
- else
- cache->tail = node->prev;
- node->next = node->prev = NULL;
-/*
- * Return the successfully initialized cache node to the caller.
- */
- return node;
-}
-
-/*.......................................................................
- * Return a directory reader object to the cache, after first closing
- * the directory that it was managing.
- *
- * Input:
- * ef ExpandFile * The pathname expansion resource object.
- * node DirNode * The cache entry of the directory reader, as returned
- * by ef_open_dir().
- * Output:
- * return DirNode * The deleted DirNode (ie. allways NULL).
- */
-static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node)
-{
-/*
- * Get the directory reader cache.
- */
- DirCache *cache = &ef->cache;
-/*
- * Close the directory.
- */
- _dr_close_dir(node->dr);
-/*
- * Return the node to the tail of the cache list.
- */
- node->next = NULL;
- node->prev = cache->tail;
- if(cache->tail)
- cache->tail->next = node;
- else
- cache->head = cache->tail = node;
- if(!cache->next)
- cache->next = node;
- return NULL;
-}
-
-/*.......................................................................
- * Return non-zero if the specified file name matches a given glob
- * pattern.
- *
- * Input:
- * file const char * The file-name component to be matched to the pattern.
- * pattern const char * The start of the pattern to match against file[].
- * xplicit int If non-zero, the first character must be matched
- * explicitly (ie. not with a wildcard).
- * nextp const char * The pointer to the the character following the
- * end of the pattern in pattern[].
- * Output:
- * return int 0 - Doesn't match.
- * 1 - The file-name string matches the pattern.
- */
-static int ef_string_matches_pattern(const char *file, const char *pattern,
- int xplicit, const char *nextp)
-{
- const char *pptr = pattern; /* The pointer used to scan the pattern */
- const char *fptr = file; /* The pointer used to scan the filename string */
-/*
- * Match each character of the pattern in turn.
- */
- while(pptr < nextp) {
-/*
- * Handle the next character of the pattern.
- */
- switch(*pptr) {
-/*
- * A match zero-or-more characters wildcard operator.
- */
- case '*':
-/*
- * Skip the '*' character in the pattern.
- */
- pptr++;
-/*
- * If wildcards aren't allowed, the pattern doesn't match.
- */
- if(xplicit)
- return 0;
-/*
- * If the pattern ends with a the '*' wildcard, then the
- * rest of the filename matches this.
- */
- if(pptr >= nextp)
- return 1;
-/*
- * Using the wildcard to match successively longer sections of
- * the remaining characters of the filename, attempt to match
- * the tail of the filename against the tail of the pattern.
- */
- for( ; *fptr; fptr++) {
- if(ef_string_matches_pattern(fptr, pptr, 0, nextp))
- return 1;
- };
- return 0; /* The pattern following the '*' didn't match */
- break;
-/*
- * A match-one-character wildcard operator.
- */
- case '?':
-/*
- * If there is a character to be matched, skip it and advance the
- * pattern pointer.
- */
- if(!xplicit && *fptr) {
- fptr++;
- pptr++;
-/*
- * If we hit the end of the filename string, there is no character
- * matching the operator, so the string doesn't match.
- */
- } else {
- return 0;
- };
- break;
-/*
- * A character range operator, with the character ranges enclosed
- * in matching square brackets.
- */
- case '[':
- if(xplicit || !ef_matches_range(*fptr++, ++pptr, &pptr))
- return 0;
- break;
-/*
- * A backslash in the pattern prevents the following character as
- * being seen as a special character.
- */
- case '\\':
- pptr++;
- /* Note fallthrough to default */
-/*
- * A normal character to be matched explicitly.
- */
- default:
- if(*fptr == *pptr) {
- fptr++;
- pptr++;
- } else {
- return 0;
- };
- break;
- };
-/*
- * After passing the first character, turn off the explicit match
- * requirement.
- */
- xplicit = 0;
- };
-/*
- * To get here the pattern must have been exhausted. If the filename
- * string matched, then the filename string must also have been
- * exhausted.
- */
- return *fptr == '\0';
-}
-
-/*.......................................................................
- * Match a character range expression terminated by an unescaped close
- * square bracket.
- *
- * Input:
- * c int The character to be matched with the range
- * pattern.
- * pattern const char * The range pattern to be matched (ie. after the
- * initiating '[' character).
- * endp const char ** On output a pointer to the character following the
- * range expression will be assigned to *endp.
- * Output:
- * return int 0 - Doesn't match.
- * 1 - The character matched.
- */
-static int ef_matches_range(int c, const char *pattern, const char **endp)
-{
- const char *pptr = pattern; /* The pointer used to scan the pattern */
- int invert = 0; /* True to invert the sense of the match */
- int matched = 0; /* True if the character matched the pattern */
-/*
- * If the first character is a caret, the sense of the match is
- * inverted and only if the character isn't one of those in the
- * range, do we say that it matches.
- */
- if(*pptr == '^') {
- pptr++;
- invert = 1;
- };
-/*
- * The hyphen is only a special character when it follows the first
- * character of the range (not including the caret).
- */
- if(*pptr == '-') {
- pptr++;
- if(c == '-') {
- *endp = pptr;
- matched = 1;
- };
-/*
- * Skip other leading '-' characters since they make no sense.
- */
- while(*pptr == '-')
- pptr++;
- };
-/*
- * The hyphen is only a special character when it follows the first
- * character of the range (not including the caret or a hyphen).
- */
- if(*pptr == ']') {
- pptr++;
- if(c == ']') {
- *endp = pptr;
- matched = 1;
- };
- };
-/*
- * Having dealt with the characters that have special meanings at
- * the beginning of a character range expression, see if the
- * character matches any of the remaining characters of the range,
- * up until a terminating ']' character is seen.
- */
- while(!matched && *pptr && *pptr != ']') {
-/*
- * Is this a range of characters signaled by the two end characters
- * separated by a hyphen?
- */
- if(*pptr == '-') {
- if(pptr[1] != ']') {
- if(c >= pptr[-1] && c <= pptr[1])
- matched = 1;
- pptr += 2;
- };
-/*
- * A normal character to be compared directly.
- */
- } else if(*pptr++ == c) {
- matched = 1;
- };
- };
-/*
- * Find the terminating ']'.
- */
- while(*pptr && *pptr != ']')
- pptr++;
-/*
- * Did we find a terminating ']'?
- */
- if(*pptr == ']') {
- *endp = pptr + 1;
- return matched ? !invert : invert;
- };
-/*
- * If the pattern didn't end with a ']' then it doesn't match, regardless
- * of the value of the required sense of the match.
- */
- *endp = pptr;
- return 0;
-}
-
-/*.......................................................................
- * This is a qsort() comparison function used to sort strings.
- *
- * Input:
- * v1, v2 void * Pointers to the two strings to be compared.
- * Output:
- * return int -1 -> v1 < v2.
- * 0 -> v1 == v2
- * 1 -> v1 > v2
- */
-static int ef_cmp_strings(const void *v1, const void *v2)
-{
- char * const *s1 = (char * const *) v1;
- char * const *s2 = (char * const *) v2;
- return strcmp(*s1, *s2);
-}
-
-/*.......................................................................
- * 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) {
- strcpy(ef->errmsg, "Insufficient memory to expand path");
- 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) {
- strcpy(ef->errmsg, "Environment variable name too long");
- 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";
- sprintf(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) {
- strcpy(ef->errmsg, "Insufficient memory to expand path");
- 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) {
- strcpy(ef->errmsg, "Insufficient memory to expand path");
- 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) {
- strcpy(ef->errmsg, "Username too long");
- 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) {
- strcpy(ef->errmsg, "Insufficient memory to expand filename");
- 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);
-}
-
-/*.......................................................................
- * Return a description of the last path-expansion error that occurred.
- *
- * Input:
- * ef ExpandFile * The path-expansion resource object.
- * Output:
- * return char * The description of the last error.
- */
-const char *ef_last_error(ExpandFile *ef)
-{
- return ef ? ef->errmsg : "NULL ExpandFile argument";
-}
-
-/*.......................................................................
- * Print out an array of matching files.
- *
- * Input:
- * result FileExpansion * The container of the sorted array of
- * expansions.
- * fp FILE * The output stream to write to.
- * term_width int The width of the terminal.
- * Output:
- * return int 0 - OK.
- * 1 - Error.
- */
-int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width)
-{
- int maxlen; /* The length of the longest matching string */
- int width; /* The width of a column */
- int ncol; /* The number of columns to list */
- int nrow; /* The number of rows needed to list all of the expansions */
- int row,col; /* The row and column being written to */
- int i;
-/*
- * Check the arguments.
- */
- if(!result || !fp) {
- fprintf(stderr, "ef_list_expansions: NULL argument(s).\n");
- return 1;
- };
-/*
- * Not enough space to list anything?
- */
- if(term_width < 1)
- return 0;
-/*
- * Work out the maximum length of the matching filenames.
- */
- maxlen = 0;
- for(i=0; i<result->nfile; i++) {
- int len = strlen(result->files[i]);
- if(len > maxlen)
- maxlen = len;
- };
-/*
- * Nothing to list?
- */
- if(maxlen == 0)
- return 0;
-/*
- * Split the available terminal width into columns of maxlen + 2 characters.
- */
- width = maxlen + 2;
- ncol = term_width / width;
-/*
- * If the column width is greater than the terminal width, the matches will
- * just have to overlap onto the next line.
- */
- if(ncol < 1)
- ncol = 1;
-/*
- * How many rows will be needed?
- */
- nrow = (result->nfile + ncol - 1) / ncol;
-/*
- * Print the expansions out in ncol columns, sorted in row order within each
- * column.
- */
- for(row=0; row < nrow; row++) {
- for(col=0; col < ncol; col++) {
- int m = col*nrow + row;
- if(m < result->nfile) {
- const char *filename = result->files[m];
- if(fprintf(fp, "%s%-*s%s", filename,
- (int) (ncol > 1 ? maxlen - strlen(filename):0), "",
- col<ncol-1 ? " " : "\r\n") < 0)
- return 1;
- } else {
- if(fprintf(fp, "\r\n") < 0)
- return 1;
- break;
- };
- };
- };
- return 0;
-}
-